| use tracing_core::{dispatcher::get_default as with_dispatcher, span, span::Id, Dispatch}; |
| // these are used later in macros. |
| pub use tracing_core::{field, metadata, Event, Metadata}; |
| |
| /// An entered span which will exit on drop. |
| #[derive(Clone)] |
| pub struct Span { |
| id: Option<(Id, Dispatch, &'static Metadata<'static>)>, |
| } |
| |
| impl Span { |
| /// Create a new span. |
| /// |
| /// This constructor is typically invoked by the [`crate::span!`] macro. |
| pub fn new( |
| level: crate::Level, |
| meta: &'static Metadata<'static>, |
| values: &tracing_core::field::ValueSet<'_>, |
| ) -> Self { |
| if level > crate::MAX_LEVEL { |
| Self { id: None } |
| } else { |
| with_dispatcher(|dispatch| { |
| let id = dispatch.new_span(&tracing_core::span::Attributes::new(meta, values)); |
| dispatch.enter(&id); |
| Self { |
| id: Some((id, dispatch.clone(), meta)), |
| } |
| }) |
| } |
| } |
| |
| /// Record a single `field` to take `value`. |
| /// |
| /// ### Panics |
| /// |
| /// If the field name wasn't mentioned when the span was created. |
| pub fn record<V>(&self, field: &str, value: V) -> &Self |
| where |
| V: field::Value, |
| { |
| if let Some((_, _, meta)) = &self.id { |
| let fields = meta.fields(); |
| let field = fields |
| .field(field) |
| .unwrap_or_else(|| panic!("Field name '{field}' must be registered at creation time.")); |
| self.record_all(&fields.value_set(&[(&field, Some(&value as &dyn field::Value))])); |
| } |
| self |
| } |
| |
| fn record_all(&self, values: &field::ValueSet<'_>) -> &Self { |
| if let Some((id, dispatch, _)) = &self.id { |
| let record = span::Record::new(values); |
| dispatch.record(id, &record); |
| } |
| self |
| } |
| } |
| |
| impl Drop for Span { |
| fn drop(&mut self) { |
| if let Some((id, dispatch, _meta)) = self.id.take() { |
| dispatch.exit(&id); |
| dispatch.try_close(id); |
| } |
| } |
| } |
| |
| #[doc(hidden)] |
| pub struct MetaOnlyCallsite(pub &'static Metadata<'static>); |
| |
| impl tracing_core::callsite::Callsite for MetaOnlyCallsite { |
| fn set_interest(&self, _: tracing_core::subscriber::Interest) {} |
| |
| fn metadata(&self) -> &Metadata<'_> { |
| self.0 |
| } |
| } |
| |
| #[doc(hidden)] |
| impl crate::Level { |
| pub const fn into_tracing_level(self) -> tracing_core::Level { |
| match self { |
| crate::Level::Coarse => tracing_core::Level::INFO, |
| crate::Level::Detail => tracing_core::Level::DEBUG, |
| } |
| } |
| } |
| |
| /// A macro to create a span. |
| #[macro_export] |
| macro_rules! span { |
| (target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { |
| { |
| static META: $crate::Metadata<'static> = { |
| $crate::metadata! { |
| name: $name, |
| target: $target, |
| level: $lvl.into_tracing_level(), |
| fields: $crate::fieldset!( $($fields)* ), |
| callsite: &$crate::MetaOnlyCallsite(&META), |
| kind: $crate::metadata::Kind::SPAN, |
| } |
| }; |
| |
| $crate::Span::new( |
| $lvl, |
| &META, |
| &$crate::valueset!(META.fields(), $($fields)*), |
| ) |
| } |
| }; |
| (target: $target:expr, $lvl:expr, $name:expr) => { |
| $crate::span!(target: $target, $lvl, $name,) |
| }; |
| ($lvl:expr, $name:expr, $($fields:tt)*) => { |
| $crate::span!( |
| target: module_path!(), |
| $lvl, |
| $name, |
| $($fields)* |
| ) |
| }; |
| ($lvl:expr, $name:expr) => { |
| $crate::span!( |
| target: module_path!(), |
| $lvl, |
| $name, |
| ) |
| }; |
| } |
| |
| /// Create an event with the given level. |
| #[macro_export] |
| macro_rules! event { |
| (target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ( |
| { |
| static META: $crate::Metadata<'static> = { |
| $crate::metadata! { |
| name: concat!( |
| "event ", |
| file!(), |
| ":", |
| line!() |
| ), |
| target: $target, |
| level: $lvl, |
| fields: $crate::fieldset!( $($fields)* ), |
| callsite: &$crate::MetaOnlyCallsite(&META), |
| kind: $crate::metadata::Kind::EVENT, |
| } |
| }; |
| $crate::Event::dispatch( |
| &META, |
| &$crate::valueset!(META.fields(), $($fields)*) |
| ); |
| } |
| ); |
| (target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( |
| $crate::event!( |
| target: $target, |
| $lvl, |
| { message = format_args!($($arg)+), $($fields)* } |
| ) |
| ); |
| (target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( |
| $crate::event!(target: $target, $lvl, { $($k).+ = $($fields)* }) |
| ); |
| (target: $target:expr, $lvl:expr, $($arg:tt)+ ) => ( |
| $crate::event!(target: $target, $lvl, { $($arg)+ }) |
| ); |
| ( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( |
| $crate::event!( |
| target: module_path!(), |
| $lvl, |
| { message = format_args!($($arg)+), $($fields)* } |
| ) |
| ); |
| ($lvl:expr, $($k:ident).+ = $($field:tt)*) => ( |
| $crate::event!( |
| target: module_path!(), |
| $lvl, |
| { $($k).+ = $($field)*} |
| ) |
| ); |
| ($lvl:expr, $($k:ident).+, $($field:tt)*) => ( |
| $crate::event!( |
| target: module_path!(), |
| $lvl, |
| { $($k).+, $($field)*} |
| ) |
| ); |
| ($lvl:expr, ?$($k:ident).+, $($field:tt)*) => ( |
| $crate::event!( |
| target: module_path!(), |
| $lvl, |
| { ?$($k).+, $($field)*} |
| ) |
| ); |
| ($lvl:expr, %$($k:ident).+, $($field:tt)*) => ( |
| $crate::event!( |
| target: module_path!(), |
| $lvl, |
| { %$($k).+, $($field)*} |
| ) |
| ); |
| ($lvl:expr, ?$($k:ident).+) => ( |
| $crate::event!($lvl, ?$($k).+,) |
| ); |
| ($lvl:expr, %$($k:ident).+) => ( |
| $crate::event!($lvl, %$($k).+,) |
| ); |
| ($lvl:expr, $($k:ident).+) => ( |
| $crate::event!($lvl, $($k).+,) |
| ); |
| ( $lvl:expr, $($arg:tt)+ ) => ( |
| $crate::event!(target: module_path!(), $lvl, { $($arg)+ }) |
| ); |
| } |
| |
| // Copied from`tracing`, would be nice to have it in `tracing-core`. |
| #[doc(hidden)] |
| #[macro_export] |
| macro_rules! fieldset { |
| // == base case == |
| (@ { $(,)* $($out:expr),* $(,)* } $(,)*) => { |
| &[ $($out),* ] |
| }; |
| |
| // == recursive cases (more tts) == |
| (@ { $(,)* $($out:expr),* } $($k:ident).+ = ?$val:expr, $($rest:tt)*) => { |
| $crate::fieldset!(@ { $($out),*, stringify!($($k).+) } $($rest)*) |
| }; |
| (@ { $(,)* $($out:expr),* } $($k:ident).+ = %$val:expr, $($rest:tt)*) => { |
| $crate::fieldset!(@ { $($out),*, stringify!($($k).+) } $($rest)*) |
| }; |
| (@ { $(,)* $($out:expr),* } $($k:ident).+ = $val:expr, $($rest:tt)*) => { |
| $crate::fieldset!(@ { $($out),*, stringify!($($k).+) } $($rest)*) |
| }; |
| // TODO(#1138): determine a new syntax for uninitialized span fields, and |
| // re-enable this. |
| // (@ { $($out:expr),* } $($k:ident).+ = _, $($rest:tt)*) => { |
| // $crate::fieldset!(@ { $($out),*, stringify!($($k).+) } $($rest)*) |
| // }; |
| (@ { $(,)* $($out:expr),* } ?$($k:ident).+, $($rest:tt)*) => { |
| $crate::fieldset!(@ { $($out),*, stringify!($($k).+) } $($rest)*) |
| }; |
| (@ { $(,)* $($out:expr),* } %$($k:ident).+, $($rest:tt)*) => { |
| $crate::fieldset!(@ { $($out),*, stringify!($($k).+) } $($rest)*) |
| }; |
| (@ { $(,)* $($out:expr),* } $($k:ident).+, $($rest:tt)*) => { |
| $crate::fieldset!(@ { $($out),*, 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 unparsable, but exists --- must be format args! |
| (@ { $(,)* $($out:expr),* } $($rest:tt)+) => { |
| $crate::fieldset!(@ { "message", $($out),*, }) |
| }; |
| |
| // == entry == |
| ($($args:tt)*) => { |
| $crate::fieldset!(@ { } $($args)*,) |
| }; |
| } |
| |
| // Copied from`tracing`, would be nice to have it in `tracing-core`. |
| #[doc(hidden)] |
| #[macro_export] |
| macro_rules! valueset { |
| |
| // === base case === |
| (@ { $(,)* $($val:expr),* $(,)* }, $next:expr $(,)*) => { |
| &[ $($val),* ] |
| }; |
| |
| // === recursive case (more tts) === |
| |
| // TODO(#1138): determine a new syntax for uninitialized span fields, and |
| // re-enable this. |
| // (@{ $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = _, $($rest:tt)*) => { |
| // $crate::valueset!(@ { $($out),*, (&$next, None) }, $next, $($rest)*) |
| // }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&$($k).+ as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&display(&$($k).+) as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&$($k).+ as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&display(&$($k).+) as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| |
| // Handle literal names |
| (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, |
| $next, |
| $($rest)* |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => { |
| $crate::valueset!( |
| @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, |
| $next, |
| ) |
| }; |
| |
| // Remainder is unparsable, but exists --- must be format args! |
| (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => { |
| $crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, ) |
| }; |
| |
| // === entry === |
| ($fields:expr, $($kvs:tt)+) => { |
| { |
| #[allow(unused_imports)] |
| use $crate::field::{debug, display, Value}; |
| let mut iter = $fields.iter(); |
| $fields.value_set($crate::valueset!( |
| @ { }, |
| iter.next().expect("FieldSet corrupted (this is a bug)"), |
| $($kvs)+ |
| )) |
| } |
| }; |
| ($fields:expr,) => { |
| { |
| $fields.value_set(&[]) |
| } |
| }; |
| } |