diff --git a/examples/examples/sloggish/sloggish_subscriber.rs b/examples/examples/sloggish/sloggish_subscriber.rs index 94cf90a378..f438ed6526 100644 --- a/examples/examples/sloggish/sloggish_subscriber.rs +++ b/examples/examples/sloggish/sloggish_subscriber.rs @@ -133,7 +133,6 @@ impl<'a> Visit for Event<'a> { Style::new().bold().paint(format!("{:?}", value)) ) .unwrap(); - self.comma = true; } else { write!( &mut self.stderr, @@ -142,8 +141,8 @@ impl<'a> Visit for Event<'a> { value ) .unwrap(); - self.comma = true; } + self.comma = true; } } diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs index 91c23a41e9..7bf7428a62 100644 --- a/tracing-core/src/metadata.rs +++ b/tracing-core/src/metadata.rs @@ -829,10 +829,10 @@ mod tests { (LevelFilter::TRACE, Some(Level::TRACE)), ]; for (filter, level) in mapping.iter() { - assert_eq!(filter.clone().into_level(), *level); + assert_eq!(filter.into_level(), *level); match level { Some(level) => { - let actual: LevelFilter = level.clone().into(); + let actual: LevelFilter = (*level).into(); assert_eq!(actual, *filter); } None => { diff --git a/tracing-error/Cargo.toml b/tracing-error/Cargo.toml index 6a3abb2827..eb0962e02a 100644 --- a/tracing-error/Cargo.toml +++ b/tracing-error/Cargo.toml @@ -38,7 +38,7 @@ default = ["traced-error"] traced-error = [] [dependencies] -tracing-subscriber = { path = "../tracing-subscriber", version = "0.2.7", default-features = false, features = ["registry", "fmt"] } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.2.19", default-features = false, features = ["registry", "fmt"] } tracing = { path = "../tracing", version = "0.1.12", default-features = false, features = ["std"] } [badges] diff --git a/tracing-error/src/layer.rs b/tracing-error/src/layer.rs index 05e50c7d86..d2ddf65b2f 100644 --- a/tracing-error/src/layer.rs +++ b/tracing-error/src/layer.rs @@ -90,8 +90,7 @@ where let span = subscriber .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/Cargo.toml b/tracing-flame/Cargo.toml index dd2a994581..3ec28fcc62 100644 --- a/tracing-flame/Cargo.toml +++ b/tracing-flame/Cargo.toml @@ -25,7 +25,7 @@ default = ["smallvec"] smallvec = ["tracing-subscriber/smallvec"] [dependencies] -tracing-subscriber = { path = "../tracing-subscriber", version = "0.2.3", default-features = false, features = ["registry", "fmt"] } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.2.19", default-features = false, features = ["registry", "fmt"] } tracing = { path = "../tracing", version = "0.1.12", default-features = false, features = ["std"] } lazy_static = "1.3.0" diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs index e307d09e9b..e0392e9e68 100644 --- a/tracing-flame/src/lib.rs +++ b/tracing-flame/src/lib.rs @@ -398,12 +398,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 { @@ -412,9 +410,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()) @@ -444,7 +445,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 { @@ -452,20 +452,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/Cargo.toml b/tracing-journald/Cargo.toml index 3acce8ece4..905a288266 100644 --- a/tracing-journald/Cargo.toml +++ b/tracing-journald/Cargo.toml @@ -16,4 +16,4 @@ keywords = ["tracing", "journald"] [dependencies] tracing-core = { path = "../tracing-core", version = "0.1.10" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.2.5" } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.2.19" } diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index 8b3cfaed56..395b067dce 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/Cargo.toml b/tracing-subscriber/Cargo.toml index 277c843412..a518c41642 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-subscriber" -version = "0.2.18" +version = "0.2.19" authors = [ "Eliza Weisman ", "David Barsky ", diff --git a/tracing-subscriber/src/filter/env/mod.rs b/tracing-subscriber/src/filter/env/mod.rs index f1c71416e0..4ae87697dc 100644 --- a/tracing-subscriber/src/filter/env/mod.rs +++ b/tracing-subscriber/src/filter/env/mod.rs @@ -271,7 +271,7 @@ impl EnvFilter { let bold = Style::new().bold(); let mut warning = Color::Yellow.paint("warning"); warning.style_ref_mut().is_bold = true; - format!("{}{} {}", warning, bold.clone().paint(":"), bold.paint(msg)) + format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg)) }; eprintln!("{}", msg); }; @@ -308,7 +308,6 @@ impl EnvFilter { }; let level = directive .level - .clone() .into_level() .expect("=off would not have enabled any filters"); ctx(&format!( @@ -396,8 +395,8 @@ impl Layer for EnvFilter { return Some(LevelFilter::TRACE); } std::cmp::max( - self.statics.max_level.clone().into(), - self.dynamics.max_level.clone().into(), + self.statics.max_level.into(), + self.dynamics.max_level.into(), ) } diff --git a/tracing-subscriber/src/filter/level.rs b/tracing-subscriber/src/filter/level.rs index 0c981f3475..0fa601260a 100644 --- a/tracing-subscriber/src/filter/level.rs +++ b/tracing-subscriber/src/filter/level.rs @@ -22,6 +22,6 @@ impl crate::Layer for LevelFilter { } fn max_level_hint(&self) -> Option { - self.clone().into() + Some(*self) } } diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index eeddabd471..ab1806d4dd 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -1,7 +1,7 @@ use crate::{ field::RecordFields, fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter}, - layer::{self, Context, Scope}, + layer::{self, Context}, registry::{LookupSpan, SpanRef}, }; use format::{FmtSpan, TimingDisplay}; @@ -804,8 +804,10 @@ where F: FnMut(&SpanRef<'_, S>) -> 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(()) } @@ -864,7 +866,12 @@ where /// the current span. /// /// [stored data]: ../registry/struct.SpanRef.html - pub fn scope(&self) -> Scope<'_, S> + #[deprecated( + note = "wraps layer::Context::scope, which is deprecated", + since = "0.2.19" + )] + #[allow(deprecated)] + pub fn scope(&self) -> crate::layer::Scope<'_, S> where S: for<'lookup> LookupSpan<'lookup>, { diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index 5cc522f6a4..c2a0ced8f4 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -98,8 +98,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 3950847cb8..7a551bee46 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -7,10 +7,7 @@ use crate::{ registry::LookupSpan, }; -use std::{ - fmt::{self, Write}, - iter, -}; +use std::fmt::{self, Write}; use tracing_core::{ field::{self, Field, Visit}, span, Event, Level, Subscriber, @@ -861,9 +858,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 { seen = true; @@ -933,9 +928,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()))?; @@ -1510,27 +1503,27 @@ pub(super) mod test { #[test] fn fmt_span_combinations() { let f = FmtSpan::NONE; - assert_eq!(f.contains(FmtSpan::NEW), false); - assert_eq!(f.contains(FmtSpan::ENTER), false); - assert_eq!(f.contains(FmtSpan::EXIT), false); - assert_eq!(f.contains(FmtSpan::CLOSE), false); + assert!(!f.contains(FmtSpan::NEW)); + assert!(!f.contains(FmtSpan::ENTER)); + assert!(!f.contains(FmtSpan::EXIT)); + assert!(!f.contains(FmtSpan::CLOSE)); let f = FmtSpan::ACTIVE; - assert_eq!(f.contains(FmtSpan::NEW), false); - assert_eq!(f.contains(FmtSpan::ENTER), true); - assert_eq!(f.contains(FmtSpan::EXIT), true); - assert_eq!(f.contains(FmtSpan::CLOSE), false); + assert!(!f.contains(FmtSpan::NEW)); + assert!(f.contains(FmtSpan::ENTER)); + assert!(f.contains(FmtSpan::EXIT)); + assert!(!f.contains(FmtSpan::CLOSE)); let f = FmtSpan::FULL; - assert_eq!(f.contains(FmtSpan::NEW), true); - assert_eq!(f.contains(FmtSpan::ENTER), true); - assert_eq!(f.contains(FmtSpan::EXIT), true); - assert_eq!(f.contains(FmtSpan::CLOSE), true); + assert!(f.contains(FmtSpan::NEW)); + assert!(f.contains(FmtSpan::ENTER)); + assert!(f.contains(FmtSpan::EXIT)); + assert!(f.contains(FmtSpan::CLOSE)); let f = FmtSpan::NEW | FmtSpan::CLOSE; - assert_eq!(f.contains(FmtSpan::NEW), true); - assert_eq!(f.contains(FmtSpan::ENTER), false); - assert_eq!(f.contains(FmtSpan::EXIT), false); - assert_eq!(f.contains(FmtSpan::CLOSE), true); + assert!(f.contains(FmtSpan::NEW)); + assert!(!f.contains(FmtSpan::ENTER)); + assert!(!f.contains(FmtSpan::EXIT)); + assert!(f.contains(FmtSpan::CLOSE)); } } diff --git a/tracing-subscriber/src/fmt/format/pretty.rs b/tracing-subscriber/src/fmt/format/pretty.rs index d98cef813d..ab1180823f 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}, Event, Level, Subscriber, @@ -187,10 +184,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/layer.rs b/tracing-subscriber/src/layer.rs index e4322e24a4..03391e4fa6 100644 --- a/tracing-subscriber/src/layer.rs +++ b/tracing-subscriber/src/layer.rs @@ -580,9 +580,24 @@ pub struct Identity { /// [`Context::scope`]: struct.Context.html#method.scope #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -pub struct Scope<'a, L: LookupSpan<'a>>( - Option, std::iter::Once>>>, -); +#[deprecated(note = "renamed to crate::registry::ScopeFromRoot", since = "0.2.19")] +#[derive(Debug)] +pub struct Scope<'a, L>(std::iter::Flatten>>) +where + L: LookupSpan<'a>; + +#[cfg(feature = "registry")] +#[allow(deprecated)] +impl<'a, L> Iterator for Scope<'a, L> +where + L: LookupSpan<'a>, +{ + type Item = SpanRef<'a, L>; + + fn next(&mut self) -> Option { + self.0.next() + } +} // === impl Layered === @@ -1096,15 +1111,59 @@ where /// [stored data]: ../registry/struct.SpanRef.html #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] + #[deprecated( + note = "equivalent to `self.current_span().id().and_then(|id| self.span_scope(id).from_root())` but consider passing an explicit ID instead of relying on the contextual span", + since = "0.2.19" + )] + #[allow(deprecated)] pub fn scope(&self) -> Scope<'_, S> where S: 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) + Scope( + self.lookup_current() + .as_ref() + .map(registry::SpanRef::scope) + .map(registry::Scope::from_root) + .into_iter() + .flatten(), + ) + } + + /// Returns an iterator over the [stored data] for all the spans in the + /// current context, starting with the specified span and ending with the + /// root of the trace tree and ending with the current span. + /// + ///
+ ///
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
+ ///
+ ///
+ ///
+    /// 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 + S: for<'lookup> registry::LookupSpan<'lookup>, + { + Some(self.span(id)?.scope()) } } @@ -1133,27 +1192,6 @@ 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 super::*; diff --git a/tracing-subscriber/src/registry/mod.rs b/tracing-subscriber/src/registry/mod.rs index 327531c1c0..0761c528a8 100644 --- a/tracing-subscriber/src/registry/mod.rs +++ b/tracing-subscriber/src/registry/mod.rs @@ -62,6 +62,8 @@ //! [lookup]: ../layer/struct.Context.html#method.span //! [`LookupSpan`]: trait.LookupSpan.html //! [`SpanData`]: trait.SpanData.html +use std::fmt::Debug; + use tracing_core::{field::FieldSet, span::Id, Metadata}; /// A module containing a type map of span extensions. @@ -168,15 +170,112 @@ pub struct SpanRef<'a, R: LookupSpan<'a>> { data: R::Data, } +/// 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 Scope<'a, R> { + registry: &'a R, + next: Option, +} + +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. +/// +/// This is returned by the [`Scope::from_root`] method. +pub struct ScopeFromRoot<'a, R> +where + R: LookupSpan<'a>, +{ + #[cfg(feature = "smallvec")] + spans: std::iter::Rev>>, + #[cfg(not(feature = "smallvec"))] + 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 { .. }") + } +} + /// An iterator over the parents of a span. /// /// This is returned by the [`SpanRef::parents`] method. /// /// [`SpanRef::parents`]: struct.SpanRef.html#method.parents +#[deprecated(note = "replaced by `Scope`")] #[derive(Debug)] -pub struct Parents<'a, R> { - registry: &'a R, - next: Option, +pub struct Parents<'a, R>(Scope<'a, R>); + +#[allow(deprecated)] +impl<'a, R> Iterator for Parents<'a, R> +where + R: LookupSpan<'a>, +{ + type Item = SpanRef<'a, R>; + + fn next(&mut self) -> Option { + self.0.next() + } } /// An iterator over a span's parents, starting with the root of the trace @@ -185,11 +284,22 @@ pub struct Parents<'a, R> { /// For additonal details, see [`SpanRef::from_root`]. /// /// [`Span::from_root`]: struct.SpanRef.html#method.from_root -pub struct FromRoot<'a, R: LookupSpan<'a>> { - #[cfg(feature = "smallvec")] - inner: std::iter::Rev>>, - #[cfg(not(feature = "smallvec"))] - inner: std::iter::Rev>>, +#[deprecated(note = "replaced by `ScopeFromRoot`", since = "0.2.19")] +#[derive(Debug)] +pub struct FromRoot<'a, R>(ScopeFromRoot<'a, R>) +where + R: LookupSpan<'a>; + +#[allow(deprecated)] +impl<'a, R> Iterator for FromRoot<'a, R> +where + R: LookupSpan<'a>, +{ + type Item = SpanRef<'a, R>; + + fn next(&mut self) -> Option { + self.0.next() + } } #[cfg(feature = "smallvec")] @@ -238,17 +348,95 @@ where }) } + /// 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, then the span's immediate parent, + /// followed by that span's parent, and so on, until it reaches a root span. + /// + /// ```rust + /// use tracing::{span, 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_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::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || { + /// 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, 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_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::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || { + /// 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 an iterator over all parents of this span, starting with the /// immediate parent. /// /// 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. + #[deprecated( + note = "equivalent to `self.parent().into_iter().flat_map(SpanRef::scope)`, but consider whether excluding `self` is actually intended" + )] + #[allow(deprecated)] pub fn parents(&self) -> Parents<'a, R> { - Parents { + Parents(Scope { registry: self.registry, - next: self.parent().map(|parent| parent.id()), - } + next: self.parent_id().cloned(), + }) } /// Returns an iterator over all parents of this span, starting with the @@ -258,20 +446,15 @@ where /// next span, and then the next, until this span's immediate parent is /// returned. /// - /// **Note**: if the "smallvec" feature flag is not enabled, this may - /// allocate. + /// **Note**: this will allocate if there are many spans remaining, or if the + /// "smallvec" feature flag is not enabled. + #[deprecated( + note = "equivalent to `self.parent().into_iter().flat_map(|span| span.scope().from_root())`, but consider whether excluding `self` is actually intended", + since = "0.2.19" + )] + #[allow(deprecated)] 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 } + FromRoot(self.parents().0.from_root()) } /// Returns a reference to this span's `Extensions`. @@ -291,43 +474,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) - } -} +#[cfg(test)] +mod tests { + use crate::{ + layer::{Context, Layer}, + prelude::*, + registry::LookupSpan, + }; + use std::sync::{Arc, Mutex}; + use tracing::{span, Subscriber}; -// === impl FromRoot === + #[test] + fn spanref_scope_iteration_order() { + let last_entered_scope = Arc::new(Mutex::new(Vec::new())); -impl<'span, R> Iterator for FromRoot<'span, R> -where - R: LookupSpan<'span>, -{ - type Item = SpanRef<'span, R>; + #[derive(Default)] + struct PrintingLayer { + last_entered_scope: Arc>>, + } - #[inline] - fn next(&mut self) -> Option { - self.inner.next() - } + impl Layer for PrintingLayer + where + S: Subscriber + 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 size_hint(&self) -> (usize, Option) { - self.inner.size_hint() + let _guard = tracing::subscriber::set_default(crate::registry().with(PrintingLayer { + 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"] + ); } -} -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 { .. }") + #[test] + fn spanref_scope_fromroot_iteration_order() { + let last_entered_scope = Arc::new(Mutex::new(Vec::new())); + + #[derive(Default)] + struct PrintingLayer { + last_entered_scope: Arc>>, + } + + impl Layer for PrintingLayer + where + S: Subscriber + 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::subscriber::set_default(crate::registry().with(PrintingLayer { + 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"] + ); } }