use tracing_core::{metadata::Metadata, span, subscriber::Subscriber, Event}; use crate::registry::{self, LookupSpan, SpanRef}; #[cfg(all(feature = "registry", feature = "std"))] use crate::{filter::FilterId, registry::Registry}; /// Represents information about the current context provided to [`Layer`]s by the /// wrapped [`Subscriber`]. /// /// To access [stored data] keyed by a span ID, implementors of the `Layer` /// trait should ensure that the `Subscriber` type parameter is *also* bound by the /// [`LookupSpan`]: /// /// ```rust /// use tracing::Subscriber; /// use tracing_subscriber::{Layer, registry::LookupSpan}; /// /// pub struct MyLayer; /// /// impl Layer for MyLayer /// where /// S: Subscriber + for<'a> LookupSpan<'a>, /// { /// // ... /// } /// ``` /// /// [`Layer`]: super::Layer /// [`Subscriber`]: tracing_core::Subscriber /// [stored data]: crate::registry::SpanRef /// [`LookupSpan`]: crate::registry::LookupSpan #[derive(Debug)] pub struct Context<'a, S> { subscriber: Option<&'a S>, /// The bitmask of all [`Filtered`] layers that currently apply in this /// context. If there is only a single [`Filtered`] wrapping the layer that /// produced this context, then this is that filter's ID. Otherwise, if we /// are in a nested tree with multiple filters, this is produced by /// [`and`]-ing together the [`FilterId`]s of each of the filters that wrap /// the current layer. /// /// [`Filtered`]: crate::filter::Filtered /// [`FilterId`]: crate::filter::FilterId /// [`and`]: crate::filter::FilterId::and #[cfg(all(feature = "registry", feature = "std"))] filter: FilterId, } // === impl Context === impl<'a, S> Context<'a, S> where S: Subscriber, { pub(super) fn new(subscriber: &'a S) -> Self { Self { subscriber: Some(subscriber), #[cfg(feature = "registry")] filter: FilterId::none(), } } /// Returns the wrapped subscriber's view of the current span. #[inline] pub fn current_span(&self) -> span::Current { self.subscriber .map(Subscriber::current_span) // TODO: this would be more correct as "unknown", so perhaps // `tracing-core` should make `Current::unknown()` public? .unwrap_or_else(span::Current::none) } /// Returns whether the wrapped subscriber would enable the current span. #[inline] pub fn enabled(&self, metadata: &Metadata<'_>) -> bool { self.subscriber .map(|subscriber| subscriber.enabled(metadata)) // If this context is `None`, we are registering a callsite, so // return `true` so that the layer does not incorrectly assume that // the inner subscriber has disabled this metadata. // TODO(eliza): would it be more correct for this to return an `Option`? .unwrap_or(true) } /// Records the provided `event` with the wrapped subscriber. /// /// # Notes /// /// - The subscriber is free to expect that the event's callsite has been /// [registered][register], and may panic or fail to observe the event if this is /// not the case. The `tracing` crate's macros ensure that all events are /// registered, but if the event is constructed through other means, the /// user is responsible for ensuring that [`register_callsite`][register] /// has been called prior to calling this method. /// - This does _not_ call [`enabled`] on the inner subscriber. If the /// caller wishes to apply the wrapped subscriber's filter before choosing /// whether to record the event, it may first call [`Context::enabled`] to /// check whether the event would be enabled. This allows `Layer`s to /// elide constructing the event if it would not be recorded. /// /// [register]: tracing_core::subscriber::Subscriber::register_callsite() /// [`enabled`]: tracing_core::subscriber::Subscriber::enabled() /// [`Context::enabled`]: Context::enabled() #[inline] pub fn event(&self, event: &Event<'_>) { if let Some(subscriber) = self.subscriber { subscriber.event(event); } } /// Returns a [`SpanRef`] for the parent span of the given [`Event`], if /// it has a parent. /// /// If the event has an explicitly overridden parent, this method returns /// a reference to that span. If the event's parent is the current span, /// this returns a reference to the current span, if there is one. If this /// returns `None`, then either the event's parent was explicitly set to /// `None`, or the event's parent was defined contextually, but no span /// is currently entered. /// /// Compared to [`Context::current_span`] and [`Context::lookup_current`], /// this respects overrides provided by the [`Event`]. /// /// Compared to [`Event::parent`], this automatically falls back to the contextual /// span, if required. /// /// ```rust /// use tracing::{Event, Subscriber}; /// use tracing_subscriber::{ /// layer::{Context, Layer}, /// prelude::*, /// registry::LookupSpan, /// }; /// /// struct PrintingLayer; /// impl Layer for PrintingLayer /// where /// S: Subscriber + for<'lookup> LookupSpan<'lookup>, /// { /// fn on_event(&self, event: &Event, ctx: Context) { /// let span = ctx.event_span(event); /// println!("Event in span: {:?}", span.map(|s| s.name())); /// } /// } /// /// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || { /// tracing::info!("no span"); /// // Prints: Event in span: None /// /// let span = tracing::info_span!("span"); /// tracing::info!(parent: &span, "explicitly specified"); /// // Prints: Event in span: Some("span") /// /// let _guard = span.enter(); /// tracing::info!("contextual span"); /// // Prints: Event in span: Some("span") /// }); /// ``` /// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
#[inline] pub fn event_span(&self, event: &Event<'_>) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { if event.is_root() { None } else if event.is_contextual() { self.lookup_current() } else { // TODO(eliza): this should handle parent IDs event.parent().and_then(|id| self.span(id)) } } /// Returns metadata for the span with the given `id`, if it exists. /// /// If this returns `None`, then no span exists for that ID (either it has /// closed or the ID is invalid). #[inline] pub fn metadata(&self, id: &span::Id) -> Option<&'static Metadata<'static>> where S: for<'lookup> LookupSpan<'lookup>, { let span = self.span(id)?; Some(span.metadata()) } /// Returns [stored data] for the span with the given `id`, if it exists. /// /// If this returns `None`, then no span exists for that ID (either it has /// closed or the ID is invalid). /// ///
    ///     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 #[inline] pub fn span(&self, id: &span::Id) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { let span = self.subscriber.as_ref()?.span(id)?; #[cfg(all(feature = "registry", feature = "std"))] return span.try_with_filter(self.filter); #[cfg(not(feature = "registry"))] Some(span) } /// Returns `true` if an active span exists for the given `Id`. /// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
#[inline] pub fn exists(&self, id: &span::Id) -> bool where S: for<'lookup> LookupSpan<'lookup>, { self.subscriber.as_ref().and_then(|s| s.span(id)).is_some() } /// Returns [stored data] for the span that the wrapped subscriber considers /// to be the current. /// /// If this returns `None`, then we are not currently within a span. /// ///
    ///     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 #[inline] pub fn lookup_current(&self) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { let subscriber = *self.subscriber.as_ref()?; let current = subscriber.current_span(); let id = current.id()?; let span = subscriber.span(id); debug_assert!( span.is_some(), "the subscriber should have data for the current span ({:?})!", id, ); // If we found a span, and our per-layer filter enables it, return that // span! #[cfg(all(feature = "registry", feature = "std"))] { if let Some(span) = span?.try_with_filter(self.filter) { Some(span) } else { // Otherwise, the span at the *top* of the stack is disabled by // per-layer filtering, but there may be additional spans in the stack. // // Currently, `LookupSpan` doesn't have a nice way of exposing access to // the whole span stack. However, if we can downcast the innermost // subscriber to a a `Registry`, we can iterate over its current span // stack. // // TODO(eliza): when https://github.com/tokio-rs/tracing/issues/1459 is // implemented, change this to use that instead... self.lookup_current_filtered(subscriber) } } #[cfg(not(feature = "registry"))] span } /// Slow path for when the current span is disabled by PLF and we have a /// registry. // This is called by `lookup_current` in the case that per-layer filtering // is in use. `lookup_current` is allowed to be inlined, but this method is // factored out to prevent the loop and (potentially-recursive) subscriber // downcasting from being inlined if `lookup_current` is inlined. #[inline(never)] #[cfg(all(feature = "registry", feature = "std"))] fn lookup_current_filtered<'lookup>( &self, subscriber: &'lookup S, ) -> Option> where S: LookupSpan<'lookup>, { let registry = (subscriber as &dyn Subscriber).downcast_ref::()?; registry .span_stack() .iter() .find_map(|id| subscriber.span(id)?.try_with_filter(self.filter)) } /// 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. /// ///
    /// 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: &span::Id) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { Some(self.span(id)?.scope()) } /// Returns an iterator over the [stored data] for all the spans in the /// current context, starting with the parent span of the specified event, /// and ending with the root of the trace tree and ending with the current span. /// ///
    /// 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, event: &Event<'_>) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { Some(self.event_span(event)?.scope()) } #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn with_filter(self, filter: FilterId) -> Self { // If we already have our own `FilterId`, combine it with the provided // one. That way, the new `FilterId` will consider a span to be disabled // if it was disabled by the given `FilterId` *or* any `FilterId`s for // layers "above" us in the stack. // // See the doc comment for `FilterId::and` for details. let filter = self.filter.and(filter); Self { filter, ..self } } #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn is_enabled_for(&self, span: &span::Id, filter: FilterId) -> bool where S: for<'lookup> LookupSpan<'lookup>, { self.is_enabled_inner(span, filter).unwrap_or(false) } #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn if_enabled_for(self, span: &span::Id, filter: FilterId) -> Option where S: for<'lookup> LookupSpan<'lookup>, { if self.is_enabled_inner(span, filter)? { Some(self.with_filter(filter)) } else { None } } #[cfg(all(feature = "registry", feature = "std"))] fn is_enabled_inner(&self, span: &span::Id, filter: FilterId) -> Option where S: for<'lookup> LookupSpan<'lookup>, { Some(self.span(span)?.is_enabled_for(filter)) } } impl<'a, S> Context<'a, S> { pub(crate) fn none() -> Self { Self { subscriber: None, #[cfg(feature = "registry")] filter: FilterId::none(), } } } impl<'a, S> Clone for Context<'a, S> { #[inline] fn clone(&self) -> Self { let subscriber = self.subscriber.as_ref().copied(); Context { subscriber, #[cfg(all(feature = "registry", feature = "std"))] filter: self.filter, } } }