From 32cf418c3b632c4a0b763a24d8b1bb2c4c5f883f Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Thu, 30 Jul 2020 12:36:44 +1200 Subject: [PATCH] tracing: add support literals for field names (#790) ## Motivation Some opentracing systems use fields with : in the names, which are not legal rust identifiers. We could special case :, but then we have complicated double-nested patterns (repeated idents separated by : and a nested repeat separated by .), and this may by a case of always being one-step-behind as more cases turn up; so this patch instead just gets in front and lets users put in whatever they want: as they are not rust identifiers, they miss out on some niceness. ## Solution This permits : in field names but also potentially anything stringifiable, which may be overly liberal. Signed-off-by: Robert Collins --- README.md | 2 +- examples/README.md | 2 + .../examples/attrs-literal-field-names.rs | 22 ++++++++ tracing/src/lib.rs | 13 +++++ tracing/src/macros.rs | 53 +++++++++++++++++++ tracing/tests/macros.rs | 16 ++++++ 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 examples/examples/attrs-literal-field-names.rs diff --git a/README.md b/README.md index 54484e8c..82121d29 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ use tracing::{debug, error, info, span, warn, Level}; // the `#[tracing::instrument]` attribute creates and enters a span // every time the instrumented function is called. The span is named after the -// the function or method. Paramaters passed to the function are recorded as fields. +// the function or method. Parameters passed to the function are recorded as fields. #[tracing::instrument] pub fn shave(yak: usize) -> Result<(), Box> { // this creates an event at the DEBUG level with two fields: diff --git a/examples/README.md b/examples/README.md index b1d2c0cd..3c9cd88e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,6 +10,8 @@ This directory contains a collection of examples that demonstrate the use of the `slog-term`'s `Compact` formatter. - **tracing-attributes**: + `attrs-basic`: A simple example of the `#[instrument]` attribute. + + `attrs-literal-field-names`: Demonstrates using literal field names rather + than rust tokens.. + `attrs-args`: An example implementing a simple recursive calculation of Fibonacci numbers, to demonstrate how the `#[instrument]` attribute can record function arguments. diff --git a/examples/examples/attrs-literal-field-names.rs b/examples/examples/attrs-literal-field-names.rs new file mode 100644 index 00000000..0701cf1f --- /dev/null +++ b/examples/examples/attrs-literal-field-names.rs @@ -0,0 +1,22 @@ +#![deny(rust_2018_idioms)] + +use tracing::{debug, span, Level}; +use tracing_attributes::instrument; + +#[instrument] +#[inline] +fn suggest_band() -> String { + debug!("Suggesting a band."); + String::from("Wild Pink") +} + +fn main() { + let subscriber = tracing_subscriber::fmt() + .with_env_filter("attrs_literal_field_names=trace") + .finish(); + tracing::subscriber::with_default(subscriber, || { + let span = span!(Level::TRACE, "get_band_rec", "guid:x-request-id" = "abcdef"); + let _enter = span.enter(); + suggest_band(); + }); +} diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index bd850c0f..d51f1f5f 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -287,6 +287,19 @@ //! # } //!``` //! +//! Fields with names that are not Rust identifiers, or with names that are Rust reserved words, +//! may be created using quoted string literals. However, this may not be used with the local +//! variable shorthand. +//! ``` +//! # use tracing::{span, Level}; +//! # fn main() { +//! // records an event with fields whose names are not Rust identifiers +//! // - "guid:x-request-id", containing a `:`, with the value "abcdef" +//! // - "type", which is a reserved word, with the value "request" +//! span!(Level::TRACE, "api", "guid:x-request-id" = "abcdef", "type" = "request"); +//! # } +//!``` +//! //! The `?` sigil is shorthand that specifies a field should be recorded using //! its [`fmt::Debug`] implementation: //! ``` diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index b67b0fce..73f33e78 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -1959,6 +1959,48 @@ macro_rules! valueset { $next, ) }; + + // Handle literal names + (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&debug(&$val) as &Value)) }, + $next, + $($rest)* + ) + }; + (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&display(&$val) as &Value)) }, + $next, + $($rest)* + ) + }; + (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&$val as &Value)) }, + $next, + $($rest)* + ) + }; + (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&debug(&$val) as &Value)) }, + $next, + ) + }; + (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&display(&$val) as &Value)) }, + $next, + ) + }; + (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&$val as &Value)) }, + $next, + ) + }; + // Remainder is unparseable, but exists --- must be format args! (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => { $crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &Value)), $($out),* }, $next, ) @@ -2017,6 +2059,17 @@ macro_rules! fieldset { $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*) }; + // Handle literal names + (@ { $(,)* $($out:expr),* } $k:literal = ?$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) + }; + (@ { $(,)* $($out:expr),* } $k:literal = %$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) + }; + (@ { $(,)* $($out:expr),* } $k:literal = $val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) + }; + // Remainder is unparseable, but exists --- must be format args! (@ { $(,)* $($out:expr),* } $($rest:tt)+) => { $crate::fieldset!(@ { "message", $($out),*, }) diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs index 3a6f28e1..18bcdb39 100644 --- a/tracing/tests/macros.rs +++ b/tracing/tests/macros.rs @@ -262,6 +262,22 @@ fn error_span_with_parent() { error_span!(parent: &p, "bar",); } +#[test] +fn span_with_non_rust_symbol() { + span!(Level::TRACE, "non-rust", "guid:x-request-id" = ?"abcdef", "more {}", 42); + span!(Level::TRACE, "non-rust", "guid:x-request-id" = %"abcdef", "more {}", 51); + span!( + Level::TRACE, + "non-rust", + "guid:x-request-id" = "abcdef", + "more {}", + 60 + ); + span!(Level::TRACE, "non-rust", "guid:x-request-id" = ?"abcdef"); + span!(Level::TRACE, "non-rust", "guid:x-request-id" = %"abcdef"); + span!(Level::TRACE, "non-rust", "guid:x-request-id" = "abcdef"); +} + #[test] fn event() { event!(Level::DEBUG, foo = ?3, bar.baz = %2, quux = false);