diff --git a/src/__private_api.rs b/src/__private_api.rs index 11bc2fc1c..57f038120 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -1,7 +1,7 @@ //! WARNING: this is not part of the crate's public API and is subject to change at any time use self::sealed::KVs; -use crate::{Level, Metadata, Record}; +use crate::{Level, Log, Metadata, Record}; use std::fmt::Arguments; use std::panic::Location; pub use std::{format_args, module_path, stringify}; @@ -34,7 +34,8 @@ impl<'a> KVs<'a> for () { // Log implementation. -fn log_impl( +fn log_impl( + logger: L, args: Arguments, level: Level, &(target, module_path, loc): &(&str, &'static str, &'static Location), @@ -58,22 +59,30 @@ fn log_impl( #[cfg(feature = "kv")] builder.key_values(&kvs); - crate::logger().log(&builder.build()); + logger.log(&builder.build()); } -pub fn log<'a, K>( +pub fn log<'a, K, L>( + logger: L, args: Arguments, level: Level, target_module_path_and_loc: &(&str, &'static str, &'static Location), kvs: K, ) where K: KVs<'a>, + L: Log, { - log_impl(args, level, target_module_path_and_loc, kvs.into_kvs()) + log_impl( + logger, + args, + level, + target_module_path_and_loc, + kvs.into_kvs(), + ) } -pub fn enabled(level: Level, target: &str) -> bool { - crate::logger().enabled(&Metadata::builder().level(level).target(target).build()) +pub fn enabled(logger: L, level: Level, target: &str) -> bool { + logger.enabled(&Metadata::builder().level(level).target(target).build()) } #[track_caller] diff --git a/src/lib.rs b/src/lib.rs index a862b5fa9..2dd300874 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1204,7 +1204,7 @@ pub trait Log: Sync + Send { fn flush(&self); } -// Just used as a dummy initial value for LOGGER +/// A dummy initial value for LOGGER. struct NopLogger; impl Log for NopLogger { @@ -1216,6 +1216,28 @@ impl Log for NopLogger { fn flush(&self) {} } +/// The global logger proxy. +/// +/// This zero-sized type implements the [`Log`] trait by forwarding calls +/// to the logger registered with the `set_boxed_logger` or `set_logger` +/// methods if there is one, or a nop logger as default. +#[derive(Copy, Clone, Default, Debug)] +pub struct GlobalLogger; + +impl Log for GlobalLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + logger().enabled(metadata) + } + + fn log(&self, record: &Record) { + logger().log(record) + } + + fn flush(&self) { + logger().flush() + } +} + impl Log for &'_ T where T: ?Sized + Log, diff --git a/src/macros.rs b/src/macros.rs index 0f78f8525..e962b1b62 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,11 +30,13 @@ #[macro_export] #[clippy::format_args] macro_rules! log { - // log!(target: "my_target", Level::Info, key1:? = 42, key2 = true; "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({ + // log!(logger: my_logger, target: "my_target", Level::Info, key1:? = 42, key2 = true; "a {} event", "log"); + (logger: $logger:expr, target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({ + let logger = $logger; let lvl = $lvl; if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { - $crate::__private_api::log::<&_>( + $crate::__private_api::log::<&_, _>( + logger, $crate::__private_api::format_args!($($arg)+), lvl, &($target, $crate::__private_api::module_path!(), $crate::__private_api::loc()), @@ -43,11 +45,13 @@ macro_rules! log { } }); - // log!(target: "my_target", Level::Info, "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + // log!(logger: my_logger, target: "my_target", Level::Info, "a {} event", "log"); + (logger: $logger:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + let logger = $logger; let lvl = $lvl; if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { $crate::__private_api::log( + logger, $crate::__private_api::format_args!($($arg)+), lvl, &($target, $crate::__private_api::module_path!(), $crate::__private_api::loc()), @@ -56,8 +60,20 @@ macro_rules! log { } }); + // log!(logger: my_logger, Level::Info, "a log event") + (logger: $logger:expr, $lvl:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, target: $crate::__private_api::module_path!(), $lvl, $($arg)+) + }); + + // log!(target: "my_target", Level::Info, "a log event") + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::GlobalLogger, target: $target, $lvl, $($arg)+) + }); + // log!(Level::Info, "a log event") - ($lvl:expr, $($arg:tt)+) => ($crate::log!(target: $crate::__private_api::module_path!(), $lvl, $($arg)+)); + ($lvl:expr, $($arg:tt)+) => ({ + $crate::log!(target: $crate::__private_api::module_path!(), $lvl, $($arg)+) + }); } /// Logs a message at the error level. @@ -77,9 +93,23 @@ macro_rules! log { #[macro_export] #[clippy::format_args] macro_rules! error { + // error!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // error!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, target: $target, $crate::Level::Error, $($arg)+) + }); + + // error!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // error!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, $crate::Level::Error, $($arg)+) + }); + // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // error!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Error, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Error, $($arg)+) + }); // error!("a {} event", "log") ($($arg:tt)+) => ($crate::log!($crate::Level::Error, $($arg)+)) @@ -102,9 +132,23 @@ macro_rules! error { #[macro_export] #[clippy::format_args] macro_rules! warn { + // warn!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // warn!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, target: $target, $crate::Level::Warn, $($arg)+) + }); + + // warn!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // warn!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, $crate::Level::Warn, $($arg)+) + }); + // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // warn!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Warn, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Warn, $($arg)+) + }); // warn!("a {} event", "log") ($($arg:tt)+) => ($crate::log!($crate::Level::Warn, $($arg)+)) @@ -129,9 +173,23 @@ macro_rules! warn { #[macro_export] #[clippy::format_args] macro_rules! info { + // info!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // info!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, target: $target, $crate::Level::Info, $($arg)+) + }); + + // info!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // info!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, $crate::Level::Info, $($arg)+) + }); + // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // info!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Info, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Info, $($arg)+) + }); // info!("a {} event", "log") ($($arg:tt)+) => ($crate::log!($crate::Level::Info, $($arg)+)) @@ -155,9 +213,23 @@ macro_rules! info { #[macro_export] #[clippy::format_args] macro_rules! debug { + // debug!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // debug!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, target: $target, $crate::Level::Debug, $($arg)+) + }); + + // debug!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // debug!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, $crate::Level::Debug, $($arg)+) + }); + // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // debug!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Debug, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Debug, $($arg)+) + }); // debug!("a {} event", "log") ($($arg:tt)+) => ($crate::log!($crate::Level::Debug, $($arg)+)) @@ -183,9 +255,23 @@ macro_rules! debug { #[macro_export] #[clippy::format_args] macro_rules! trace { + // trace!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // trace!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, target: $target, $crate::Level::Trace, $($arg)+) + }); + + // trace!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // trace!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $logger, $crate::Level::Trace, $($arg)+) + }); + // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // trace!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Trace, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Trace, $($arg)+) + }); // trace!("a {} event", "log") ($($arg:tt)+) => ($crate::log!($crate::Level::Trace, $($arg)+)) @@ -219,15 +305,25 @@ macro_rules! trace { /// ``` #[macro_export] macro_rules! log_enabled { - (target: $target:expr, $lvl:expr) => {{ + (logger: $logger:expr, target: $target:expr, $lvl:expr) => ({ + let logger = $logger; let lvl = $lvl; lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() - && $crate::__private_api::enabled(lvl, $target) - }}; - ($lvl:expr) => { + && $crate::__private_api::enabled(logger, lvl, $target) + }); + + (logger: $logger:expr, $lvl:expr) => ({ + $crate::log_enabled!(logger: $logger, target: $crate::__private_api::module_path!(), $lvl) + }); + + (target: $target:expr, $lvl:expr) => ({ + $crate::log_enabled!(logger: $crate::GlobalLogger, target: $target, $lvl) + }); + + ($lvl:expr) => ({ $crate::log_enabled!(target: $crate::__private_api::module_path!(), $lvl) - }; + }); } // These macros use a pattern of #[cfg]s to produce nicer error diff --git a/tests/integration.rs b/tests/integration.rs index 651fe6cd9..14f7a427d 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -53,31 +53,31 @@ fn main() { last_log_location: Mutex::new(None), }); let a = me.clone(); - set_boxed_logger(Box::new(Logger(me))).unwrap(); + let logger = Logger(me); - test_filter(&a, LevelFilter::Off); - test_filter(&a, LevelFilter::Error); - test_filter(&a, LevelFilter::Warn); - test_filter(&a, LevelFilter::Info); - test_filter(&a, LevelFilter::Debug); - test_filter(&a, LevelFilter::Trace); + test_filter(&logger, &a, LevelFilter::Off); + test_filter(&logger, &a, LevelFilter::Error); + test_filter(&logger, &a, LevelFilter::Warn); + test_filter(&logger, &a, LevelFilter::Info); + test_filter(&logger, &a, LevelFilter::Debug); + test_filter(&logger, &a, LevelFilter::Trace); - test_line_numbers(&a); + test_line_numbers(&logger, &a); } } -fn test_filter(a: &State, filter: LevelFilter) { +fn test_filter(logger: &dyn Log, a: &State, filter: LevelFilter) { // tests to ensure logs with a level beneath 'max_level' are filtered out log::set_max_level(filter); - error!(""); + error!(logger: logger, ""); last(a, t(Level::Error, filter)); - warn!(""); + warn!(logger: logger, ""); last(a, t(Level::Warn, filter)); - info!(""); + info!(logger: logger, ""); last(a, t(Level::Info, filter)); - debug!(""); + debug!(logger: logger, ""); last(a, t(Level::Debug, filter)); - trace!(""); + trace!(logger: logger, ""); last(a, t(Level::Trace, filter)); fn t(lvl: Level, filter: LevelFilter) -> Option { @@ -93,10 +93,10 @@ fn test_filter(a: &State, filter: LevelFilter) { } } -fn test_line_numbers(state: &State) { +fn test_line_numbers(logger: &dyn Log, state: &State) { log::set_max_level(LevelFilter::Trace); - info!(""); // ensure check_line function follows log macro + info!(logger: logger, ""); // ensure check_line function follows log macro check_log_location(state); #[track_caller] diff --git a/tests/macros.rs b/tests/macros.rs index 20da6ac44..e4023ea22 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -1,4 +1,4 @@ -use log::{log, log_enabled}; +use log::{log, log_enabled, Log, Metadata, Record}; macro_rules! all_log_macros { ($($arg:tt)*) => ({ @@ -10,6 +10,16 @@ macro_rules! all_log_macros { }); } +struct NopLogger; + +impl Log for NopLogger { + fn enabled(&self, _: &Metadata) -> bool { + false + } + fn log(&self, _: &Record) {} + fn flush(&self) {} +} + #[test] fn no_args() { for lvl in log::Level::iter() { @@ -28,6 +38,11 @@ fn no_args() { all_log_macros!(target: "my_target", "hello"); all_log_macros!(target: "my_target", "hello",); + + all_log_macros!(logger: NopLogger, "hello"); + all_log_macros!(logger: NopLogger, "hello",); + all_log_macros!(logger: NopLogger, target: "my_target", "hello"); + all_log_macros!(logger: NopLogger, target: "my_target", "hello",); } #[test] @@ -115,6 +130,8 @@ fn kv_no_args() { log!(lvl, cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello"); } + all_log_macros!(logger: NopLogger, cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello"); + all_log_macros!(logger: NopLogger, target: "my_target", cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello"); all_log_macros!(target: "my_target", cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello"); all_log_macros!(target = "my_target", cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello"); all_log_macros!(cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello");