diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index c868d69d..5420a36b 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -42,6 +42,7 @@ use crate::stdlib::{ borrow::Borrow, fmt, hash::{Hash, Hasher}, + num, ops::Range, }; @@ -276,33 +277,88 @@ macro_rules! impl_values { )+ } } -macro_rules! impl_value { - ( $record:ident( $( $value_ty:ty ),+ ) ) => { - $( - impl $crate::sealed::Sealed for $value_ty {} - impl $crate::field::Value for $value_ty { - fn record( - &self, - key: &$crate::field::Field, - visitor: &mut dyn $crate::field::Visit, - ) { - visitor.$record(key, *self) - } + +macro_rules! ty_to_nonzero { + (u8) => { + NonZeroU8 + }; + (u16) => { + NonZeroU16 + }; + (u32) => { + NonZeroU32 + }; + (u64) => { + NonZeroU64 + }; + (u128) => { + NonZeroU128 + }; + (usize) => { + NonZeroUsize + }; + (i8) => { + NonZeroI8 + }; + (i16) => { + NonZeroI16 + }; + (i32) => { + NonZeroI32 + }; + (i64) => { + NonZeroI64 + }; + (i128) => { + NonZeroI128 + }; + (isize) => { + NonZeroIsize + }; +} + +macro_rules! impl_one_value { + (bool, $op:expr, $record:ident) => { + impl_one_value!(normal, bool, $op, $record); + }; + ($value_ty:tt, $op:expr, $record:ident) => { + impl_one_value!(normal, $value_ty, $op, $record); + impl_one_value!(nonzero, $value_ty, $op, $record); + }; + (normal, $value_ty:tt, $op:expr, $record:ident) => { + impl $crate::sealed::Sealed for $value_ty {} + impl $crate::field::Value for $value_ty { + fn record(&self, key: &$crate::field::Field, visitor: &mut dyn $crate::field::Visit) { + visitor.$record(key, $op(*self)) } + } + }; + (nonzero, $value_ty:tt, $op:expr, $record:ident) => { + // This `use num::*;` is reported as unused because it gets emitted + // for every single invocation of this macro, so there are multiple `use`s. + // All but the first are useless indeed. + // We need this import because we can't write a path where one part is + // the `ty_to_nonzero!($value_ty)` invocation. + #[allow(clippy::useless_attribute, unused)] + use num::*; + impl $crate::sealed::Sealed for ty_to_nonzero!($value_ty) {} + impl $crate::field::Value for ty_to_nonzero!($value_ty) { + fn record(&self, key: &$crate::field::Field, visitor: &mut dyn $crate::field::Visit) { + visitor.$record(key, $op(self.get())) + } + } + }; +} + +macro_rules! impl_value { + ( $record:ident( $( $value_ty:tt ),+ ) ) => { + $( + impl_one_value!($value_ty, |this: $value_ty| this, $record); )+ }; - ( $record:ident( $( $value_ty:ty ),+ as $as_ty:ty) ) => { + ( $record:ident( $( $value_ty:tt ),+ as $as_ty:ty) ) => { $( - impl $crate::sealed::Sealed for $value_ty {} - impl Value for $value_ty { - fn record( - &self, - key: &$crate::field::Field, - visitor: &mut dyn $crate::field::Visit, - ) { - visitor.$record(key, *self as $as_ty) - } - } + impl_one_value!($value_ty, |this: $value_ty| this as $as_ty, $record); )+ }; } @@ -317,6 +373,13 @@ impl_values! { record_bool(bool) } +impl crate::sealed::Sealed for Wrapping {} +impl crate::field::Value for Wrapping { + fn record(&self, key: &crate::field::Field, visitor: &mut dyn crate::field::Visit) { + self.0.record(key, visitor) + } +} + impl crate::sealed::Sealed for str {} impl Value for str { diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 7024db2e..57a89582 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -27,7 +27,7 @@ keywords = ["logging", "tracing", "metrics", "async"] edition = "2018" [dependencies] -tracing-core = { version = "0.1.9", default-features = false } +tracing-core = { path = "../tracing-core", version = "0.1.9", default-features = false } log = { version = "0.4", optional = true } tracing-attributes = "0.1.6" cfg-if = "0.1.10" diff --git a/tracing/test-log-support/tests/log_with_trace.rs b/tracing/test-log-support/tests/log_with_trace.rs index 25937c34..23ae8071 100644 --- a/tracing/test-log-support/tests/log_with_trace.rs +++ b/tracing/test-log-support/tests/log_with_trace.rs @@ -33,6 +33,12 @@ fn test_always_log() { error!(foo = 5); test.assert_logged("foo=5"); + error!(foo = std::num::NonZeroU16::new(42).unwrap()); + test.assert_logged("foo=42"); + + error!(foo = std::num::Wrapping(39)); + test.assert_logged("foo=39"); + warn!("hello {};", "world"); test.assert_logged("hello world;"); diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs index 2aa4a257..5128090e 100644 --- a/tracing/tests/event.rs +++ b/tracing/tests/event.rs @@ -18,33 +18,43 @@ use tracing::{ Level, }; -#[test] -fn event_without_message() { - let (subscriber, handle) = subscriber::mock() - .event( - event::mock().with_fields( - field::mock("answer") - .with_value(&42) - .and( - field::mock("to_question") - .with_value(&"life, the universe, and everything"), - ) - .only(), - ), - ) - .done() - .run_with_handle(); +macro_rules! event_without_message { + ($name:ident: $e:expr) => { + #[test] + fn $name() { + let (subscriber, handle) = subscriber::mock() + .event( + event::mock().with_fields( + field::mock("answer") + .with_value(&42) + .and( + field::mock("to_question") + .with_value(&"life, the universe, and everything"), + ) + .only(), + ), + ) + .done() + .run_with_handle(); - with_default(subscriber, || { - info!( - answer = 42, - to_question = "life, the universe, and everything" - ); - }); + with_default(subscriber, || { + info!( + answer = $e, + to_question = "life, the universe, and everything" + ); + }); - handle.assert_finished(); + handle.assert_finished(); + } + }; } +event_without_message! {event_without_message: 42} +event_without_message! {wrapping_event_without_message: std::num::Wrapping(42)} +event_without_message! {nonzeroi32_event_without_message: std::num::NonZeroI32::new(42).unwrap()} +// needs API breakage +//event_without_message!{nonzerou128_event_without_message: std::num::NonZeroU128::new(42).unwrap()} + #[test] fn event_with_message() { let (subscriber, handle) = subscriber::mock()