From 361a8678c488b0747ef1414330a3a4fe9c3ad04a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 4 May 2020 17:48:55 -0700 Subject: [PATCH] core: add `fmt::Debug` impl to `dyn Value`s (#696) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation Currently, the only way to interact with `Value`s is to record them with a visitor. In the most common case, where typed data is not needed, `Value`s are recorded with their `fmt::Debug` implementations — a visitor which does not implement the `record_${TYPE}` traits will automatically fall back to recording all primitive value types with `Debug`. However, implementing a visitor requires a bit of boilerplate: an entire trait implementation has to be written, and a visitor object passed to `record`. ## Solution This branch hopes to simplify this common case by adding a `Debug` implementation to _all_ `dyn Value` trait objects. This is equivalent to a visitor that only implements `record_debug`, but requiring less boilerplate. Signed-off-by: Eliza Weisman --- tracing-core/src/field.rs | 54 ++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 36636f32..cb9c8488 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -17,15 +17,15 @@ //! //! `tracing` represents values as either one of a set of Rust primitives //! (`i64`, `u64`, `bool`, and `&str`) or using a `fmt::Display` or `fmt::Debug` -//! implementation. The [`record`] trait method on the `Subscriber` trait -//! allow `Subscriber` implementations to provide type-specific behaviour for -//! consuming values of each type. +//! implementation. `Subscriber`s are provided these primitive value types as +//! `dyn Value` trait objects. //! -//! Instances of the [`Visit`] trait are provided by `Subscriber`s to record the -//! values attached to spans and `Event`. This trait represents the behavior -//! used to record values of various types. For example, we might record -//! integers by incrementing counters for their field names, rather than printing -//! them. +//! These trait objects can be formatted using `fmt::Debug`, but may also be +//! recorded as typed data by calling the [`Value::record`] method on these +//! trait objects with a _visitor_ implementing the [`Visit`] trait. This trait +//! represents the behavior used to record values of various types. For example, +//! we might record integers by incrementing counters for their field names, +//! rather than printing them. //! //! [`Value`]: trait.Value.html //! [span]: ../span/ @@ -35,7 +35,8 @@ //! [`Record`]: ../span/struct.Record.html //! [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span //! [`record`]: ../subscriber/trait.Subscriber.html#method.record -//! [`event`]: ../subscriber/trait.Subscriber.html#method.record +//! [`event`]: ../subscriber/trait.Subscriber.html#method.event +//! [`Value::record`]: trait.Value.html#method.record //! [`Visit`]: trait.Visit.html use crate::callsite; use crate::stdlib::{ @@ -427,6 +428,41 @@ impl<'a> Value for fmt::Arguments<'a> { } } +impl fmt::Debug for dyn Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // We are only going to be recording the field value, so we don't + // actually care about the field name here. + struct NullCallsite; + static NULL_CALLSITE: NullCallsite = NullCallsite; + impl crate::callsite::Callsite for NullCallsite { + fn set_interest(&self, _: crate::subscriber::Interest) { + unreachable!("you somehow managed to register the null callsite?") + } + + fn metadata(&self) -> &crate::Metadata<'_> { + unreachable!("you somehow managed to access the null callsite?") + } + } + + static FIELD: Field = Field { + i: 0, + fields: FieldSet::new(&[], crate::identify_callsite!(&NULL_CALLSITE)), + }; + + let mut res = Ok(()); + self.record(&FIELD, &mut |_: &Field, val: &dyn fmt::Debug| { + res = write!(f, "{:?}", val); + }); + res + } +} + +impl fmt::Display for dyn Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + // ===== impl DisplayValue ===== impl crate::sealed::Sealed for DisplayValue {}