diff --git a/examples/examples/valuable.rs b/examples/examples/valuable.rs new file mode 100644 index 000000000..53913a52c --- /dev/null +++ b/examples/examples/valuable.rs @@ -0,0 +1,60 @@ +#![allow(dead_code)] +//! This example shows how a field value may be recorded using the `valuable` +//! crate (https://crates.io/crates/valuable). +//! +//! `valuable` provides a lightweight but flexible way to record structured data, allowing +//! visitors to extract individual fields or elements of structs, maps, arrays, and other +//! nested structures. +//! +//! `tracing`'s support for `valuable` is currently feature flagged. Additionally, `valuable` +//! support is considered an *unstable feature*: in order to use `valuable` with `tracing`, +//! the project must be built with `RUSTFLAGS="--cfg tracing_unstable`. +//! +//! Therefore, when `valuable` support is not enabled, this example falls back to using +//! `fmt::Debug` to record fields that implement `valuable::Valuable`. +#[cfg(tracing_unstable)] +use tracing::field::valuable; +use tracing::{info, info_span}; +use valuable::Valuable; + +#[derive(Clone, Debug, Valuable)] +struct User { + name: String, + age: u32, + address: Address, +} + +#[derive(Clone, Debug, Valuable)] +struct Address { + country: String, + city: String, + street: String, +} + +fn main() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::TRACE) + .init(); + + let user = User { + name: "Arwen Undomiel".to_string(), + age: 3000, + address: Address { + country: "Middle Earth".to_string(), + city: "Rivendell".to_string(), + street: "leafy lane".to_string(), + }, + }; + + // If the `valuable` feature is enabled, record `user` using its' + // `valuable::Valuable` implementation: + #[cfg(tracing_unstable)] + let span = info_span!("Processing", user = valuable(&user)); + + // Otherwise, record `user` using its `fmt::Debug` implementation: + #[cfg(not(tracing_unstable))] + let span = info_span!("Processing", user = ?user); + + let _handle = span.enter(); + info!("Nothing to do"); +} diff --git a/examples/examples/valuable_instrument.rs b/examples/examples/valuable_instrument.rs new file mode 100644 index 000000000..a8f93b5a8 --- /dev/null +++ b/examples/examples/valuable_instrument.rs @@ -0,0 +1,45 @@ +#[cfg(tracing_unstable)] +mod app { + use std::collections::HashMap; + use tracing::field::valuable; + use tracing::{info, info_span, instrument}; + use valuable::Valuable; + + #[derive(Valuable)] + struct Headers<'a> { + headers: HashMap<&'a str, &'a str>, + } + + // Current there's no way to automatically apply valuable to a type, so we need to make use of + // the fields argument for instrument + #[instrument(fields(headers=valuable(&headers)))] + fn process(headers: Headers) { + info!("Handle request") + } + + pub fn run() { + let headers = [ + ("content-type", "application/json"), + ("content-length", "568"), + ("server", "github.com"), + ] + .iter() + .cloned() + .collect::>(); + + let http_headers = Headers { headers }; + + process(http_headers); + } +} + +fn main() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::TRACE) + .init(); + + #[cfg(tracing_unstable)] + app::run(); + #[cfg(not(tracing_unstable))] + println!("Nothing to do, this example needs --cfg=tracing_unstable to run"); +} diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index c56b59f26..924a06630 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -37,6 +37,9 @@ maintenance = { status = "actively-developed" } [dependencies] once_cell = { version = "1.13.0", optional = true } +[target.'cfg(tracing_unstable)'.dependencies] +valuable = { version = "0.1.0", optional = true, default_features = false } + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 421924244..d0e19c897 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -189,6 +189,15 @@ pub struct Iter { /// [set of `Value`s added to a `Span`]: super::collect::Collect::record /// [`Event`]: super::event::Event pub trait Visit { + /// Visits an arbitrary type implementing the [`valuable`] crate's `Valuable` trait. + /// + /// [`valuable`]: https://docs.rs/valuable + #[cfg(all(tracing_unstable, feature = "valuable"))] + #[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))] + fn record_value(&mut self, field: &Field, value: &dyn valuable::Valuable) { + self.record_debug(field, &value) + } + /// Visit a double-precision floating point value. fn record_f64(&mut self, field: &Field, value: f64) { self.record_debug(field, &value) @@ -270,6 +279,14 @@ pub struct DisplayValue(T); #[derive(Clone)] pub struct DebugValue(T); +/// A `Value` which serializes using [`Valuable`]. +/// +/// [`Valuable`]: https://docs.rs/valuable/latest/valuable/trait.Valuable.html +#[derive(Clone)] +#[cfg(all(tracing_unstable, feature = "valuable"))] +#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))] +pub struct ValuableValue(T); + /// Wraps a type implementing `fmt::Display` as a `Value` that can be /// recorded using its `Display` implementation. pub fn display(t: T) -> DisplayValue @@ -658,6 +675,27 @@ impl fmt::Debug for DebugValue { } } +// ===== impl ValuableValue ===== + +#[cfg(all(tracing_unstable, feature = "valuable"))] +impl crate::sealed::Sealed for ValuableValue {} + +#[cfg(all(tracing_unstable, feature = "valuable"))] +#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))] +impl Value for ValuableValue { + fn record(&self, key: &Field, visitor: &mut dyn Visit) { + visitor.record_value(key, &self.0) + } +} + +#[cfg(all(tracing_unstable, feature = "valuable"))] +#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))] +impl fmt::Debug for ValuableValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", &self.0 as &dyn valuable::Valuable) + } +} + impl crate::sealed::Sealed for Empty {} impl Value for Empty { #[inline] diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index a653458fa..86b205654 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -66,6 +66,7 @@ alloc = ["tracing-core/alloc"] std = ["tracing-core/std", "alloc"] log-always = ["log"] attributes = ["tracing-attributes"] +valuable = ["tracing-core/valuable"] [[bench]] name = "baseline"