Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Callbacks and logging

raylib exposes hooks for several behaviours: trace-log output, custom file I/O loaders/savers, and audio stream processing. raylib-rs wraps the trace-log hook as a plain Rust function pointer so you can redirect raylib’s diagnostic output to your own logger.

The logging free functions (trace_log, set_trace_log) live in raylib::core::logging and are re-exported through raylib::prelude.

API surface

Example

Install a trace-log callback and emit one message. No window needed to install the callback, but trace_log calls into ffi::TraceLog which does require an active raylib context, so this example is no_run.

extern crate raylib;
use raylib::core::callbacks::set_trace_log_callback;
use raylib::core::logging::trace_log;
use raylib::consts::TraceLogLevel;

fn my_logger(level: TraceLogLevel, msg: &str) {
    eprintln!("[{level:?}] {msg}");
}

fn main() {
    // Install the callback before init so we capture startup messages too.
    set_trace_log_callback(my_logger).expect("callback already set");

    let (_rl, _thread) = raylib::init()
        .size(1, 1)
        .title("callback demo")
        .build();

    // Emit a custom message through the same callback.
    trace_log(TraceLogLevel::LOG_INFO, "Hello from the custom logger");
}

Gotchas

  • Callbacks are global state. Installing a new trace-log callback replaces the previous one. set_trace_log_callback always returns Ok(()). The old methods on RaylibHandle (e.g., rl.set_trace_log_callback(...)) are #[deprecated] — use the free function form instead.
  • The audio callback was removed in 6.0. The old per-sample audio callback API is gone entirely. Use AudioStream with is_processed / update_audio_stream for custom PCM generation, or attach a processor via attach_audio_stream_processor_to_music.
  • File I/O callbacks replace raylib’s I/O for the lifetime of the process. There is no “unset” for set_load_file_data_callback etc. — they can only be set once per callback slot.

Routing raylib logs through the log crate

With the opt-in log feature, the window builder can forward raylib’s TraceLog output into the log facade, so your application’s logger (env_logger, tracing-log, …) receives raylib’s logs alongside your own:

[dependencies]
raylib = { version = "6", features = ["log"] }
log = "0.4"
env_logger = "0.11"
env_logger::init(); // any `log`-compatible logger

let (mut rl, thread) = raylib::init()
    .size(800, 450)
    .title("game")
    .log_to_rust()
    .build();

log::info!("app and raylib logs share one pipeline now");

Messages arrive under the target "raylib", so RUST_LOG=raylib=debug scopes raylib’s output independently of your app’s.

The bridge makes the log facade the single level filter: it sets raylib’s own threshold to LOG_ALL (overriding .log_level()), so what you see is controlled entirely by your logger’s configuration. Raylib levels map TRACE→trace, DEBUG→debug, INFO→info, WARNING→warn, and both ERROR and FATAL to error (the facade has no fatal level; raylib still aborts after a fatal log, unchanged).

Two caveats:

  • The bridge claims the same single callback slot as set_trace_log_callback — they are mutually exclusive, last writer wins.
  • The bridge only emits into the facade. Without a logger installed, the messages are silently dropped, like any library using log. (It also requires the SUPPORT_TRACELOG feature, which is in the default set.)

See also

Showcase examples

Showcase examples that exercise this module: