mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-09-29 22:10:38 +00:00
core: add initial support for valuable
field values (#1608)
This branch adds initial support for using the [`valuable`] crate as an opt-in `Value` type in `tracing`. `valuable` provides a mechanism for defining custom ways to record user-implemented types, as well as structured recording of standard library data structures such as maps, arrays, and sets. For more details, see the tracking issue #1570. In `tracing` v0.2, the intent is for `valuable` to replace the existing `tracing_core::field::Value` trait. However, in v0.1.x, `valuable` support must be added in a backwards-compatible way, so recording types that implement `valuable::Valueable` currently requires opting in using a `field::valuable` wrapper function. Since this is the first release of `valuable` and the API is still stabilizing, we don't want to tie `tracing-core`'s stability to `valuable`'s. Therefore, the valuable dependency is feature-flagged *and* also requires `RUSTFLAGS="--cfg tracing_unstable"`. [`valuable`]: https://github.com/tokio-rs/valuable Co-authored-by: Daniel McKenna <daniel@emotech.co> Co-authored-by: David Barsky <me@davidbarsky.com> Co-authored-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
parent
71fc562f32
commit
5d08634501
@ -58,3 +58,9 @@ opentelemetry-jaeger = "0.15"
|
||||
# fmt examples
|
||||
snafu = "0.6.10"
|
||||
thiserror = "1.0.26"
|
||||
|
||||
# valuable examples
|
||||
valuable = { version = "0.1.0", features = ["derive"] }
|
||||
|
||||
[target.'cfg(tracing_unstable)'.dependencies]
|
||||
tracing-core = { path = "../tracing-core", version = "0.1", features = ["valuable"]}
|
||||
|
60
examples/examples/valuable.rs
Normal file
60
examples/examples/valuable.rs
Normal file
@ -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");
|
||||
}
|
45
examples/examples/valuable_instrument.rs
Normal file
45
examples/examples/valuable_instrument.rs
Normal file
@ -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::<HashMap<_, _>>();
|
||||
|
||||
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");
|
||||
}
|
@ -27,7 +27,7 @@ edition = "2018"
|
||||
rust-version = "1.42.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
default = ["std", "valuable/std"]
|
||||
std = ["lazy_static"]
|
||||
|
||||
[badges]
|
||||
@ -36,6 +36,9 @@ maintenance = { status = "actively-developed" }
|
||||
[dependencies]
|
||||
lazy_static = { version = "1", 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"]
|
||||
|
@ -184,6 +184,15 @@ pub struct Iter {
|
||||
/// [`Event`]: ../event/struct.Event.html
|
||||
/// [`ValueSet`]: struct.ValueSet.html
|
||||
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)
|
||||
@ -249,6 +258,14 @@ pub struct DisplayValue<T: fmt::Display>(T);
|
||||
#[derive(Clone)]
|
||||
pub struct DebugValue<T: fmt::Debug>(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: valuable::Valuable>(T);
|
||||
|
||||
/// Wraps a type implementing `fmt::Display` as a `Value` that can be
|
||||
/// recorded using its `Display` implementation.
|
||||
pub fn display<T>(t: T) -> DisplayValue<T>
|
||||
@ -267,6 +284,19 @@ where
|
||||
DebugValue(t)
|
||||
}
|
||||
|
||||
/// Wraps a type implementing [`Valuable`] as a `Value` that
|
||||
/// can be recorded using its `Valuable` implementation.
|
||||
///
|
||||
/// [`Valuable`]: https://docs.rs/valuable/latest/valuable/trait.Valuable.html
|
||||
#[cfg(all(tracing_unstable, feature = "valuable"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))]
|
||||
pub fn valuable<T>(t: T) -> ValuableValue<T>
|
||||
where
|
||||
T: valuable::Valuable,
|
||||
{
|
||||
ValuableValue(t)
|
||||
}
|
||||
|
||||
// ===== impl Visit =====
|
||||
|
||||
impl<'a, 'b> Visit for fmt::DebugStruct<'a, 'b> {
|
||||
@ -539,6 +569,27 @@ impl<T: fmt::Debug> fmt::Debug for DebugValue<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl ValuableValue =====
|
||||
|
||||
#[cfg(all(tracing_unstable, feature = "valuable"))]
|
||||
impl<T: valuable::Valuable> crate::sealed::Sealed for ValuableValue<T> {}
|
||||
|
||||
#[cfg(all(tracing_unstable, feature = "valuable"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))]
|
||||
impl<T: valuable::Valuable> Value for ValuableValue<T> {
|
||||
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<T: valuable::Valuable> fmt::Debug for ValuableValue<T> {
|
||||
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]
|
||||
|
@ -66,6 +66,7 @@ async-await = []
|
||||
std = ["tracing-core/std"]
|
||||
log-always = ["log"]
|
||||
attributes = ["tracing-attributes"]
|
||||
valuable = ["tracing-core/valuable"]
|
||||
|
||||
[[bench]]
|
||||
name = "subscriber"
|
||||
|
Loading…
x
Reference in New Issue
Block a user