diff --git a/Cargo.toml b/Cargo.toml index e7e789293..cf07384f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,11 @@ features = ["std", "serde", "kv_unstable_sval"] name = "filters" harness = false +[[test]] +name = "kv_macro" +required-features = ["kv_unstable"] +harness = false + [features] max_level_off = [] max_level_error = [] diff --git a/src/kv/error.rs b/src/kv/error.rs index 0f5652f92..4b701f46b 100644 --- a/src/kv/error.rs +++ b/src/kv/error.rs @@ -54,7 +54,7 @@ mod std_support { use super::*; use std::{error, io}; - pub(super) type BoxedError = Box; + pub(super) type BoxedError = Box; impl Error { /// Create an error from a standard error type. diff --git a/src/kv/source.rs b/src/kv/source.rs index 0538c14f4..85249843d 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -19,7 +19,7 @@ pub trait Source { /// /// A source should yield the same key-value pairs to a subsequent visitor unless /// that visitor itself fails. - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error>; + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; /// Get the value for a given key. /// @@ -85,7 +85,7 @@ impl<'a, T> Source for &'a T where T: Source + ?Sized, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -103,7 +103,7 @@ where K: ToKey, V: ToValue, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { visitor.visit_pair(self.0.to_key(), self.1.to_value()) } @@ -124,7 +124,7 @@ impl Source for [S] where S: Source, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { for source in self { source.visit(visitor)?; } @@ -141,7 +141,7 @@ impl Source for Option where S: Source, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { if let Some(ref source) = *self { source.visit(visitor)?; } @@ -209,7 +209,7 @@ mod std_support { where S: Source + ?Sized, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -226,7 +226,7 @@ mod std_support { where S: Source, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -254,7 +254,7 @@ mod std_support { V: ToValue, S: BuildHasher, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { for (key, value) in self { visitor.visit_pair(key.to_key(), value.to_value())?; } @@ -275,7 +275,7 @@ mod std_support { K: ToKey + Borrow + Ord, V: ToValue, { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { for (key, value) in self { visitor.visit_pair(key.to_key(), value.to_value())?; } @@ -350,12 +350,12 @@ mod tests { #[test] fn source_is_object_safe() { - fn _check(_: &Source) {} + fn _check(_: &dyn Source) {} } #[test] fn visitor_is_object_safe() { - fn _check(_: &Visitor) {} + fn _check(_: &dyn Visitor) {} } #[test] @@ -366,7 +366,7 @@ mod tests { } impl Source for OnePair { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { visitor.visit_pair(self.key.to_key(), self.value.to_value()) } } diff --git a/src/kv/value/internal.rs b/src/kv/value/internal.rs index 5f01a317b..8e8d2ec73 100644 --- a/src/kv/value/internal.rs +++ b/src/kv/value/internal.rs @@ -12,19 +12,19 @@ pub(super) enum Inner<'v> { /// A simple primitive value that can be copied without allocating. Primitive(Primitive<'v>), /// A value that can be filled. - Fill(&'v Fill), + Fill(&'v dyn Fill), /// A debuggable value. - Debug(&'v fmt::Debug), + Debug(&'v dyn fmt::Debug), /// A displayable value. - Display(&'v fmt::Display), + Display(&'v dyn fmt::Display), #[cfg(feature = "kv_unstable_sval")] /// A structured value from `sval`. - Sval(&'v sval_support::Value), + Sval(&'v dyn sval_support::Value), } impl<'v> Inner<'v> { - pub(super) fn visit(&self, visitor: &mut Visitor) -> Result<(), Error> { + pub(super) fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> { match *self { Inner::Primitive(value) => match value { Primitive::Signed(value) => visitor.i64(value), @@ -47,8 +47,8 @@ impl<'v> Inner<'v> { /// The internal serialization contract. pub(super) trait Visitor { - fn debug(&mut self, v: &fmt::Debug) -> Result<(), Error>; - fn display(&mut self, v: &fmt::Display) -> Result<(), Error> { + fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error>; + fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error> { self.debug(&format_args!("{}", v)) } @@ -119,7 +119,7 @@ mod fmt_support { struct FmtVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>); impl<'a, 'b: 'a> Visitor for FmtVisitor<'a, 'b> { - fn debug(&mut self, v: &fmt::Debug) -> Result<(), Error> { + fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> { v.fmt(self.0)?; Ok(()) diff --git a/src/kv/value/mod.rs b/src/kv/value/mod.rs index 1695afbd1..95e4bb496 100644 --- a/src/kv/value/mod.rs +++ b/src/kv/value/mod.rs @@ -57,7 +57,7 @@ where /// A value slot to fill using the [`Fill`](trait.Fill.html) trait. pub struct Slot<'a> { filled: bool, - visitor: &'a mut Visitor, + visitor: &'a mut dyn Visitor, } impl<'a> fmt::Debug for Slot<'a> { @@ -67,7 +67,7 @@ impl<'a> fmt::Debug for Slot<'a> { } impl<'a> Slot<'a> { - fn new(visitor: &'a mut Visitor) -> Self { + fn new(visitor: &'a mut dyn Visitor) -> Self { Slot { visitor, filled: false, @@ -112,7 +112,7 @@ impl<'v> Value<'v> { } } - fn visit(&self, visitor: &mut Visitor) -> Result<(), Error> { + fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> { self.inner.visit(visitor) } } @@ -127,7 +127,7 @@ mod tests { impl Fill for TestFill { fn fill(&self, slot: &mut Slot) -> Result<(), Error> { - let dbg: &fmt::Debug = &1; + let dbg: &dyn fmt::Debug = &1; slot.fill(Value::from_debug(&dbg)) } diff --git a/src/kv/value/test.rs b/src/kv/value/test.rs index c9c03dc42..4db46d29a 100644 --- a/src/kv/value/test.rs +++ b/src/kv/value/test.rs @@ -26,7 +26,7 @@ impl<'v> Value<'v> { struct TestVisitor(Option); impl internal::Visitor for TestVisitor { - fn debug(&mut self, v: &fmt::Debug) -> Result<(), Error> { + fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> { self.0 = Some(Token::Str(format!("{:?}", v))); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index c79219dc6..2fa43e9c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -306,7 +306,7 @@ pub mod kv; // The LOGGER static holds a pointer to the global logger. It is protected by // the STATE static which determines whether LOGGER has been initialized yet. -static mut LOGGER: &'static Log = &NopLogger; +static mut LOGGER: &'static dyn Log = &NopLogger; #[allow(deprecated)] static STATE: AtomicUsize = ATOMIC_USIZE_INIT; @@ -747,7 +747,7 @@ pub struct Record<'a> { // the underlying `Source`. #[cfg(feature = "kv_unstable")] #[derive(Clone)] -struct KeyValues<'a>(&'a kv::Source); +struct KeyValues<'a>(&'a dyn kv::Source); #[cfg(feature = "kv_unstable")] impl<'a> fmt::Debug for KeyValues<'a> { @@ -828,7 +828,7 @@ impl<'a> Record<'a> { /// The structued key-value pairs associated with the message. #[cfg(feature = "kv_unstable")] #[inline] - pub fn key_values(&self) -> &kv::Source { + pub fn key_values(&self) -> &dyn kv::Source { self.key_values.0 } @@ -1002,7 +1002,7 @@ impl<'a> RecordBuilder<'a> { /// Set [`key_values`](struct.Record.html#method.key_values) #[cfg(feature = "kv_unstable")] #[inline] - pub fn key_values(&mut self, kvs: &'a kv::Source) -> &mut RecordBuilder<'a> { + pub fn key_values(&mut self, kvs: &'a dyn kv::Source) -> &mut RecordBuilder<'a> { self.record.key_values = KeyValues(kvs); self } @@ -1210,7 +1210,7 @@ pub fn max_level() -> LevelFilter { /// /// [`set_logger`]: fn.set_logger.html #[cfg(all(feature = "std", atomic_cas))] -pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { +pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { set_logger_inner(|| unsafe { &*Box::into_raw(logger) }) } @@ -1268,14 +1268,14 @@ pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { /// /// [`set_logger_racy`]: fn.set_logger_racy.html #[cfg(atomic_cas)] -pub fn set_logger(logger: &'static Log) -> Result<(), SetLoggerError> { +pub fn set_logger(logger: &'static dyn Log) -> Result<(), SetLoggerError> { set_logger_inner(|| logger) } #[cfg(atomic_cas)] fn set_logger_inner(make_logger: F) -> Result<(), SetLoggerError> where - F: FnOnce() -> &'static Log, + F: FnOnce() -> &'static dyn Log, { unsafe { match STATE.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) { @@ -1312,7 +1312,7 @@ where /// (including all logging macros). /// /// [`set_logger`]: fn.set_logger.html -pub unsafe fn set_logger_racy(logger: &'static Log) -> Result<(), SetLoggerError> { +pub unsafe fn set_logger_racy(logger: &'static dyn Log) -> Result<(), SetLoggerError> { match STATE.load(Ordering::SeqCst) { UNINITIALIZED => { LOGGER = logger; @@ -1372,7 +1372,7 @@ impl error::Error for ParseLevelError { /// Returns a reference to the logger. /// /// If a logger has not been set, a no-op implementation is returned. -pub fn logger() -> &'static Log { +pub fn logger() -> &'static dyn Log { unsafe { if STATE.load(Ordering::SeqCst) != INITIALIZED { static NOP: NopLogger = NopLogger; @@ -1384,6 +1384,7 @@ pub fn logger() -> &'static Log { } // WARNING: this is not part of the crate's public API and is subject to change at any time +#[cfg(not(feature = "kv_unstable"))] #[doc(hidden)] pub fn __private_api_log( args: fmt::Arguments, @@ -1402,6 +1403,30 @@ pub fn __private_api_log( ); } +#[cfg(feature = "kv_unstable")] +#[doc(hidden)] +pub fn __private_api_log( + args: fmt::Arguments, + level: Level, + &(target, module_path, file, line): &(&str, &'static str, &'static str, u32), + kvs: Option<&dyn kv::Source>, +) { + let mut record = Record::builder(); + record + .args(args) + .level(level) + .target(target) + .module_path_static(Some(module_path)) + .file_static(Some(file)) + .line(Some(line)); + + if let Some(kvs) = kvs { + record.key_values(kvs); + } + + logger().log(&record.build()); +} + // WARNING: this is not part of the crate's public API and is subject to change at any time #[doc(hidden)] pub fn __private_api_enabled(level: Level, target: &str) -> bool { diff --git a/src/macros.rs b/src/macros.rs index bb40b7107..9a1709f58 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,6 +27,7 @@ /// data.0, data.1, private_data); /// # } /// ``` +#[cfg(not(feature = "kv_unstable"))] #[macro_export(local_inner_macros)] macro_rules! log { (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ @@ -42,6 +43,41 @@ macro_rules! log { ($lvl:expr, $($arg:tt)+) => (log!(target: __log_module_path!(), $lvl, $($arg)+)) } +/// TODO: merge docs. +#[cfg(feature = "kv_unstable")] +#[macro_export(local_inner_macros)] +macro_rules! log { + (target: $target:expr, $lvl:expr, $($arg:tt, )+ { + $($key:ident: $value:tt),+ $(,)* + }) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + let kvs = [ + $((&$crate::kv::Key::from_str($crate::std::stringify!($key)), + &$crate::kv::Value::from($value)),)+ + ]; + $crate::__private_api_log( + __log_format_args!($($arg)+), + lvl, + &($target, __log_module_path!(), __log_file!(), __log_line!()), + Some(&kvs.as_ref()), + ); + } + }); + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api_log( + __log_format_args!($($arg)+), + lvl, + &($target, __log_module_path!(), __log_file!(), __log_line!()), + None, + ); + } + }); + ($lvl:expr, $($arg:tt)+) => (log!(target: __log_module_path!(), $lvl, $($arg)+)) +} + /// Logs a message at the error level. /// /// # Examples diff --git a/tests/filters.rs b/tests/filters.rs index e4d21a87f..247c30a5e 100644 --- a/tests/filters.rs +++ b/tests/filters.rs @@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex}; use log::set_boxed_logger; #[cfg(not(feature = "std"))] -fn set_boxed_logger(logger: Box) -> Result<(), log::SetLoggerError> { +fn set_boxed_logger(logger: Box) -> Result<(), log::SetLoggerError> { log::set_logger(unsafe { &*Box::into_raw(logger) }) } diff --git a/tests/kv_macro.rs b/tests/kv_macro.rs new file mode 100644 index 000000000..8f701e5ec --- /dev/null +++ b/tests/kv_macro.rs @@ -0,0 +1,53 @@ +#[macro_use] +extern crate log; + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; + +use log::{Level, Log, LevelFilter, Metadata, Record}; +use log::kv::Key; + +#[cfg(feature = "std")] +use log::set_boxed_logger; + +#[cfg(not(feature = "std"))] +fn set_boxed_logger(logger: Box) -> Result<(), log::SetLoggerError> { + log::set_logger(unsafe { &*Box::into_raw(logger) }) +} + +fn main() { + let ran = Arc::new(AtomicBool::new(false)); + set_boxed_logger(Box::new(Logger(Arc::clone(&ran)))).unwrap(); + log::set_max_level(LevelFilter::Info); + + log!(target: __log_module_path!(), Level::Info, "some {} message {}", "great", 1, { + key1: "Some value", + key2: 2, + }); + + assert!(ran.load(Ordering::Acquire), "logger didn't run"); +} + +struct Logger(Arc); + +impl Log for Logger { + fn enabled(&self, _: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + self.0.store(true, Ordering::Release); + + assert_eq!(record.level(), Level::Info); + assert_eq!(record.target(), "kv_macro"); + assert_eq!(record.module_path(), Some("kv_macro")); + assert_eq!(record.file(), Some("tests/kv_macro.rs")); + assert_eq!(record.line(), Some(23)); + let kvs = record.key_values(); + assert_eq!(kvs.count(), 2); + assert_eq!(kvs.get(Key::from_str("key1")).unwrap().to_string(), "Some value"); + assert_eq!(kvs.get(Key::from_str("key2")).unwrap().to_string(), "2"); + } + + fn flush(&self) {} +}