core: support Wrapping and NonZero types as Values (#538)

## Motivation

For tracing there's no difference between a `NonZero*` or `Wrapping`
type and their underlying type. This allows such types to directly be
referenced by the tracing macros without requiring additional method
calls or boilerplate.

## Solution

Implemented `Value` for `Wrapping<T: Value>` and for all the `NonZero*`
types from `std::num`.

This required a slight refactoring of the macros generating the regular
integer impls as to reduce the amount of generated code.
This commit is contained in:
Oliver Scherer @ Cosmian 2020-01-15 17:41:35 +01:00 committed by Eliza Weisman
parent ed7e5a7624
commit c5972d8447
4 changed files with 126 additions and 47 deletions

View File

@ -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<T: crate::sealed::Sealed> crate::sealed::Sealed for Wrapping<T> {}
impl<T: crate::field::Value> crate::field::Value for Wrapping<T> {
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 {

View File

@ -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"

View File

@ -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;");

View File

@ -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()