Skip to content

Commit

Permalink
subscriber: expose access to event scope in FmtContext (#1728)
Browse files Browse the repository at this point in the history
## 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.
  • Loading branch information
hawkw committed Nov 19, 2021
1 parent 772290d commit 5a076ed
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 28 deletions.
91 changes: 85 additions & 6 deletions 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};
Expand Down Expand Up @@ -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
///
Expand All @@ -106,6 +106,7 @@ where
/// ```
/// [`FormatEvent`]: format::FormatEvent
/// [`Event`]: tracing::Event
/// [`Writer`]: crate::format::Writer
pub fn event_format<E2>(self, e: E2) -> Subscriber<C, N, E2, W>
where
E2: FormatEvent<C, N> + 'static,
Expand Down Expand Up @@ -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,
}
}
}
Expand Down Expand Up @@ -719,7 +721,7 @@ where
}
};

let ctx = self.make_ctx(ctx);
let ctx = self.make_ctx(ctx, event);
if self
.fmt_event
.format_event(
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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)?;
}
}
Expand Down Expand Up @@ -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<SpanRef<'_, C>> {
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.
///
/// <div class="information">
/// <div class="tooltip ignore" style="">ⓘ<span class="tooltiptext">Note</span></div>
/// </div>
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: Compared to <a href="#method.scope"><code>scope</code></a> this
/// returns the spans in reverse order (from leaf to root). Use
/// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
/// in case root-to-leaf ordering is desired.
/// </pre></div>
///
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to implement the
/// <a href="../registry/trait.LookupSpan.html"><code>LookupSpan</code></a> trait.
/// See the documentation on <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre></div>
///
/// [stored data]: crate::registry::SpanRef
pub fn span_scope(&self, id: &Id) -> Option<registry::Scope<'_, C>>
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.
///
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: Compared to <a href="#method.scope"><code>scope</code></a> this
/// returns the spans in reverse order (from leaf to root). Use
/// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
/// in case root-to-leaf ordering is desired.
/// </pre></div>
///
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to implement the
/// <a href="../registry/trait.LookupSpan.html"><code>LookupSpan</code></a> trait.
/// See the documentation on <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre></div>
///
/// [stored data]: crate::registry::SpanRef
pub fn event_scope(&self) -> Option<registry::Scope<'_, C>>
where
C: for<'lookup> registry::LookupSpan<'lookup>,
{
self.ctx.event_scope(self.event)
}

/// Returns the [field formatter] configured by the subscriber invoking
/// `format_event`.
///
Expand Down
43 changes: 21 additions & 22 deletions tracing-subscriber/src/fmt/format/mod.rs
Expand Up @@ -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::<FormattedFields<N>>()
/// .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::<FormattedFields<N>>()
/// .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)?;
Expand All @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 5a076ed

Please sign in to comment.