From 8829448d812a1a7222404d2f7f42067c31f27db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teo=20Klestrup=20R=C3=B6ijezon?= Date: Wed, 23 Jun 2021 20:26:35 +0200 Subject: [PATCH] subscriber: add Context method for resolving an Event's SpanRef (#1434) Fixes #1428 (forward-port from v0.1.x) Adds a new `Context::event_span` method as proposed in the issue. No existing formatters were changed to use it yet, that seems like a secondary issue (especially if they already work correctly). --- tracing-subscriber/src/subscribe.rs | 143 +++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/tracing-subscriber/src/subscribe.rs b/tracing-subscriber/src/subscribe.rs index b09ea0f38f..dde247b6dd 100644 --- a/tracing-subscriber/src/subscribe.rs +++ b/tracing-subscriber/src/subscribe.rs @@ -8,7 +8,7 @@ use tracing_core::{ }; #[cfg(feature = "registry")] -use crate::registry::{self, LookupSpan, Registry}; +use crate::registry::{self, LookupSpan, Registry, SpanRef}; use std::{any::TypeId, marker::PhantomData, ptr::NonNull}; /// A composable handler for `tracing` events. @@ -965,6 +965,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::{Collect, Event}; + /// use tracing_subscriber::{ + /// subscribe::{Context, Subscribe}, + /// prelude::*, + /// registry::LookupSpan, + /// }; + /// + /// struct PrintingSubscriber; + /// impl Subscribe for PrintingSubscriber + /// where + /// C: Collect + 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::collect::with_default(tracing_subscriber::registry().with(PrintingSubscriber), || { + /// 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 + C: 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 @@ -1100,6 +1172,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 + C: for<'lookup> registry::LookupSpan<'lookup>, + { + Some(self.event_span(event)?.scope()) + } } impl<'a, C> Context<'a, C> { @@ -1129,6 +1231,8 @@ impl Identity { #[cfg(test)] pub(crate) mod tests { + use std::sync::{Arc, Mutex}; + use super::*; pub(crate) struct NopCollector; @@ -1245,4 +1349,41 @@ pub(crate) mod tests { .expect("subscriber 3 should downcast"); assert_eq!(&subscriber.0, "subscriber_3"); } + + #[test] + fn context_event_span() { + let last_event_span = Arc::new(Mutex::new(None)); + + struct RecordingSubscriber { + last_event_span: Arc>>, + } + + impl Subscribe for RecordingSubscriber + where + S: Collect + 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::collect::with_default( + crate::registry().with(RecordingSubscriber { + 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")); + }, + ); + } }