Logging¶
MuJoCo uses its own logging system, more in detail described in their documentation (C code guide).
Pure wrappers¶
We provide safe wrappers around their logging API, available in the mujoco_rs::wrappers::mj_logging module.
The module provides abstractions over the raw logging functions, such as
log_info, log_message
and others. The default MuJoCo handler is configured via
set_log_config, which is an unsafe function:
MuJoCo keeps the configuration in one non-atomic global that every logging call reads, so the
caller must hold every other thread that uses MuJoCo.
log_error exits the program to adhere to the MuJoCo’s internal error assumptions.
Rust ecosystem integration¶
Rust has a de facto standard of doing logging, which is done via the log crate.
We provide a hook for integrating the MuJoCo logging with the log crate.
The hook can be installed by calling install_logging_hook once
at the start of the program.
Note
Installing the hook, through either function, replaces MuJoCo’s own handler, so the
logto_console, logto_file and logfile fields of
MjLogConfig no longer reach the output.
The install also writes every topic into the global topic bitmask once, so a bitmask that the
program set before the install is lost. The producer-side topic gate still reads that bitmask,
so a set_log_config call after the install
still suppresses the topics it clears.
Attention
install_logging_hook and
set_log_handler are unsafe. MuJoCo keeps the log
handler and the handler configuration in two globals that it writes and reads without
synchronization, so a thread that emits a message during the installation races with it.
Call either function at the start of the program, before any thread steps a simulation,
loads a model, or emits a message.
A message with level MjtLogLevel::mjLOG_ERROR still ends the process to adhere to MuJoCo’s error assumptions, thus avoiding possible undefined behaviors.
An example (using the env_logger backend):
use mujoco_rs::prelude::*;
fn main() {
env_logger::init(); // Any `log` backend works.
// SAFETY: no other thread uses MuJoCo yet.
unsafe { install_logging_hook() };
// log's logging function
log::warn!("this is an example warning!");
// MuJoCo's logging function
log_warning("the log backend receives this, with the target 'mujoco'");
}
Run the example with RUST_LOG=warn, because env_logger displays only errors by default.
In the above example, the log’s target is set to the Rust-module path when called from the user’s code.
The message is displayed in a format similar to the following:
[WARN <module path>] this is an example warning!
When calling log_warning from the user’s program, or when MuJoCo tries to log internally, the message
is displayed in a format similar to the following:
[WARN mujoco<:: optional sub-target>] the log backend receives this, with the target ‘mujoco’
Most MuJoCo logging messages will have no specific sub-topic, thus the format will be something like:
[WARN mujoco] …
The translation from a MuJoCo logging topic (MjtLogTopic)
to the log target is as follows:
Topic |
|
|---|---|
|
|
|
|
|
|
|
|
The MuJoCo levels mjLOG_DEBUG, mjLOG_INFO, mjLOG_WARNING and mjLOG_ERROR map onto
log’s Debug, Info, Warn and Error.
See Logging example.
Custom logging callback¶
In addition to the default log integration, the hook also accepts a single safe callback function
that takes a regular Rust reference to the
MjLogMessage, containing the requested message to
log.
The user callback function can be registered via set_log_handler.
When setting a custom logging callback inside MuJoCo-rs, there is no need to call install_logging_hook separately, because the set_log_handler does it automatically.
All log_ function calls (and internal MuJoCo calls) will now be redirected to the handler:
use mujoco_rs::prelude::*;
fn handler(message: &MjLogMessage) {
println!("{:?}: {}", message.level(), message.subject());
}
fn main() {
// SAFETY: no other thread uses MuJoCo yet.
unsafe { set_log_handler(handler) };
log_warning("the handler receives this, and the `log` backend receives nothing");
}
Attention
Setting a custom handler using only set_log_handler only redirects MuJoCo messages
to the user handler.
Any message emitted using log:: invocations bypass the handler. This also applies
to the internal invocations of MuJoCo-rs as well. For a custom callback of that, a custom
logger can be defined. Such logger
can also process the messages from MuJoCo itself, provided set_log_handler is not used.