diff --git a/tracing-subscriber/src/layer.rs b/tracing-subscriber/src/layer.rs index 03391e4fa6..aa996aaed3 100644 --- a/tracing-subscriber/src/layer.rs +++ b/tracing-subscriber/src/layer.rs @@ -1003,6 +1003,78 @@ where } } + /// Returns a [`SpanRef`] for the parent span of the given [`Event`], if + /// it has a parent. + /// + /// If the event has an explicitly overridden parent, this method returns + /// a reference to that span. If the event's parent is the current span, + /// this returns a reference to the current span, if there is one. If this + /// returns `None`, then either the event's parent was explicitly set to + /// `None`, or the event's parent was defined contextually, but no span + /// is currently entered. + /// + /// Compared to [`Context::current_span`] and [`Context::lookup_current`], + /// this respects overrides provided by the [`Event`]. + /// + /// Compared to [`Event::parent`], this automatically falls back to the contextual + /// span, if required. + /// + /// ```rust + /// use tracing::{Event, Subscriber}; + /// use tracing_subscriber::{ + /// layer::{Context, Layer}, + /// prelude::*, + /// registry::LookupSpan, + /// }; + /// + /// struct PrintingLayer; + /// impl Layer for PrintingLayer + /// where + /// S: Subscriber + for<'lookup> LookupSpan<'lookup>, + /// { + /// fn on_event(&self, event: &Event, ctx: Context) { + /// let span = ctx.event_span(event); + /// println!("Event in span: {:?}", span.map(|s| s.name())); + /// } + /// } + /// + /// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || { + /// tracing::info!("no span"); + /// // Prints: Event in span: None + /// + /// let span = tracing::info_span!("span"); + /// tracing::info!(parent: &span, "explicitly specified"); + /// // Prints: Event in span: Some("span") + /// + /// let _guard = span.enter(); + /// tracing::info!("contextual span"); + /// // Prints: Event in span: Some("span") + /// }); + /// ``` + /// + ///
+ ///
+    /// Note: This requires the wrapped subscriber to implement the
+    /// LookupSpan trait.
+    /// See the documentation on Context's
+    /// declaration for details.
+    /// 
+ #[inline] + #[cfg(feature = "registry")] + #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] + pub fn event_span(&self, event: &Event<'_>) -> Option> + where + S: for<'lookup> LookupSpan<'lookup>, + { + if event.is_root() { + None + } else if event.is_contextual() { + self.lookup_current() + } else { + event.parent().and_then(|id| self.span(id)) + } + } + /// Returns metadata for the span with the given `id`, if it exists. /// /// If this returns `None`, then no span exists for that ID (either it has @@ -1165,6 +1237,36 @@ where { Some(self.span(id)?.scope()) } + + /// Returns an iterator over the [stored data] for all the spans in the + /// current context, starting with the parent span of the specified event, + /// and ending with the root of the trace tree and ending with the current span. + /// + ///
+ ///
+    /// Note: Compared to scope this
+    /// returns the spans in reverse order (from leaf to root). Use
+    /// Scope::from_root
+    /// in case root-to-leaf ordering is desired.
+    /// 
+ /// + ///
+ ///
+    /// Note: This requires the wrapped subscriber to implement the
+    /// LookupSpan trait.
+    /// See the documentation on Context's
+    /// declaration for details.
+    /// 
+ /// + /// [stored data]: ../registry/struct.SpanRef.html + #[cfg(feature = "registry")] + #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] + pub fn event_scope(&self, event: &Event<'_>) -> Option> + where + S: for<'lookup> registry::LookupSpan<'lookup>, + { + Some(self.event_span(event)?.scope()) + } } impl<'a, S> Context<'a, S> { @@ -1194,6 +1296,8 @@ impl Identity { #[cfg(test)] pub(crate) mod tests { + use std::sync::{Arc, Mutex}; + use super::*; pub(crate) struct NopSubscriber; @@ -1308,4 +1412,41 @@ pub(crate) mod tests { ::downcast_ref::(&s).expect("layer 3 should downcast"); assert_eq!(&layer.0, "layer_3"); } + + #[test] + fn context_event_span() { + let last_event_span = Arc::new(Mutex::new(None)); + + struct RecordingLayer { + last_event_span: Arc>>, + } + + impl Layer for RecordingLayer + where + S: Subscriber + for<'lookup> LookupSpan<'lookup>, + { + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + let span = ctx.event_span(event); + *self.last_event_span.lock().unwrap() = span.map(|s| s.name()); + } + } + + tracing::subscriber::with_default( + crate::registry().with(RecordingLayer { + last_event_span: last_event_span.clone(), + }), + || { + tracing::info!("no span"); + assert_eq!(*last_event_span.lock().unwrap(), None); + + let parent = tracing::info_span!("explicit"); + tracing::info!(parent: &parent, "explicit span"); + assert_eq!(*last_event_span.lock().unwrap(), Some("explicit")); + + let _guard = tracing::info_span!("contextual").entered(); + tracing::info!("contextual span"); + assert_eq!(*last_event_span.lock().unwrap(), Some("contextual")); + }, + ); + } }