Skip to content

Commit 89e7f41

Browse files
committed
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.
1 parent 51f498a commit 89e7f41

File tree

2 files changed

+108
-31
lines changed

2 files changed

+108
-31
lines changed

tracing-subscriber/src/fmt/fmt_layer.rs

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
field::RecordFields,
33
fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter},
44
layer::{self, Context},
5-
registry::{LookupSpan, SpanRef},
5+
registry::{self, LookupSpan, SpanRef},
66
};
77
use format::{FmtSpan, TimingDisplay};
88
use std::{any::TypeId, cell::RefCell, fmt, io, marker::PhantomData, ops::Deref, time::Instant};
@@ -92,7 +92,7 @@ where
9292
///
9393
/// The event formatter may be any type implementing the [`FormatEvent`]
9494
/// trait, which is implemented for all functions taking a [`FmtContext`], a
95-
/// `&mut dyn Write`, and an [`Event`].
95+
/// [`Writer`], and an [`Event`].
9696
///
9797
/// # Examples
9898
///
@@ -106,9 +106,9 @@ where
106106
/// # use tracing_subscriber::Layer as _;
107107
/// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
108108
/// ```
109-
/// [`FormatEvent`]: ./format/trait.FormatEvent.html
110-
/// [`FmtContext`]: ./struct.FmtContext.html
111-
/// [`Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html
109+
/// [`FormatEvent`]: format::FormatEvent
110+
/// [`Event`]: tracing::Event
111+
/// [`Writer`]: crate::format::Writer
112112
pub fn event_format<E2>(self, e: E2) -> Layer<S, N, E2, W>
113113
where
114114
E2: FormatEvent<S, N> + 'static,
@@ -480,10 +480,11 @@ where
480480
W: for<'writer> MakeWriter<'writer> + 'static,
481481
{
482482
#[inline]
483-
fn make_ctx<'a>(&'a self, ctx: Context<'a, S>) -> FmtContext<'a, S, N> {
483+
fn make_ctx<'a>(&'a self, ctx: Context<'a, S>, event: &'a Event<'a>) -> FmtContext<'a, S, N> {
484484
FmtContext {
485485
ctx,
486486
fmt_fields: &self.fmt_fields,
487+
event,
487488
}
488489
}
489490
}
@@ -720,7 +721,7 @@ where
720721
}
721722
};
722723

723-
let ctx = self.make_ctx(ctx);
724+
let ctx = self.make_ctx(ctx, event);
724725
if self
725726
.fmt_event
726727
.format_event(
@@ -757,6 +758,7 @@ where
757758
pub struct FmtContext<'a, S, N> {
758759
pub(crate) ctx: Context<'a, S>,
759760
pub(crate) fmt_fields: &'a N,
761+
pub(crate) event: &'a Event<'a>,
760762
}
761763

762764
impl<'a, S, N> fmt::Debug for FmtContext<'a, S, N> {
@@ -794,8 +796,8 @@ where
794796
F: FnMut(&SpanRef<'_, S>) -> Result<(), E>,
795797
{
796798
// visit all the current spans
797-
if let Some(leaf) = self.ctx.lookup_current() {
798-
for span in leaf.scope().from_root() {
799+
if let Some(scope) = self.event_scope() {
800+
for span in scope.from_root() {
799801
f(&span)?;
800802
}
801803
}
@@ -856,6 +858,82 @@ where
856858
self.ctx.current_span()
857859
}
858860

861+
/// Returns [stored data] for the parent span of the event currently being
862+
/// formatted.
863+
///
864+
/// If the event has a contextual parent, this will return the current span. If
865+
/// the event has an explicit parent span, this will return that span. If
866+
/// the event does not have a parent span, this will return `None`.
867+
///
868+
/// [stored data]: SpanRef
869+
pub fn parent_span(&self) -> Option<SpanRef<'_, S>> {
870+
self.ctx.event_span(self.event)
871+
}
872+
873+
/// Returns an iterator over the [stored data] for all the spans in the
874+
/// current context, starting with the specified span and ending with the
875+
/// root of the trace tree and ending with the current span.
876+
///
877+
/// This is equivalent to the [`Context::span_scope`] method.
878+
///
879+
/// <div class="information">
880+
/// <div class="tooltip ignore" style="">ⓘ<span class="tooltiptext">Note</span></div>
881+
/// </div>
882+
/// <div class="example-wrap" style="display:inline-block">
883+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
884+
/// <strong>Note</strong>: Compared to <a href="#method.scope"><code>scope</code></a> this
885+
/// returns the spans in reverse order (from leaf to root). Use
886+
/// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
887+
/// in case root-to-leaf ordering is desired.
888+
/// </pre></div>
889+
///
890+
/// <div class="example-wrap" style="display:inline-block">
891+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
892+
/// <strong>Note</strong>: This requires the wrapped subscriber to implement the
893+
/// <a href="../registry/trait.LookupSpan.html"><code>LookupSpan</code></a> trait.
894+
/// See the documentation on <a href="./struct.Context.html"><code>Context</code>'s
895+
/// declaration</a> for details.
896+
/// </pre></div>
897+
///
898+
/// [stored data]: crate::registry::SpanRef
899+
pub fn span_scope(&self, id: &Id) -> Option<registry::Scope<'_, S>>
900+
where
901+
S: for<'lookup> LookupSpan<'lookup>,
902+
{
903+
self.ctx.span_scope(id)
904+
}
905+
906+
/// Returns an iterator over the [stored data] for all the spans in the
907+
/// event's span context, starting with its parent span and ending with the
908+
/// root of the trace tree.
909+
///
910+
/// This is equivalent to calling the [`Context::event_scope`] method and
911+
/// passing the event currently being formatted.
912+
///
913+
/// <div class="example-wrap" style="display:inline-block">
914+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
915+
/// <strong>Note</strong>: Compared to <a href="#method.scope"><code>scope</code></a> this
916+
/// returns the spans in reverse order (from leaf to root). Use
917+
/// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
918+
/// in case root-to-leaf ordering is desired.
919+
/// </pre></div>
920+
///
921+
/// <div class="example-wrap" style="display:inline-block">
922+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
923+
/// <strong>Note</strong>: This requires the wrapped subscriber to implement the
924+
/// <a href="../registry/trait.LookupSpan.html"><code>LookupSpan</code></a> trait.
925+
/// See the documentation on <a href="./struct.Context.html"><code>Context</code>'s
926+
/// declaration</a> for details.
927+
/// </pre></div>
928+
///
929+
/// [stored data]: crate::registry::SpanRef
930+
pub fn event_scope(&self) -> Option<registry::Scope<'_, S>>
931+
where
932+
S: for<'lookup> registry::LookupSpan<'lookup>,
933+
{
934+
self.ctx.event_scope(self.event)
935+
}
936+
859937
/// Returns the [field formatter] configured by the subscriber invoking
860938
/// `format_event`.
861939
///

tracing-subscriber/src/fmt/format/mod.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,27 @@ pub use pretty::*;
110110
/// write!(&mut writer, "{} {}: ", metadata.level(), metadata.target())?;
111111
///
112112
/// // Format all the spans in the event's span context.
113-
/// ctx.visit_spans(|span| {
114-
/// write!(writer, "{}", span.name())?;
113+
/// if let Some(scope) = ctx.event_scope() {
114+
/// for span in scope.from_root() {
115+
/// write!(writer, "{}", span.name())?;
115116
///
116-
/// // `FormattedFields` is a formatted representation of the span's
117-
/// // fields, which is stored in its extensions by the `fmt` layer's
118-
/// // `new_span` method. The fields will have been formatted
119-
/// // by the same field formatter that's provided to the event
120-
/// // formatter in the `FmtContext`.
121-
/// let ext = span.extensions();
122-
/// let fields = &ext
123-
/// .get::<FormattedFields<N>>()
124-
/// .expect("will never be `None`");
117+
/// // `FormattedFields` is a formatted representation of the span's
118+
/// // fields, which is stored in its extensions by the `fmt` layer's
119+
/// // `new_span` method. The fields will have been formatted
120+
/// // by the same field formatter that's provided to the event
121+
/// // formatter in the `FmtContext`.
122+
/// let ext = span.extensions();
123+
/// let fields = &ext
124+
/// .get::<FormattedFields<N>>()
125+
/// .expect("will never be `None`");
125126
///
126-
/// // Skip formatting the fields if the span had no fields.
127-
/// if !fields.is_empty() {
128-
/// write!(writer, "{{{}}}", fields)?;
127+
/// // Skip formatting the fields if the span had no fields.
128+
/// if !fields.is_empty() {
129+
/// write!(writer, "{{{}}}", fields)?;
130+
/// }
131+
/// write!(writer, ": ")?;
129132
/// }
130-
/// write!(writer, ": ")?;
131-
///
132-
/// Ok(())
133-
/// })?;
133+
/// }
134134
///
135135
/// // Write fields on the event
136136
/// ctx.field_format().format_fields(writer.by_ref(), event)?;
@@ -140,7 +140,7 @@ pub use pretty::*;
140140
/// }
141141
///
142142
/// let _subscriber = tracing_subscriber::fmt()
143-
/// .event_format(MyFormatter)
143+
/// .event_format(MyFormatter)
144144
/// .init();
145145
///
146146
/// let _span = tracing::info_span!("my_span", answer = 42).entered();
@@ -808,7 +808,7 @@ where
808808

809809
let dimmed = writer.dimmed();
810810

811-
if let Some(scope) = ctx.ctx.event_scope(event) {
811+
if let Some(scope) = ctx.event_scope() {
812812
let bold = writer.bold();
813813

814814
let mut seen = false;
@@ -929,8 +929,7 @@ where
929929

930930
let dimmed = writer.dimmed();
931931
for span in ctx
932-
.ctx
933-
.event_scope(event)
932+
.event_scope()
934933
.into_iter()
935934
.map(crate::registry::Scope::from_root)
936935
.flatten()

0 commit comments

Comments
 (0)