Wrapping Rust's log crate
Rust’s log crate crate is a thin macro interface for logging that can
plug in to many different backends. Its functions can be called just like
println!
:
let you = "cow".to_string();
let name = "moo".to_string();
// There's also log::log, log::warn, and so on.
log::info!("Hello {animal}, {} to you", greeting);
I wanted to wrap it to add a prefix [farm]
so the output looks like:
[farm] Hello cow, moo to you
Easy, right? 😅
My first attempt was a macro that tried to concat!
the prefix:
macro_rules! warn {
($str:literal) => (::log::warn!(concat!("[farm] ", $str)));
($str:literal, $($args:tt)*) => (::log::warn!(concat!("[farm] ", $str), $($args)*))
}
But it doesn’t work:
error: there is no argument named `animal`
--> src/log.rs:3:51
|
3 | ($str:literal, $($args:tt)*) => (::log::warn!(concat!("[farm] ", $str), $(...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: src/main.rs:46:5
|
46 | warn!("Hello {animal}");
| -------------------- in this macro invocation
Keep in mind that animal
was previously defined with a let
-statement as in
the example code, so it should be in the calling context. Fortunately, there’s
a helpful note:
= note: did you intend to capture a variable `animal` from the surrounding scope?
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
The solution is straightforward. We just call format_args!
ourselves!
// It works!
macro_rules! warn {
($str:literal) => (::log::warn!("[farm] {}", format_args!($str)));
($str:literal, $($args:tt)*) => (::log::warn!("[farm] {}", format_args!($str, $($args)*)))
}