From 89e7f414f27aa8574bec9a1a6d0af1954841deab Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 19 Nov 2021 15:07:54 -0800 Subject: [PATCH] subscriber: expose access to event scope in `FmtContext` (#1728) ## Motivation Currently, `tracing_subscriber::fmt`'s `FmtContext` type is missing the `span_scope`, `event_span`, and `event_scope` methods that `Context` provides. This is a shame; these will make it much easier for users implementing formatters to iterate over the scope of the event being formatted correctly. We should expose those methods. ## Solution This branch adds new methods to `FmtContext`, most of which forward to the similarly-named `Context` methods. However, because a `FmtContext` is only constructed when formatting an event, we can also make the event scope methods a little more ergonomic by storing a ref to the span being formatted and automatically passing it to `Context::event_scope` and `Context::event_span`. This means the `FmtContext` can just always return the current event's scope without the user having to pass it in. --- tracing-subscriber/src/fmt/fmt_layer.rs | 96 +++++++++++++++++++++--- tracing-subscriber/src/fmt/format/mod.rs | 43 ++++++----- 2 files changed, 108 insertions(+), 31 deletions(-) diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 0eddbfb3c6..0e0d5e0eb0 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -2,7 +2,7 @@ use crate::{ field::RecordFields, fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter}, layer::{self, Context}, - registry::{LookupSpan, SpanRef}, + registry::{self, LookupSpan, SpanRef}, }; use format::{FmtSpan, TimingDisplay}; use std::{any::TypeId, cell::RefCell, fmt, io, marker::PhantomData, ops::Deref, time::Instant}; @@ -92,7 +92,7 @@ where /// /// The event formatter may be any type implementing the [`FormatEvent`] /// trait, which is implemented for all functions taking a [`FmtContext`], a - /// `&mut dyn Write`, and an [`Event`]. + /// [`Writer`], and an [`Event`]. /// /// # Examples /// @@ -106,9 +106,9 @@ where /// # use tracing_subscriber::Layer as _; /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// ``` - /// [`FormatEvent`]: ./format/trait.FormatEvent.html - /// [`FmtContext`]: ./struct.FmtContext.html - /// [`Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html + /// [`FormatEvent`]: format::FormatEvent + /// [`Event`]: tracing::Event + /// [`Writer`]: crate::format::Writer pub fn event_format(self, e: E2) -> Layer where E2: FormatEvent + 'static, @@ -480,10 +480,11 @@ where W: for<'writer> MakeWriter<'writer> + 'static, { #[inline] - fn make_ctx<'a>(&'a self, ctx: Context<'a, S>) -> FmtContext<'a, S, N> { + fn make_ctx<'a>(&'a self, ctx: Context<'a, S>, event: &'a Event<'a>) -> FmtContext<'a, S, N> { FmtContext { ctx, fmt_fields: &self.fmt_fields, + event, } } } @@ -720,7 +721,7 @@ where } }; - let ctx = self.make_ctx(ctx); + let ctx = self.make_ctx(ctx, event); if self .fmt_event .format_event( @@ -757,6 +758,7 @@ where pub struct FmtContext<'a, S, N> { pub(crate) ctx: Context<'a, S>, pub(crate) fmt_fields: &'a N, + pub(crate) event: &'a Event<'a>, } impl<'a, S, N> fmt::Debug for FmtContext<'a, S, N> { @@ -794,8 +796,8 @@ where F: FnMut(&SpanRef<'_, S>) -> Result<(), E>, { // visit all the current spans - if let Some(leaf) = self.ctx.lookup_current() { - for span in leaf.scope().from_root() { + if let Some(scope) = self.event_scope() { + for span in scope.from_root() { f(&span)?; } } @@ -856,6 +858,82 @@ where self.ctx.current_span() } + /// Returns [stored data] for the parent span of the event currently being + /// formatted. + /// + /// If the event has a contextual parent, this will return the current span. If + /// the event has an explicit parent span, this will return that span. If + /// the event does not have a parent span, this will return `None`. + /// + /// [stored data]: SpanRef + pub fn parent_span(&self) -> Option> { + self.ctx.event_span(self.event) + } + + /// 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. + /// + /// This is equivalent to the [`Context::span_scope`] method. + /// + ///
+ ///
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.
+    /// 
+ /// + /// [stored data]: crate::registry::SpanRef + pub fn span_scope(&self, id: &Id) -> Option> + where + S: for<'lookup> LookupSpan<'lookup>, + { + self.ctx.span_scope(id) + } + + /// Returns an iterator over the [stored data] for all the spans in the + /// event's span context, starting with its parent span and ending with the + /// root of the trace tree. + /// + /// This is equivalent to calling the [`Context::event_scope`] method and + /// passing the event currently being formatted. + /// + ///
+ ///
+    /// 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]: crate::registry::SpanRef + pub fn event_scope(&self) -> Option> + where + S: for<'lookup> registry::LookupSpan<'lookup>, + { + self.ctx.event_scope(self.event) + } + /// Returns the [field formatter] configured by the subscriber invoking /// `format_event`. /// diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index edae251025..2f4ae6f6d6 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -110,27 +110,27 @@ pub use pretty::*; /// write!(&mut writer, "{} {}: ", metadata.level(), metadata.target())?; /// /// // Format all the spans in the event's span context. -/// ctx.visit_spans(|span| { -/// write!(writer, "{}", span.name())?; +/// if let Some(scope) = ctx.event_scope() { +/// for span in scope.from_root() { +/// write!(writer, "{}", span.name())?; /// -/// // `FormattedFields` is a formatted representation of the span's -/// // fields, which is stored in its extensions by the `fmt` layer's -/// // `new_span` method. The fields will have been formatted -/// // by the same field formatter that's provided to the event -/// // formatter in the `FmtContext`. -/// let ext = span.extensions(); -/// let fields = &ext -/// .get::>() -/// .expect("will never be `None`"); +/// // `FormattedFields` is a formatted representation of the span's +/// // fields, which is stored in its extensions by the `fmt` layer's +/// // `new_span` method. The fields will have been formatted +/// // by the same field formatter that's provided to the event +/// // formatter in the `FmtContext`. +/// let ext = span.extensions(); +/// let fields = &ext +/// .get::>() +/// .expect("will never be `None`"); /// -/// // Skip formatting the fields if the span had no fields. -/// if !fields.is_empty() { -/// write!(writer, "{{{}}}", fields)?; +/// // Skip formatting the fields if the span had no fields. +/// if !fields.is_empty() { +/// write!(writer, "{{{}}}", fields)?; +/// } +/// write!(writer, ": ")?; /// } -/// write!(writer, ": ")?; -/// -/// Ok(()) -/// })?; +/// } /// /// // Write fields on the event /// ctx.field_format().format_fields(writer.by_ref(), event)?; @@ -140,7 +140,7 @@ pub use pretty::*; /// } /// /// let _subscriber = tracing_subscriber::fmt() -/// .event_format(MyFormatter) +/// .event_format(MyFormatter) /// .init(); /// /// let _span = tracing::info_span!("my_span", answer = 42).entered(); @@ -808,7 +808,7 @@ where let dimmed = writer.dimmed(); - if let Some(scope) = ctx.ctx.event_scope(event) { + if let Some(scope) = ctx.event_scope() { let bold = writer.bold(); let mut seen = false; @@ -929,8 +929,7 @@ where let dimmed = writer.dimmed(); for span in ctx - .ctx - .event_scope(event) + .event_scope() .into_iter() .map(crate::registry::Scope::from_root) .flatten()