From f54136cd14dd6efcfdea825b70a6bdb694212b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teo=20Klestrup=20R=C3=B6ijezon?= Date: Thu, 24 Jun 2021 19:12:29 +0200 Subject: [PATCH] subscriber: unify span traversal (#1445) Forward-port of #1431 and #1434 * subscriber: unify span traversal (#1431) ## Motivation Fixes #1429 (forwardport from v0.1.x branch) ## Solution Implemented as described in #1429, and migrated all internal uses of the deprecated methods. All tests passed both before and after the migration (9ec8130). - Add a new method `SpanRef::scope(&self)` that returns a leaf-to-root `Iterator`, including the specified leaf - Add a new method `Scope::from_root(self)` (where `Scope` is the type returned by `SpanRef::scope` defined earlier) that returns a root-to-leaf `Iterator` that ends at the current leaf (in other words: it's essentially the same as `Scope::collect::>().into_iter().rev()`) - Deprecate all existing iterators, since they can be replaced by the new unified mechanism: - `Span::parents` is equivalent to `Span::scope().skip(1)` (although the `skip` is typically a bug) - `Span::from_root` is equivalent to `Span::scope().skip(1).from_root()` (although the `skip` is typically a bug) - `Context::scope` is equivalent to `Context::lookup_current().scope().from_root()` (although the `lookup_current` is sometimes a bug, see also #1428) * subscriber: add Context method for resolving an Event's SpanRef (#1434) ## Motivation Fixes #1428 (forward-port from v0.1.x) ## Solution 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-error/src/subscriber.rs | 3 +- tracing-flame/src/lib.rs | 23 +- tracing-journald/src/lib.rs | 10 +- tracing-subscriber/src/fmt/fmt_subscriber.rs | 20 +- tracing-subscriber/src/fmt/format/json.rs | 6 +- tracing-subscriber/src/fmt/format/mod.rs | 10 +- tracing-subscriber/src/fmt/format/pretty.rs | 10 +- tracing-subscriber/src/registry/mod.rs | 300 ++++++++++++++----- tracing-subscriber/src/subscribe.rs | 204 ++++++++++--- 9 files changed, 412 insertions(+), 174 deletions(-) diff --git a/tracing-error/src/subscriber.rs b/tracing-error/src/subscriber.rs index d27fe62728..8874401a22 100644 --- a/tracing-error/src/subscriber.rs +++ b/tracing-error/src/subscriber.rs @@ -98,8 +98,7 @@ where let span = collector .span(id) .expect("registry should have a span for the current ID"); - let parents = span.parents(); - for span in std::iter::once(span).chain(parents) { + for span in span.scope() { let cont = if let Some(fields) = span.extensions().get::>() { f(span.metadata(), fields.fields.as_str()) } else { diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs index 33d86b8d9e..654c0fbdba 100644 --- a/tracing-flame/src/lib.rs +++ b/tracing-flame/src/lib.rs @@ -394,12 +394,10 @@ where let first = ctx.span(id).expect("expected: span id exists in registry"); - if !self.config.empty_samples && first.from_root().count() == 0 { + if !self.config.empty_samples && first.parent().is_none() { return; } - let parents = first.from_root(); - let mut stack = String::new(); if !self.config.threads_collapsed { @@ -408,9 +406,12 @@ where stack += "all-threads"; } - for parent in parents { - stack += "; "; - write(&mut stack, parent, &self.config).expect("expected: write to String never fails"); + if let Some(second) = first.parent() { + for parent in second.scope().from_root() { + stack += "; "; + write(&mut stack, parent, &self.config) + .expect("expected: write to String never fails"); + } } write!(&mut stack, " {}", samples.as_nanos()) @@ -440,7 +441,6 @@ where let samples = self.time_since_last_event(); let first = expect!(ctx.span(&id), "expected: span id exists in registry"); - let parents = first.from_root(); let mut stack = String::new(); if !self.config.threads_collapsed { @@ -448,20 +448,15 @@ where } else { stack += "all-threads"; } - stack += "; "; - for parent in parents { + for parent in first.scope().from_root() { + stack += "; "; expect!( write(&mut stack, parent, &self.config), "expected: write to String never fails" ); - stack += "; "; } - expect!( - write(&mut stack, first, &self.config), - "expected: write to String never fails" - ); expect!( write!(&mut stack, " {}", samples.as_nanos()), "expected: write to String never fails" diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index 5278974f57..b40d946a3c 100644 --- a/tracing-journald/src/lib.rs +++ b/tracing-journald/src/lib.rs @@ -126,7 +126,7 @@ where let span = ctx.span(id).expect("unknown span"); let mut buf = Vec::with_capacity(256); - let depth = span.parents().count(); + let depth = span.scope().skip(1).count(); writeln!(buf, "S{}_NAME", depth).unwrap(); put_value(&mut buf, span.name().as_bytes()); @@ -143,7 +143,7 @@ where fn on_record(&self, id: &Id, values: &Record, ctx: Context) { let span = ctx.span(id).expect("unknown span"); - let depth = span.parents().count(); + let depth = span.scope().skip(1).count(); let mut exts = span.extensions_mut(); let buf = &mut exts.get_mut::().expect("missing fields").0; values.record(&mut SpanVisitor { @@ -157,7 +157,11 @@ where let mut buf = Vec::with_capacity(256); // Record span fields - for span in ctx.scope() { + for span in ctx + .lookup_current() + .into_iter() + .flat_map(|span| span.scope().from_root()) + { let exts = span.extensions(); let fields = exts.get::().expect("missing fields"); buf.extend_from_slice(&fields.0); diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs index de91d147fd..c89556e6af 100644 --- a/tracing-subscriber/src/fmt/fmt_subscriber.rs +++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs @@ -2,7 +2,7 @@ use crate::{ field::RecordFields, fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter}, registry::{LookupSpan, SpanRef}, - subscribe::{self, Context, Scope}, + subscribe::{self, Context}, }; use format::{FmtSpan, TimingDisplay}; use std::{ @@ -756,8 +756,10 @@ where F: FnMut(&SpanRef<'_, C>) -> Result<(), E>, { // visit all the current spans - for span in self.ctx.scope() { - f(&span)?; + if let Some(leaf) = self.ctx.lookup_current() { + for span in leaf.scope().from_root() { + f(&span)?; + } } Ok(()) } @@ -811,18 +813,6 @@ where self.ctx.lookup_current() } - /// Returns an iterator over the [stored data] for all the spans in the - /// current context, starting the root of the trace tree and ending with - /// the current span. - /// - /// [stored data]: SpanRef - pub fn scope(&self) -> Scope<'_, C> - where - C: for<'lookup> LookupSpan<'lookup>, - { - self.ctx.scope() - } - /// Returns the current span for this formatter. pub fn current_span(&self) -> Current { self.ctx.current_span() diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index 7b379348f7..083729d328 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -95,8 +95,10 @@ where use serde::ser::SerializeSeq; let mut serializer = serializer_o.serialize_seq(None)?; - for span in self.0.scope() { - serializer.serialize_element(&SerializableSpan(&span, self.1))?; + if let Some(leaf_span) = self.0.lookup_current() { + for span in leaf_span.scope().from_root() { + serializer.serialize_element(&SerializableSpan(&span, self.1))?; + } } serializer.end() diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index 583f7d244d..b14b3fd02c 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -9,7 +9,6 @@ use crate::{ use std::{ fmt::{self, Write}, - iter, marker::PhantomData, }; use tracing_core::{ @@ -650,10 +649,7 @@ where .and_then(|id| ctx.ctx.span(&id)) .or_else(|| ctx.ctx.lookup_current()); - let scope = span.into_iter().flat_map(|span| { - let parents = span.parents(); - iter::once(span).chain(parents) - }); + let scope = span.into_iter().flat_map(|span| span.scope()); #[cfg(feature = "ansi")] let dimmed = if self.ansi { Style::new().dimmed() @@ -876,9 +872,7 @@ where .and_then(|id| self.ctx.ctx.span(&id)) .or_else(|| self.ctx.ctx.lookup_current()); - let scope = span - .into_iter() - .flat_map(|span| span.from_root().chain(iter::once(span))); + let scope = span.into_iter().flat_map(|span| span.scope().from_root()); for span in scope { write!(f, "{}", bold.paint(span.metadata().name()))?; diff --git a/tracing-subscriber/src/fmt/format/pretty.rs b/tracing-subscriber/src/fmt/format/pretty.rs index 724116b2b5..586f0f3933 100644 --- a/tracing-subscriber/src/fmt/format/pretty.rs +++ b/tracing-subscriber/src/fmt/format/pretty.rs @@ -5,10 +5,7 @@ use crate::{ registry::LookupSpan, }; -use std::{ - fmt::{self, Write}, - iter, -}; +use std::fmt::{self, Write}; use tracing_core::{ field::{self, Field}, Collect, Event, Level, @@ -186,10 +183,7 @@ where .and_then(|id| ctx.span(&id)) .or_else(|| ctx.lookup_current()); - let scope = span.into_iter().flat_map(|span| { - let parents = span.parents(); - iter::once(span).chain(parents) - }); + let scope = span.into_iter().flat_map(|span| span.scope()); for span in scope { let meta = span.metadata(); diff --git a/tracing-subscriber/src/registry/mod.rs b/tracing-subscriber/src/registry/mod.rs index 01ecf92c71..c5441d4188 100644 --- a/tracing-subscriber/src/registry/mod.rs +++ b/tracing-subscriber/src/registry/mod.rs @@ -58,6 +58,8 @@ //! [`Collect`]: tracing_core::collect::Collect //! [ctx]: crate::subscribe::Context //! [lookup]: crate::subscribe::Context::span() +use std::fmt::Debug; + use tracing_core::{field::FieldSet, span::Id, Metadata}; /// A module containing a type map of span extensions. @@ -161,27 +163,91 @@ pub struct SpanRef<'a, R: LookupSpan<'a>> { data: R::Data, } -/// An iterator over the parents of a span. -/// -/// This is returned by the [`SpanRef::parents`] method. +/// An iterator over the parents of a span, ordered from leaf to root. /// +/// This is returned by the [`SpanRef::scope`] method. #[derive(Debug)] -pub struct Parents<'a, R> { +pub struct Scope<'a, R> { registry: &'a R, next: Option, } -/// An iterator over a span's parents, starting with the root of the trace -/// tree. -/// -/// For additional details, see [`SpanRef::from_root`]. +impl<'a, R> Scope<'a, R> +where + R: LookupSpan<'a>, +{ + /// Flips the order of the iterator, so that it is ordered from root to leaf. + /// + /// The iterator will first return the root span, then that span's immediate child, + /// and so on until it finally returns the span that [`SpanRef::scope`] was called on. + /// + /// If any items were consumed from the [`Scope`] before calling this method then they + /// will *not* be returned from the [`ScopeFromRoot`]. + /// + /// **Note**: this will allocate if there are many spans remaining, or if the + /// "smallvec" feature flag is not enabled. + #[allow(clippy::wrong_self_convention)] + pub fn from_root(self) -> ScopeFromRoot<'a, R> { + #[cfg(feature = "smallvec")] + type Buf = smallvec::SmallVec; + #[cfg(not(feature = "smallvec"))] + type Buf = Vec; + ScopeFromRoot { + spans: self.collect::>().into_iter().rev(), + } + } +} + +impl<'a, R> Iterator for Scope<'a, R> +where + R: LookupSpan<'a>, +{ + type Item = SpanRef<'a, R>; + + fn next(&mut self) -> Option { + let curr = self.registry.span(self.next.as_ref()?)?; + self.next = curr.parent_id().cloned(); + Some(curr) + } +} + +/// An iterator over the parents of a span, ordered from root to leaf. /// -/// [`Span::from_root`]: SpanRef::from_root() -pub struct FromRoot<'a, R: LookupSpan<'a>> { +/// This is returned by the [`Scope::from_root`] method. +pub struct ScopeFromRoot<'a, R> +where + R: LookupSpan<'a>, +{ #[cfg(feature = "smallvec")] - inner: std::iter::Rev>>, + spans: std::iter::Rev>>, #[cfg(not(feature = "smallvec"))] - inner: std::iter::Rev>>, + spans: std::iter::Rev>>, +} + +impl<'a, R> Iterator for ScopeFromRoot<'a, R> +where + R: LookupSpan<'a>, +{ + type Item = SpanRef<'a, R>; + + #[inline] + fn next(&mut self) -> Option { + self.spans.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.spans.size_hint() + } +} + +impl<'a, R> Debug for ScopeFromRoot<'a, R> +where + R: LookupSpan<'a>, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.pad("ScopeFromRoot { .. }") + } } #[cfg(feature = "smallvec")] @@ -230,40 +296,78 @@ where }) } - /// Returns an iterator over all parents of this span, starting with the - /// immediate parent. + /// Returns an iterator over all parents of this span, starting with this span, + /// ordered from leaf to root. /// - /// The iterator will first return the span's immediate parent, followed by - /// that span's parent, followed by _that_ span's parent, and so on, until a - /// it reaches a root span. - pub fn parents(&self) -> Parents<'a, R> { - Parents { - registry: self.registry, - next: self.parent().map(|parent| parent.id()), - } - } - - /// Returns an iterator over all parents of this span, starting with the - /// root of the trace tree. + /// The iterator will first return the span, then the span's immediate parent, + /// followed by that span's parent, and so on, until it reaches a root span. /// - /// The iterator will return the root of the trace tree, followed by the - /// next span, and then the next, until this span's immediate parent is - /// returned. + /// ```rust + /// use tracing::{span, Collect}; + /// use tracing_subscriber::{ + /// subscribe::{Context, Subscribe}, + /// prelude::*, + /// registry::LookupSpan, + /// }; /// - /// **Note**: if the "smallvec" feature flag is not enabled, this may - /// allocate. - pub fn from_root(&self) -> FromRoot<'a, R> { - #[cfg(feature = "smallvec")] - type SpanRefVec<'span, L> = smallvec::SmallVec>; - #[cfg(not(feature = "smallvec"))] - type SpanRefVec<'span, L> = Vec>; - - // an alternative way to handle this would be to the recursive approach that - // `fmt` uses that _does not_ entail any allocation in this fmt'ing - // spans path. - let parents = self.parents().collect::>(); - let inner = parents.into_iter().rev(); - FromRoot { inner } + /// struct PrintingSubscriber; + /// impl Subscribe for PrintingSubscriber + /// where + /// C: Collect + for<'lookup> LookupSpan<'lookup>, + /// { + /// fn on_enter(&self, id: &span::Id, ctx: Context) { + /// let span = ctx.span(id).unwrap(); + /// let scope = span.scope().map(|span| span.name()).collect::>(); + /// println!("Entering span: {:?}", scope); + /// } + /// } + /// + /// tracing::collect::with_default(tracing_subscriber::registry().with(PrintingSubscriber), || { + /// let _root = tracing::info_span!("root").entered(); + /// // Prints: Entering span: ["root"] + /// let _child = tracing::info_span!("child").entered(); + /// // Prints: Entering span: ["child", "root"] + /// let _leaf = tracing::info_span!("leaf").entered(); + /// // Prints: Entering span: ["leaf", "child", "root"] + /// }); + /// ``` + /// + /// If the opposite order (from the root to this span) is desired, calling [`Scope::from_root`] on + /// the returned iterator reverses the order. + /// + /// ```rust + /// # use tracing::{span, Collect}; + /// # use tracing_subscriber::{ + /// # subscribe::{Context, Subscribe}, + /// # prelude::*, + /// # registry::LookupSpan, + /// # }; + /// # struct PrintingSubscriber; + /// impl Subscribe for PrintingSubscriber + /// where + /// C: Collect + for<'lookup> LookupSpan<'lookup>, + /// { + /// fn on_enter(&self, id: &span::Id, ctx: Context) { + /// let span = ctx.span(id).unwrap(); + /// let scope = span.scope().from_root().map(|span| span.name()).collect::>(); + /// println!("Entering span: {:?}", scope); + /// } + /// } + /// + /// tracing::collect::with_default(tracing_subscriber::registry().with(PrintingSubscriber), || { + /// let _root = tracing::info_span!("root").entered(); + /// // Prints: Entering span: ["root"] + /// let _child = tracing::info_span!("child").entered(); + /// // Prints: Entering span: ["root", "child"] + /// let _leaf = tracing::info_span!("leaf").entered(); + /// // Prints: Entering span: ["root", "child", "leaf"] + /// }); + /// ``` + pub fn scope(&self) -> Scope<'a, R> { + Scope { + registry: self.registry, + next: Some(self.id()), + } } /// Returns a reference to this span's `Extensions`. @@ -283,43 +387,87 @@ where } } -impl<'a, R> Iterator for Parents<'a, R> -where - R: LookupSpan<'a>, -{ - type Item = SpanRef<'a, R>; - fn next(&mut self) -> Option { - let id = self.next.take()?; - let span = self.registry.span(&id)?; - self.next = span.parent().map(|parent| parent.id()); - Some(span) - } -} - -// === impl FromRoot === +#[cfg(test)] +mod tests { + use crate::{ + prelude::*, + registry::LookupSpan, + subscribe::{Context, Subscribe}, + }; + use std::sync::{Arc, Mutex}; + use tracing::{span, Collect}; + + #[test] + fn spanref_scope_iteration_order() { + let last_entered_scope = Arc::new(Mutex::new(Vec::new())); + + #[derive(Default)] + struct RecordingSubscriber { + last_entered_scope: Arc>>, + } -impl<'span, R> Iterator for FromRoot<'span, R> -where - R: LookupSpan<'span>, -{ - type Item = SpanRef<'span, R>; + impl Subscribe for RecordingSubscriber + where + S: Collect + for<'lookup> LookupSpan<'lookup>, + { + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { + let span = ctx.span(id).unwrap(); + let scope = span.scope().map(|span| span.name()).collect::>(); + *self.last_entered_scope.lock().unwrap() = scope; + } + } - #[inline] - fn next(&mut self) -> Option { - self.inner.next() + let _guard = tracing::collect::set_default(crate::registry().with(RecordingSubscriber { + last_entered_scope: last_entered_scope.clone(), + })); + + let _root = tracing::info_span!("root").entered(); + assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]); + let _child = tracing::info_span!("child").entered(); + assert_eq!(&*last_entered_scope.lock().unwrap(), &["child", "root"]); + let _leaf = tracing::info_span!("leaf").entered(); + assert_eq!( + &*last_entered_scope.lock().unwrap(), + &["leaf", "child", "root"] + ); } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} + #[test] + fn spanref_scope_fromroot_iteration_order() { + let last_entered_scope = Arc::new(Mutex::new(Vec::new())); -impl<'span, R> std::fmt::Debug for FromRoot<'span, R> -where - R: LookupSpan<'span>, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.pad("FromRoot { .. }") + #[derive(Default)] + struct RecordingSubscriber { + last_entered_scope: Arc>>, + } + + impl Subscribe for RecordingSubscriber + where + S: Collect + for<'lookup> LookupSpan<'lookup>, + { + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { + let span = ctx.span(id).unwrap(); + let scope = span + .scope() + .from_root() + .map(|span| span.name()) + .collect::>(); + *self.last_entered_scope.lock().unwrap() = scope; + } + } + + let _guard = tracing::collect::set_default(crate::registry().with(RecordingSubscriber { + last_entered_scope: last_entered_scope.clone(), + })); + + let _root = tracing::info_span!("root").entered(); + assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]); + let _child = tracing::info_span!("child").entered(); + assert_eq!(&*last_entered_scope.lock().unwrap(), &["root", "child",]); + let _leaf = tracing::info_span!("leaf").entered(); + assert_eq!( + &*last_entered_scope.lock().unwrap(), + &["root", "child", "leaf"] + ); } } diff --git a/tracing-subscriber/src/subscribe.rs b/tracing-subscriber/src/subscribe.rs index 608d59273a..8db7a8e0dd 100644 --- a/tracing-subscriber/src/subscribe.rs +++ b/tracing-subscriber/src/subscribe.rs @@ -561,19 +561,6 @@ pub struct Identity { _p: (), } -/// An iterator over the [stored data] for all the spans in the -/// current context, starting the root of the trace tree and ending with -/// the current span. -/// -/// This is returned by [`Context::scope`]. -/// -/// [stored data]: super::registry::SpanRef -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -pub struct Scope<'a, L: LookupSpan<'a>>( - Option, std::iter::Once>>>, -); - // === impl Layered === impl Collect for Layered @@ -987,6 +974,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 @@ -1079,31 +1138,66 @@ where } /// Returns an iterator over the [stored data] for all the spans in the - /// current context, starting the root of the trace tree and ending with - /// the current span. + /// current context, starting with the specified span and ending with the + /// root of the trace tree and ending with the current span. /// - /// If this iterator is empty, then there are no spans in the current context. + ///
+ ///
Note
+ ///
+ ///
+ ///
+    /// 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.
+    /// 
/// - /// **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 span_scope(&self, id: &span::Id) -> Option> + where + C: for<'lookup> registry::LookupSpan<'lookup>, + { + 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.
     /// 
/// - /// [stored data]: super::registry::SpanRef + ///
+ ///
+    /// 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 scope(&self) -> Scope<'_, C> + pub fn event_scope(&self, event: &Event<'_>) -> Option> where C: for<'lookup> registry::LookupSpan<'lookup>, { - let scope = self.lookup_current().map(|span| { - let parents = span.from_root(); - parents.chain(std::iter::once(span)) - }); - Scope(scope) + Some(self.event_span(event)?.scope()) } } @@ -1132,29 +1226,10 @@ impl Identity { } } -// === impl Scope === - -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -impl<'a, L: LookupSpan<'a>> Iterator for Scope<'a, L> { - type Item = SpanRef<'a, L>; - - #[inline] - fn next(&mut self) -> Option { - self.0.as_mut()?.next() - } -} - -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -impl<'a, L: LookupSpan<'a>> std::fmt::Debug for Scope<'a, L> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.pad("Scope { .. }") - } -} - #[cfg(test)] pub(crate) mod tests { + use std::sync::{Arc, Mutex}; + use super::*; pub(crate) struct NopCollector; @@ -1277,4 +1352,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")); + }, + ); + } }