tracing_subscriber/layer/
context.rs

1use tracing_core::{metadata::Metadata, span, subscriber::Subscriber, Event};
2
3use crate::registry::{self, LookupSpan, SpanRef};
4
5#[cfg(all(feature = "registry", feature = "std"))]
6use crate::{filter::FilterId, registry::Registry};
7/// Represents information about the current context provided to [`Layer`]s by the
8/// wrapped [`Subscriber`].
9///
10/// To access [stored data] keyed by a span ID, implementors of the `Layer`
11/// trait should ensure that the `Subscriber` type parameter is *also* bound by the
12/// [`LookupSpan`]:
13///
14/// ```rust
15/// use tracing::Subscriber;
16/// use tracing_subscriber::{Layer, registry::LookupSpan};
17///
18/// pub struct MyLayer;
19///
20/// impl<S> Layer<S> for MyLayer
21/// where
22///     S: Subscriber + for<'a> LookupSpan<'a>,
23/// {
24///     // ...
25/// }
26/// ```
27///
28/// [`Layer`]: super::Layer
29/// [`Subscriber`]: tracing_core::Subscriber
30/// [stored data]: crate::registry::SpanRef
31/// [`LookupSpan`]: crate::registry::LookupSpan
32#[derive(Debug)]
33pub struct Context<'a, S> {
34    subscriber: Option<&'a S>,
35    /// The bitmask of all [`Filtered`] layers that currently apply in this
36    /// context. If there is only a single [`Filtered`] wrapping the layer that
37    /// produced this context, then this is that filter's ID. Otherwise, if we
38    /// are in a nested tree with multiple filters, this is produced by
39    /// [`and`]-ing together the [`FilterId`]s of each of the filters that wrap
40    /// the current layer.
41    ///
42    /// [`Filtered`]: crate::filter::Filtered
43    /// [`FilterId`]: crate::filter::FilterId
44    /// [`and`]: crate::filter::FilterId::and
45    #[cfg(all(feature = "registry", feature = "std"))]
46    filter: FilterId,
47}
48
49// === impl Context ===
50
51impl<'a, S> Context<'a, S>
52where
53    S: Subscriber,
54{
55    pub(super) fn new(subscriber: &'a S) -> Self {
56        Self {
57            subscriber: Some(subscriber),
58
59            #[cfg(feature = "registry")]
60            filter: FilterId::none(),
61        }
62    }
63
64    /// Returns the wrapped subscriber's view of the current span.
65    #[inline]
66    pub fn current_span(&self) -> span::Current {
67        self.subscriber
68            .map(Subscriber::current_span)
69            // TODO: this would be more correct as "unknown", so perhaps
70            // `tracing-core` should make `Current::unknown()` public?
71            .unwrap_or_else(span::Current::none)
72    }
73
74    /// Returns whether the wrapped subscriber would enable the current span.
75    #[inline]
76    pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
77        self.subscriber
78            .map(|subscriber| subscriber.enabled(metadata))
79            // If this context is `None`, we are registering a callsite, so
80            // return `true` so that the layer does not incorrectly assume that
81            // the inner subscriber has disabled this metadata.
82            // TODO(eliza): would it be more correct for this to return an `Option`?
83            .unwrap_or(true)
84    }
85
86    /// Records the provided `event` with the wrapped subscriber.
87    ///
88    /// # Notes
89    ///
90    /// - The subscriber is free to expect that the event's callsite has been
91    ///   [registered][register], and may panic or fail to observe the event if this is
92    ///   not the case. The `tracing` crate's macros ensure that all events are
93    ///   registered, but if the event is constructed through other means, the
94    ///   user is responsible for ensuring that [`register_callsite`][register]
95    ///   has been called prior to calling this method.
96    /// - This does _not_ call [`enabled`] on the inner subscriber. If the
97    ///   caller wishes to apply the wrapped subscriber's filter before choosing
98    ///   whether to record the event, it may first call [`Context::enabled`] to
99    ///   check whether the event would be enabled. This allows `Layer`s to
100    ///   elide constructing the event if it would not be recorded.
101    ///
102    /// [register]: tracing_core::subscriber::Subscriber::register_callsite()
103    /// [`enabled`]: tracing_core::subscriber::Subscriber::enabled()
104    /// [`Context::enabled`]: Context::enabled()
105    #[inline]
106    pub fn event(&self, event: &Event<'_>) {
107        if let Some(subscriber) = self.subscriber {
108            subscriber.event(event);
109        }
110    }
111
112    /// Returns a [`SpanRef`] for the parent span of the given [`Event`], if
113    /// it has a parent.
114    ///
115    /// If the event has an explicitly overridden parent, this method returns
116    /// a reference to that span. If the event's parent is the current span,
117    /// this returns a reference to the current span, if there is one. If this
118    /// returns `None`, then either the event's parent was explicitly set to
119    /// `None`, or the event's parent was defined contextually, but no span
120    /// is currently entered.
121    ///
122    /// Compared to [`Context::current_span`] and [`Context::lookup_current`],
123    /// this respects overrides provided by the [`Event`].
124    ///
125    /// Compared to [`Event::parent`], this automatically falls back to the contextual
126    /// span, if required.
127    ///
128    /// ```rust
129    /// use tracing::{Event, Subscriber};
130    /// use tracing_subscriber::{
131    ///     layer::{Context, Layer},
132    ///     prelude::*,
133    ///     registry::LookupSpan,
134    /// };
135    ///
136    /// struct PrintingLayer;
137    /// impl<S> Layer<S> for PrintingLayer
138    /// where
139    ///     S: Subscriber + for<'lookup> LookupSpan<'lookup>,
140    /// {
141    ///     fn on_event(&self, event: &Event, ctx: Context<S>) {
142    ///         let span = ctx.event_span(event);
143    ///         println!("Event in span: {:?}", span.map(|s| s.name()));
144    ///     }
145    /// }
146    ///
147    /// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || {
148    ///     tracing::info!("no span");
149    ///     // Prints: Event in span: None
150    ///
151    ///     let span = tracing::info_span!("span");
152    ///     tracing::info!(parent: &span, "explicitly specified");
153    ///     // Prints: Event in span: Some("span")
154    ///
155    ///     let _guard = span.enter();
156    ///     tracing::info!("contextual span");
157    ///     // Prints: Event in span: Some("span")
158    /// });
159    /// ```
160    ///
161    /// <pre class="ignore" style="white-space:normal;font:inherit;">
162    ///     <strong>Note</strong>: This requires the wrapped subscriber to
163    ///     implement the <a href="../registry/trait.LookupSpan.html"><code>
164    ///     LookupSpan</code></a> trait. See the documentation on
165    ///     <a href="./struct.Context.html"><code>Context</code>'s
166    ///     declaration</a> for details.
167    /// </pre>
168    #[inline]
169    pub fn event_span(&self, event: &Event<'_>) -> Option<SpanRef<'_, S>>
170    where
171        S: for<'lookup> LookupSpan<'lookup>,
172    {
173        if event.is_root() {
174            None
175        } else if event.is_contextual() {
176            self.lookup_current()
177        } else {
178            // TODO(eliza): this should handle parent IDs
179            event.parent().and_then(|id| self.span(id))
180        }
181    }
182
183    /// Returns metadata for the span with the given `id`, if it exists.
184    ///
185    /// If this returns `None`, then no span exists for that ID (either it has
186    /// closed or the ID is invalid).
187    #[inline]
188    pub fn metadata(&self, id: &span::Id) -> Option<&'static Metadata<'static>>
189    where
190        S: for<'lookup> LookupSpan<'lookup>,
191    {
192        let span = self.span(id)?;
193        Some(span.metadata())
194    }
195
196    /// Returns [stored data] for the span with the given `id`, if it exists.
197    ///
198    /// If this returns `None`, then no span exists for that ID (either it has
199    /// closed or the ID is invalid).
200    ///
201    /// <pre class="ignore" style="white-space:normal;font:inherit;">
202    ///     <strong>Note</strong>: This requires the wrapped subscriber to
203    ///     implement the <a href="../registry/trait.LookupSpan.html"><code>
204    ///     LookupSpan</code></a> trait. See the documentation on
205    ///     <a href="./struct.Context.html"><code>Context</code>'s
206    ///     declaration</a> for details.
207    /// </pre>
208    ///
209    /// [stored data]: crate::registry::SpanRef
210    #[inline]
211    pub fn span(&self, id: &span::Id) -> Option<registry::SpanRef<'_, S>>
212    where
213        S: for<'lookup> LookupSpan<'lookup>,
214    {
215        let span = self.subscriber.as_ref()?.span(id)?;
216
217        #[cfg(all(feature = "registry", feature = "std"))]
218        return span.try_with_filter(self.filter);
219
220        #[cfg(not(feature = "registry"))]
221        Some(span)
222    }
223
224    /// Returns `true` if an active span exists for the given `Id`.
225    ///
226    /// <pre class="ignore" style="white-space:normal;font:inherit;">
227    ///     <strong>Note</strong>: This requires the wrapped subscriber to
228    ///     implement the <a href="../registry/trait.LookupSpan.html"><code>
229    ///     LookupSpan</code></a> trait. See the documentation on
230    ///     <a href="./struct.Context.html"><code>Context</code>'s
231    ///     declaration</a> for details.
232    /// </pre>
233    #[inline]
234    pub fn exists(&self, id: &span::Id) -> bool
235    where
236        S: for<'lookup> LookupSpan<'lookup>,
237    {
238        self.subscriber.as_ref().and_then(|s| s.span(id)).is_some()
239    }
240
241    /// Returns [stored data] for the span that the wrapped subscriber considers
242    /// to be the current.
243    ///
244    /// If this returns `None`, then we are not currently within a span.
245    ///
246    /// <pre class="ignore" style="white-space:normal;font:inherit;">
247    ///     <strong>Note</strong>: This requires the wrapped subscriber to
248    ///     implement the <a href="../registry/trait.LookupSpan.html"><code>
249    ///     LookupSpan</code></a> trait. See the documentation on
250    ///     <a href="./struct.Context.html"><code>Context</code>'s
251    ///     declaration</a> for details.
252    /// </pre>
253    ///
254    /// [stored data]: crate::registry::SpanRef
255    #[inline]
256    pub fn lookup_current(&self) -> Option<registry::SpanRef<'_, S>>
257    where
258        S: for<'lookup> LookupSpan<'lookup>,
259    {
260        let subscriber = *self.subscriber.as_ref()?;
261        let current = subscriber.current_span();
262        let id = current.id()?;
263        let span = subscriber.span(id);
264        debug_assert!(
265            span.is_some(),
266            "the subscriber should have data for the current span ({:?})!",
267            id,
268        );
269
270        // If we found a span, and our per-layer filter enables it, return that
271        // span!
272        #[cfg(all(feature = "registry", feature = "std"))]
273        {
274            if let Some(span) = span?.try_with_filter(self.filter) {
275                Some(span)
276            } else {
277                // Otherwise, the span at the *top* of the stack is disabled by
278                // per-layer filtering, but there may be additional spans in the stack.
279                //
280                // Currently, `LookupSpan` doesn't have a nice way of exposing access to
281                // the whole span stack. However, if we can downcast the innermost
282                // subscriber to a a `Registry`, we can iterate over its current span
283                // stack.
284                //
285                // TODO(eliza): when https://github.com/tokio-rs/tracing/issues/1459 is
286                // implemented, change this to use that instead...
287                self.lookup_current_filtered(subscriber)
288            }
289        }
290
291        #[cfg(not(feature = "registry"))]
292        span
293    }
294
295    /// Slow path for when the current span is disabled by PLF and we have a
296    /// registry.
297    // This is called by `lookup_current` in the case that per-layer filtering
298    // is in use. `lookup_current` is allowed to be inlined, but this method is
299    // factored out to prevent the loop and (potentially-recursive) subscriber
300    // downcasting from being inlined if `lookup_current` is inlined.
301    #[inline(never)]
302    #[cfg(all(feature = "registry", feature = "std"))]
303    fn lookup_current_filtered<'lookup>(
304        &self,
305        subscriber: &'lookup S,
306    ) -> Option<registry::SpanRef<'lookup, S>>
307    where
308        S: LookupSpan<'lookup>,
309    {
310        let registry = (subscriber as &dyn Subscriber).downcast_ref::<Registry>()?;
311        registry
312            .span_stack()
313            .iter()
314            .find_map(|id| subscriber.span(id)?.try_with_filter(self.filter))
315    }
316
317    /// Returns an iterator over the [stored data] for all the spans in the
318    /// current context, starting with the specified span and ending with the
319    /// root of the trace tree.
320    ///
321    /// <pre class="ignore" style="white-space:normal;font:inherit;">
322    /// <strong>Note</strong>: This returns the spans in reverse order (from leaf to root). Use
323    /// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
324    /// in case root-to-leaf ordering is desired.
325    /// </pre>
326    ///
327    /// <pre class="ignore" style="white-space:normal;font:inherit;">
328    ///     <strong>Note</strong>: This requires the wrapped subscriber to
329    ///     implement the <a href="../registry/trait.LookupSpan.html"><code>
330    ///     LookupSpan</code></a> trait. See the documentation on
331    ///     <a href="./struct.Context.html"><code>Context</code>'s
332    ///     declaration</a> for details.
333    /// </pre>
334    ///
335    /// [stored data]: crate::registry::SpanRef
336    pub fn span_scope(&self, id: &span::Id) -> Option<registry::Scope<'_, S>>
337    where
338        S: for<'lookup> LookupSpan<'lookup>,
339    {
340        Some(self.span(id)?.scope())
341    }
342
343    /// Returns an iterator over the [stored data] for all the spans in the
344    /// current context, starting with the parent span of the specified event,
345    /// and ending with the root of the trace tree and ending with the current span.
346    ///
347    /// <pre class="ignore" style="white-space:normal;font:inherit;">
348    /// <strong>Note</strong>: Compared to <a href="#method.scope"><code>scope</code></a> this
349    /// returns the spans in reverse order (from leaf to root). Use
350    /// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
351    /// in case root-to-leaf ordering is desired.
352    /// </pre>
353    ///
354    /// <pre class="ignore" style="white-space:normal;font:inherit;">
355    ///     <strong>Note</strong>: This requires the wrapped subscriber to
356    ///     implement the <a href="../registry/trait.LookupSpan.html"><code>
357    ///     LookupSpan</code></a> trait. See the documentation on
358    ///     <a href="./struct.Context.html"><code>Context</code>'s
359    ///     declaration</a> for details.
360    /// </pre>
361    ///
362    /// [stored data]: crate::registry::SpanRef
363    pub fn event_scope(&self, event: &Event<'_>) -> Option<registry::Scope<'_, S>>
364    where
365        S: for<'lookup> LookupSpan<'lookup>,
366    {
367        Some(self.event_span(event)?.scope())
368    }
369
370    #[cfg(all(feature = "registry", feature = "std"))]
371    pub(crate) fn with_filter(self, filter: FilterId) -> Self {
372        // If we already have our own `FilterId`, combine it with the provided
373        // one. That way, the new `FilterId` will consider a span to be disabled
374        // if it was disabled by the given `FilterId` *or* any `FilterId`s for
375        // layers "above" us in the stack.
376        //
377        // See the doc comment for `FilterId::and` for details.
378        let filter = self.filter.and(filter);
379        Self { filter, ..self }
380    }
381
382    #[cfg(all(feature = "registry", feature = "std"))]
383    pub(crate) fn is_enabled_for(&self, span: &span::Id, filter: FilterId) -> bool
384    where
385        S: for<'lookup> LookupSpan<'lookup>,
386    {
387        self.is_enabled_inner(span, filter).unwrap_or(false)
388    }
389
390    #[cfg(all(feature = "registry", feature = "std"))]
391    pub(crate) fn if_enabled_for(self, span: &span::Id, filter: FilterId) -> Option<Self>
392    where
393        S: for<'lookup> LookupSpan<'lookup>,
394    {
395        if self.is_enabled_inner(span, filter)? {
396            Some(self.with_filter(filter))
397        } else {
398            None
399        }
400    }
401
402    #[cfg(all(feature = "registry", feature = "std"))]
403    fn is_enabled_inner(&self, span: &span::Id, filter: FilterId) -> Option<bool>
404    where
405        S: for<'lookup> LookupSpan<'lookup>,
406    {
407        Some(self.span(span)?.is_enabled_for(filter))
408    }
409}
410
411impl<S> Context<'_, S> {
412    pub(crate) fn none() -> Self {
413        Self {
414            subscriber: None,
415
416            #[cfg(feature = "registry")]
417            filter: FilterId::none(),
418        }
419    }
420}
421
422impl<S> Clone for Context<'_, S> {
423    #[inline]
424    fn clone(&self) -> Self {
425        let subscriber = self.subscriber.as_ref().copied();
426        Context {
427            subscriber,
428
429            #[cfg(all(feature = "registry", feature = "std"))]
430            filter: self.filter,
431        }
432    }
433}