[Rust API] Improve logger documentation
Version and Platform (required):
- Binary Ninja Version: 5.0.7290-stable
- Edition: Commercial
- OS: macOS
- OS Version: 14.5
- CPU Architecture: Apple M-series (ARM64)
Bug Description: When using the Rust API to develop a plugin, I attempted to enable logging in headless mode using:
binaryninja::logger::Logger::new("Test").init();
However, no log output is produced, even when explicitly setting the log level. This makes it difficult to debug plugins in headless mode.
Most existing Rust plugin examples use print* macros instead of the log crate, which suggests that logger.init() may not be supported in this context — or perhaps I’m missing a required step.
Steps To Reproduce:
- Create a headless plugin using the Binary Ninja Rust API.
- Call
binaryninja::logger::Logger::new("Test").init()and attempt to log a message usinglog::info!,warn!, etc. - Run the plugin in headless mode using
cargo run. - Observe that no output is produced in the console.
Expected Behavior:
I expected that logger.init() would configure logging correctly and output log messages to the console when running in headless mode.
Screenshots/Video Recording: N/A
Binary: Not applicable — this issue concerns plugin development using the Rust API.
Additional Information: Possibly related to issue #5101 and issue #1990.
Is this the intended behavior? Should I be using a third-party logging solution instead of the built-in Binary Ninja logger when working in headless mode?
The logger constructs a log crate compatible sink so that when you have log::info!("test") the logs are output to the binary ninja logger. What you want is to register a LogListener, using register_listener.
So to answer
Should I be using a third-party logging solution instead of the built-in Binary Ninja logger when working in headless mode?
Yea, you will want to probably do something like:
use binaryninja::headless::Session;
use binaryninja::logger::{register_listener, Level, LogListener, Logger};
struct MyLogListener;
impl LogListener for MyLogListener {
fn log(&self, session: usize, level: Level, msg: &str, logger_name: &str, tid: usize) {
println!("[{:?}] {}: {}", level, logger_name, msg);
}
fn level(&self) -> Level {
Level::DebugLog
}
}
#[test]
fn test_log_listener() {
// Registering the listener before i initialize the session to get the initialization logs as well.
let guard = register_listener(MyLogListener);
let _session = Session::new().expect("Failed to initialize session");
// Also show that our logs through `log` crate are now being printed.
Logger::new("Test").init();
log::info!("test");
// Remember the guard will close the listener once it goes out of scope! (or we manually drop it like here)
drop(guard);
}
I am going to add this as an example, clearly the documentation (or lack thereof) is lacking!
Thanks for the quick reply and the solution — I tried it out, and everything works as expected now.
BTW, I ended up using tracing with a custom implementation of the LogListener trait.