In Rust I have the following syslog code:
use syslog::{Facility, Formatter3164};
fn main() {
let formatter = Formatter3164 {
facility: Facility::LOG_USER,
hostname: None,
process: "syslog_test".into(),
pid: 0,
};
let mut logger = syslog::unix(formatter).expect("Could not connect to syslog");
let sum1 = 2 + 3;
logger.info(&format!("Sum 1: 2 + 3 = {}", sum1)).expect("Failed to log");
let sum2 = 10 - 4;
logger.warning(&format!("Sum 2: 10 - 4 = {}", sum2)).expect("Failed to log");
let sum3 = 6 * 7;
logger.err(&format!("Sum 3: 6 * 7 = {}", sum3)).expect("Failed to log");
}
In C++ I would usually base class or wrap this up in a function so I don't need to keep repeating the .expect("failed to log") and use macros to provide filename and line number etc.
How would I go about this in Rust? I attempted a few things, like functionalising it, but passing the logger around as a parameter proved tricky (for me).
I wanted to have a function that takes: logger, log level, message text, or such.
To access the metadata of file!, line! and/or column! with the correct source location you'll have to use a macro:
macro_rules! log {
($logger: expr, $level: ident, $message: literal $(, $args: tt)* $(,)?) => {
$logger.$level(format!($message $(, $args)*)).unwrap_or_else(|e| {
panic!(
"{file}:{line}:{column}: Failed to log: {e}",
file = file!(),
line = line!(),
column = column!(),
)
})
};
}
Note: I use unwrap_or_else(|e| panic!(…)) instead of expect to avoid an allocation in the happy path see clippy::expect_fun_call.
You can add convenience macros to directly invoke a method for example an info!:
macro_rules! info {
($logger: expr, $message: literal $(, $args: tt)* $(,)?) => {
log!($logger, info, $message $(, $args)*)
};
}
Usage:
let sum1 = 2 + 3;
log!(logger, info, "Sum 1: 2 + 3 = {sum1}");
info!(logger, "Foobar");