From 765182d9521637ac0aac8925e77a3e0ee0662fdd Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 19 Nov 2021 14:30:34 -0800 Subject: [PATCH] subscriber: expose access to event scope in `FmtContext` 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. 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_subscriber.rs | 91 ++++++++++++++++++-- tracing-subscriber/src/fmt/format/mod.rs | 43 +++++---- 2 files changed, 106 insertions(+), 28 deletions(-) diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs index 304417f949..2be78c923e 100644 --- a/tracing-subscriber/src/fmt/fmt_subscriber.rs +++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs @@ -1,7 +1,7 @@ use crate::{ field::RecordFields, fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter}, - registry::{LookupSpan, SpanRef}, + registry::{self, LookupSpan, SpanRef}, subscribe::{self, Context}, }; use format::{FmtSpan, TimingDisplay}; @@ -90,7 +90,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,6 +106,7 @@ where /// ``` /// [`FormatEvent`]: format::FormatEvent /// [`Event`]: tracing::Event + /// [`Writer`]: crate::format::Writer pub fn event_format(self, e: E2) -> Subscriber where E2: FormatEvent + 'static, @@ -479,10 +480,11 @@ where W: for<'writer> MakeWriter<'writer> + 'static, { #[inline] - fn make_ctx<'a>(&'a self, ctx: Context<'a, C>) -> FmtContext<'a, C, N> { + fn make_ctx<'a>(&'a self, ctx: Context<'a, C>, event: &'a Event<'a>) -> FmtContext<'a, C, N> { FmtContext { ctx, fmt_fields: &self.fmt_fields, + event, } } } @@ -719,7 +721,7 @@ where } }; - let ctx = self.make_ctx(ctx); + let ctx = self.make_ctx(ctx, event); if self .fmt_event .format_event( @@ -756,6 +758,7 @@ where pub struct FmtContext<'a, C, N> { pub(crate) ctx: Context<'a, C>, pub(crate) fmt_fields: &'a N, + pub(crate) event: &'a Event<'a>, } impl<'a, C, N> fmt::Debug for FmtContext<'a, C, N> { @@ -793,8 +796,8 @@ where F: FnMut(&SpanRef<'_, C>) -> 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)?; } } @@ -855,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 + C: 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 + C: 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 6dd4222ccc..6aa83ed0b3 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -112,27 +112,27 @@ use fmt::{Debug, Display}; /// 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)?; @@ -142,7 +142,7 @@ use fmt::{Debug, Display}; /// } /// /// let _subscriber = tracing_subscriber::fmt() -/// .event_format(MyFormatter) +/// .event_format(MyFormatter) /// .init(); /// /// let _span = tracing::info_span!("my_span", answer = 42).entered(); @@ -830,7 +830,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; @@ -920,8 +920,7 @@ where let dimmed = writer.dimmed(); for span in ctx - .ctx - .event_scope(event) + .event_scope() .into_iter() .map(Scope::from_root) .flatten()