diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs | 542 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/layer_filters/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs) | 454 |
2 files changed, 964 insertions, 32 deletions
diff --git a/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs b/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs new file mode 100644 index 000000000..3934a1326 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs @@ -0,0 +1,542 @@ +//! Filter combinators +use crate::layer::{Context, Filter}; +use std::{cmp, fmt, marker::PhantomData}; +use tracing_core::{ + span::{Attributes, Id, Record}, + subscriber::Interest, + LevelFilter, Metadata, +}; + +/// Combines two [`Filter`]s so that spans and events are enabled if and only if +/// *both* filters return `true`. +/// +/// This type is typically returned by the [`FilterExt::and`] method. See that +/// method's documentation for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [`FilterExt::and`]: crate::filter::FilterExt::and +pub struct And<A, B, S> { + a: A, + b: B, + _s: PhantomData<fn(S)>, +} + +/// Combines two [`Filter`]s so that spans and events are enabled if *either* filter +/// returns `true`. +/// +/// This type is typically returned by the [`FilterExt::or`] method. See that +/// method's documentation for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [`FilterExt::or`]: crate::filter::FilterExt::or +pub struct Or<A, B, S> { + a: A, + b: B, + _s: PhantomData<fn(S)>, +} + +/// Inverts the result of a [`Filter`]. +/// +/// If the wrapped filter would enable a span or event, it will be disabled. If +/// it would disable a span or event, that span or event will be enabled. +/// +/// This type is typically returned by the [`FilterExt::not`] method. See that +/// method's documentation for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [`FilterExt::not`]: crate::filter::FilterExt::not +pub struct Not<A, S> { + a: A, + _s: PhantomData<fn(S)>, +} + +// === impl And === + +impl<A, B, S> And<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + /// Combines two [`Filter`]s so that spans and events are enabled if and only if + /// *both* filters return `true`. + /// + /// # Examples + /// + /// Enabling spans or events if they have both a particular target *and* are + /// above a certain level: + /// + /// ```ignore + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, combinator::And}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together so that a span or event is only enabled + /// // if *both* filters would enable it: + /// let filter = And::new(level_filter, target_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::info!("an event with an uninteresting target"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event will *not* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + pub(crate) fn new(a: A, b: B) -> Self { + Self { + a, + b, + _s: PhantomData, + } + } +} + +impl<A, B, S> Filter<S> for And<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.a.enabled(meta, cx) && self.b.enabled(meta, cx) + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + let a = self.a.callsite_enabled(meta); + if a.is_never() { + return a; + } + + let b = self.b.callsite_enabled(meta); + + if !b.is_always() { + return b; + } + + a + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + // If either hint is `None`, return `None`. Otherwise, return the most restrictive. + cmp::min(self.a.max_level_hint(), self.b.max_level_hint()) + } + + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx) + } + + #[inline] + fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + self.a.on_new_span(attrs, id, ctx.clone()); + self.b.on_new_span(attrs, id, ctx) + } + + #[inline] + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + self.a.on_record(id, values, ctx.clone()); + self.b.on_record(id, values, ctx); + } + + #[inline] + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_enter(id, ctx.clone()); + self.b.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_exit(id, ctx.clone()); + self.b.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + self.a.on_close(id.clone(), ctx.clone()); + self.b.on_close(id, ctx); + } +} + +impl<A, B, S> Clone for And<A, B, S> +where + A: Clone, + B: Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + b: self.b.clone(), + _s: PhantomData, + } + } +} + +impl<A, B, S> fmt::Debug for And<A, B, S> +where + A: fmt::Debug, + B: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("And") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } +} + +// === impl Or === + +impl<A, B, S> Or<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter + /// returns `true`. + /// + /// # Examples + /// + /// Enabling spans and events at the `INFO` level and above, and all spans + /// and events with a particular target: + /// + /// ```ignore + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, combinator::Or}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together so that a span or event is enabled + /// // if it is at INFO or lower, or if it has a target starting with + /// // `interesting_target`. + /// let filter = Or::new(level_filter, target_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::debug!("an uninteresting event"); + /// + /// // This event *will* be enabled: + /// tracing::info!("an uninteresting INFO event"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event *will* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// Enabling a higher level for a particular target by using `Or` in + /// conjunction with the [`And`] combinator: + /// + /// ```ignore + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, combinator}, + /// prelude::*, + /// }; + /// + /// // This filter will enable spans and events with targets beginning with + /// // `my_crate`: + /// let my_crate = filter_fn(|meta| { + /// meta.target().starts_with("my_crate") + /// }); + /// + /// // Combine the `my_crate` filter with a `LevelFilter` to produce a filter + /// // that will enable the `INFO` level and lower for spans and events with + /// // `my_crate` targets: + /// let filter = combinator::And::new(my_crate, LevelFilter::INFO); + /// + /// // If a span or event *doesn't* have a target beginning with + /// // `my_crate`, enable it if it has the `WARN` level or lower: + /// // let filter = combinator::Or::new(filter, LevelFilter::WARN); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + pub(crate) fn new(a: A, b: B) -> Self { + Self { + a, + b, + _s: PhantomData, + } + } +} + +impl<A, B, S> Filter<S> for Or<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.a.enabled(meta, cx) || self.b.enabled(meta, cx) + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + let a = self.a.callsite_enabled(meta); + let b = self.b.callsite_enabled(meta); + + // If either filter will always enable the span or event, return `always`. + if a.is_always() || b.is_always() { + return Interest::always(); + } + + // Okay, if either filter will sometimes enable the span or event, + // return `sometimes`. + if a.is_sometimes() || b.is_sometimes() { + return Interest::sometimes(); + } + + debug_assert!( + a.is_never() && b.is_never(), + "if neither filter was `always` or `sometimes`, both must be `never` (a={:?}; b={:?})", + a, + b, + ); + Interest::never() + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + // If either hint is `None`, return `None`. Otherwise, return the less restrictive. + Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?)) + } + + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx) + } + + #[inline] + fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + self.a.on_new_span(attrs, id, ctx.clone()); + self.b.on_new_span(attrs, id, ctx) + } + + #[inline] + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + self.a.on_record(id, values, ctx.clone()); + self.b.on_record(id, values, ctx); + } + + #[inline] + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_enter(id, ctx.clone()); + self.b.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_exit(id, ctx.clone()); + self.b.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + self.a.on_close(id.clone(), ctx.clone()); + self.b.on_close(id, ctx); + } +} + +impl<A, B, S> Clone for Or<A, B, S> +where + A: Clone, + B: Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + b: self.b.clone(), + _s: PhantomData, + } + } +} + +impl<A, B, S> fmt::Debug for Or<A, B, S> +where + A: fmt::Debug, + B: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Or") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } +} + +// === impl Not === + +impl<A, S> Not<A, S> +where + A: Filter<S>, +{ + /// Inverts the result of a [`Filter`]. + /// + /// If the wrapped filter would enable a span or event, it will be disabled. If + /// it would disable a span or event, that span or event will be enabled. + /// + /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] + /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as + /// filters which do not implement filtering on event field values will return + /// the default `true` even for events that their [`enabled`] method disables. + /// + /// Consider a normal filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => on_span(), + /// SOMETIMES => if enabled() { on_span() }, + /// NEVER => (), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => on_event(), + /// SOMETIMES => if enabled() && event_enabled() { on_event() }, + /// NEVER => (), + /// } + /// ``` + /// + /// and an inverted filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_span() }, + /// NEVER => on_span(), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_event() }, + /// NEVER => on_event(), + /// } + /// ``` + /// + /// A proper inversion would do `!(enabled() && event_enabled())` (or + /// `!enabled() || !event_enabled()`), but because of the implicit `&&` + /// relation between `enabled` and `event_enabled`, it is difficult to + /// short circuit and not call the wrapped `event_enabled`. + /// + /// A combinator which remembers the result of `enabled` in order to call + /// `event_enabled` only when `enabled() == true` is possible, but requires + /// additional thread-local mutable state to support a very niche use case. + // + // Also, it'd mean the wrapped layer's `enabled()` always gets called and + // globally applied to events where it doesn't today, since we can't know + // what `event_enabled` will say until we have the event to call it with. + /// + /// [`Filter`]: crate::layer::Filter + /// [`enabled`]: crate::layer::Filter::enabled + /// [`event_enabled`]: crate::layer::Filter::event_enabled + /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled + pub(crate) fn new(a: A) -> Self { + Self { a, _s: PhantomData } + } +} + +impl<A, S> Filter<S> for Not<A, S> +where + A: Filter<S>, +{ + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + !self.a.enabled(meta, cx) + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + match self.a.callsite_enabled(meta) { + i if i.is_always() => Interest::never(), + i if i.is_never() => Interest::always(), + _ => Interest::sometimes(), + } + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + // TODO(eliza): figure this out??? + None + } + + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + // Never disable based on event_enabled; we "disabled" it in `enabled`, + // so the `not` has already been applied and filtered this not out. + let _ = (event, cx); + true + } + + #[inline] + fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + self.a.on_new_span(attrs, id, ctx); + } + + #[inline] + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + self.a.on_record(id, values, ctx.clone()); + } + + #[inline] + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + self.a.on_close(id, ctx); + } +} + +impl<A, S> Clone for Not<A, S> +where + A: Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + _s: PhantomData, + } + } +} + +impl<A, S> fmt::Debug for Not<A, S> +where + A: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Not").field(&self.a).finish() + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs index e77fd3751..e50ee6f00 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs +++ b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -37,14 +37,16 @@ use std::{ cell::{Cell, RefCell}, fmt, marker::PhantomData, + ops::Deref, sync::Arc, thread_local, }; use tracing_core::{ span, subscriber::{Interest, Subscriber}, - Event, Metadata, + Dispatch, Event, Metadata, }; +pub mod combinator; /// A [`Layer`] that wraps an inner [`Layer`] and adds a [`Filter`] which /// controls what spans and events are enabled for that layer. @@ -158,7 +160,288 @@ thread_local! { pub(crate) static FILTERING: FilterState = FilterState::new(); } +/// Extension trait adding [combinators] for combining [`Filter`]. +/// +/// [combinators]: crate::filter::combinator +/// [`Filter`]: crate::layer::Filter +pub trait FilterExt<S>: layer::Filter<S> { + /// Combines this [`Filter`] with another [`Filter`] s so that spans and + /// events are enabled if and only if *both* filters return `true`. + /// + /// # Examples + /// + /// Enabling spans or events if they have both a particular target *and* are + /// above a certain level: + /// + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together, returning a filter that only enables + /// // spans and events that *both* filters will enable: + /// let filter = target_filter.and(level_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::info!("an event with an uninteresting target"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event will *not* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + fn and<B>(self, other: B) -> combinator::And<Self, B, S> + where + Self: Sized, + B: layer::Filter<S>, + { + combinator::And::new(self, other) + } + + /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter + /// returns `true`. + /// + /// # Examples + /// + /// Enabling spans and events at the `INFO` level and above, and all spans + /// and events with a particular target: + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together so that a span or event is enabled + /// // if it is at INFO or lower, or if it has a target starting with + /// // `interesting_target`. + /// let filter = level_filter.or(target_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::debug!("an uninteresting event"); + /// + /// // This event *will* be enabled: + /// tracing::info!("an uninteresting INFO event"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event *will* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// Enabling a higher level for a particular target by using `or` in + /// conjunction with the [`and`] combinator: + /// + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// // This filter will enable spans and events with targets beginning with + /// // `my_crate`: + /// let my_crate = filter_fn(|meta| { + /// meta.target().starts_with("my_crate") + /// }); + /// + /// let filter = my_crate + /// // Combine the `my_crate` filter with a `LevelFilter` to produce a + /// // filter that will enable the `INFO` level and lower for spans and + /// // events with `my_crate` targets: + /// .and(LevelFilter::INFO) + /// // If a span or event *doesn't* have a target beginning with + /// // `my_crate`, enable it if it has the `WARN` level or lower: + /// .or(LevelFilter::WARN); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + /// [`and`]: FilterExt::and + fn or<B>(self, other: B) -> combinator::Or<Self, B, S> + where + Self: Sized, + B: layer::Filter<S>, + { + combinator::Or::new(self, other) + } + + /// Inverts `self`, returning a filter that enables spans and events only if + /// `self` would *not* enable them. + /// + /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] + /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as + /// filters which do not implement filtering on event field values will return + /// the default `true` even for events that their [`enabled`] method disables. + /// + /// Consider a normal filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => on_span(), + /// SOMETIMES => if enabled() { on_span() }, + /// NEVER => (), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => on_event(), + /// SOMETIMES => if enabled() && event_enabled() { on_event() }, + /// NEVER => (), + /// } + /// ``` + /// + /// and an inverted filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_span() }, + /// NEVER => on_span(), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_event() }, + /// NEVER => on_event(), + /// } + /// ``` + /// + /// A proper inversion would do `!(enabled() && event_enabled())` (or + /// `!enabled() || !event_enabled()`), but because of the implicit `&&` + /// relation between `enabled` and `event_enabled`, it is difficult to + /// short circuit and not call the wrapped `event_enabled`. + /// + /// A combinator which remembers the result of `enabled` in order to call + /// `event_enabled` only when `enabled() == true` is possible, but requires + /// additional thread-local mutable state to support a very niche use case. + // + // Also, it'd mean the wrapped layer's `enabled()` always gets called and + // globally applied to events where it doesn't today, since we can't know + // what `event_enabled` will say until we have the event to call it with. + /// + /// [`Filter`]: crate::subscribe::Filter + /// [`enabled`]: crate::subscribe::Filter::enabled + /// [`event_enabled`]: crate::subscribe::Filter::event_enabled + /// [`callsite_enabled`]: crate::subscribe::Filter::callsite_enabled + fn not(self) -> combinator::Not<Self, S> + where + Self: Sized, + { + combinator::Not::new(self) + } + + /// [Boxes] `self`, erasing its concrete type. + /// + /// This is equivalent to calling [`Box::new`], but in method form, so that + /// it can be used when chaining combinator methods. + /// + /// # Examples + /// + /// When different combinations of filters are used conditionally, they may + /// have different types. For example, the following code won't compile, + /// since the `if` and `else` clause produce filters of different types: + /// + /// ```compile_fail + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// let enable_bar_target: bool = // ... + /// # false; + /// + /// let filter = if enable_bar_target { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// // If `enable_bar_target` is true, add a `filter_fn` enabling + /// // spans and events with the target `bar`: + /// .or(filter_fn(|meta| meta.target().starts_with("bar"))) + /// .and(LevelFilter::INFO) + /// } else { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// .and(LevelFilter::INFO) + /// }; + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// By using `boxed`, the types of the two different branches can be erased, + /// so the assignment to the `filter` variable is valid (as both branches + /// have the type `Box<dyn Filter<S> + Send + Sync + 'static>`). The + /// following code *does* compile: + /// + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// let enable_bar_target: bool = // ... + /// # false; + /// + /// let filter = if enable_bar_target { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// .or(filter_fn(|meta| meta.target().starts_with("bar"))) + /// .and(LevelFilter::INFO) + /// // Boxing the filter erases its type, so both branches now + /// // have the same type. + /// .boxed() + /// } else { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// .and(LevelFilter::INFO) + /// .boxed() + /// }; + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// [Boxes]: std::boxed + /// [`Box::new`]: std::boxed::Box::new + fn boxed(self) -> Box<dyn layer::Filter<S> + Send + Sync + 'static> + where + Self: Sized + Send + Sync + 'static, + { + Box::new(self) + } +} + // === impl Filter === + #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl<S> layer::Filter<S> for LevelFilter { @@ -179,38 +462,35 @@ impl<S> layer::Filter<S> for LevelFilter { } } -impl<S> layer::Filter<S> for Arc<dyn layer::Filter<S> + Send + Sync + 'static> { - #[inline] - fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { - (**self).enabled(meta, cx) - } +macro_rules! filter_impl_body { + () => { + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.deref().enabled(meta, cx) + } - #[inline] - fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { - (**self).callsite_enabled(meta) - } + #[inline] + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + self.deref().callsite_enabled(meta) + } - #[inline] - fn max_level_hint(&self) -> Option<LevelFilter> { - (**self).max_level_hint() - } + #[inline] + fn max_level_hint(&self) -> Option<LevelFilter> { + self.deref().max_level_hint() + } + }; } -impl<S> layer::Filter<S> for Box<dyn layer::Filter<S> + Send + Sync + 'static> { - #[inline] - fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { - (**self).enabled(meta, cx) - } - - #[inline] - fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { - (**self).callsite_enabled(meta) - } +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl<S> layer::Filter<S> for Arc<dyn layer::Filter<S> + Send + Sync + 'static> { + filter_impl_body!(); +} - #[inline] - fn max_level_hint(&self) -> Option<LevelFilter> { - (**self).max_level_hint() - } +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl<S> layer::Filter<S> for Box<dyn layer::Filter<S> + Send + Sync + 'static> { + filter_impl_body!(); } // === impl Filtered === @@ -247,6 +527,78 @@ impl<L, F, S> Filtered<L, F, S> { fn did_enable(&self, f: impl FnOnce()) { FILTERING.with(|filtering| filtering.did_enable(self.id(), f)) } + + /// Borrows the [`Filter`](crate::layer::Filter) used by this layer. + pub fn filter(&self) -> &F { + &self.filter + } + + /// Mutably borrows the [`Filter`](crate::layer::Filter) used by this layer. + /// + /// When this layer can be mutably borrowed, this may be used to mutate the filter. + /// Generally, this will primarily be used with the + /// [`reload::Handle::modify`](crate::reload::Handle::modify) method. + /// + /// # Examples + /// + /// ``` + /// # use tracing::info; + /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*}; + /// # fn main() { + /// let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN); + /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer); + /// # + /// # // specifying the Registry type is required + /// # let _: &reload::Handle<filter::Filtered<fmt::Layer<Registry>, + /// # filter::LevelFilter, Registry>,Registry> + /// # = &reload_handle; + /// # + /// info!("This will be ignored"); + /// reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO); + /// info!("This will be logged"); + /// # } + /// ``` + pub fn filter_mut(&mut self) -> &mut F { + &mut self.filter + } + + /// Borrows the inner [`Layer`] wrapped by this `Filtered` layer. + pub fn inner(&self) -> &L { + &self.layer + } + + /// Mutably borrows the inner [`Layer`] wrapped by this `Filtered` layer. + /// + /// This method is primarily expected to be used with the + /// [`reload::Handle::modify`](crate::reload::Handle::modify) method. + /// + /// # Examples + /// + /// ``` + /// # use tracing::info; + /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*}; + /// # fn non_blocking<T: std::io::Write>(writer: T) -> (fn() -> std::io::Stdout) { + /// # std::io::stdout + /// # } + /// # fn main() { + /// let filtered_layer = fmt::layer().with_writer(non_blocking(std::io::stderr())).with_filter(filter::LevelFilter::INFO); + /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer); + /// # + /// # // specifying the Registry type is required + /// # let _: &reload::Handle<filter::Filtered<fmt::Layer<Registry, _, _, fn() -> std::io::Stdout>, + /// # filter::LevelFilter, Registry>, Registry> + /// # = &reload_handle; + /// # + /// info!("This will be logged to stderr"); + /// reload_handle.modify(|layer| *layer.inner_mut().writer_mut() = non_blocking(std::io::stdout())); + /// info!("This will be logged to stdout"); + /// # } + /// ``` + /// + /// [subscriber]: Subscribe + pub fn inner_mut(&mut self) -> &mut L { + &mut self.layer + } } impl<S, L, F> Layer<S> for Filtered<L, F, S> @@ -255,6 +607,10 @@ where F: layer::Filter<S> + 'static, L: Layer<S>, { + fn on_register_dispatch(&self, collector: &Dispatch) { + self.layer.on_register_dispatch(collector); + } + fn on_layer(&mut self, subscriber: &mut S) { self.id = MagicPlfDowncastMarker(subscriber.register_filter()); self.layer.on_layer(subscriber); @@ -322,7 +678,9 @@ where fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, cx: Context<'_, S>) { self.did_enable(|| { - self.layer.on_new_span(attrs, id, cx.with_filter(self.id())); + let cx = cx.with_filter(self.id()); + self.filter.on_new_span(attrs, id, cx.clone()); + self.layer.on_new_span(attrs, id, cx); }) } @@ -333,6 +691,7 @@ where fn on_record(&self, span: &span::Id, values: &span::Record<'_>, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(span, self.id()) { + self.filter.on_record(span, values, cx.clone()); self.layer.on_record(span, values, cx) } } @@ -345,6 +704,22 @@ where } } + fn event_enabled(&self, event: &Event<'_>, cx: Context<'_, S>) -> bool { + let cx = cx.with_filter(self.id()); + let enabled = FILTERING + .with(|filtering| filtering.and(self.id(), || self.filter.event_enabled(event, &cx))); + + if enabled { + // If the filter enabled this event, ask the wrapped subscriber if + // _it_ wants it --- it might have a global filter. + self.layer.event_enabled(event, cx) + } else { + // Otherwise, return `true`. See the comment in `enabled` for why this + // is necessary. + true + } + } + fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) { self.did_enable(|| { self.layer.on_event(event, cx.with_filter(self.id())); @@ -353,19 +728,22 @@ where fn on_enter(&self, id: &span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(id, self.id()) { - self.layer.on_enter(id, cx) + self.filter.on_enter(id, cx.clone()); + self.layer.on_enter(id, cx); } } fn on_exit(&self, id: &span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(id, self.id()) { - self.layer.on_exit(id, cx) + self.filter.on_exit(id, cx.clone()); + self.layer.on_exit(id, cx); } } fn on_close(&self, id: span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(&id, self.id()) { - self.layer.on_close(id, cx) + self.filter.on_close(id.clone(), cx.clone()); + self.layer.on_close(id, cx); } } @@ -544,6 +922,10 @@ impl fmt::Binary for FilterId { } } +// === impl FilterExt === + +impl<F, S> FilterExt<S> for F where F: layer::Filter<S> {} + // === impl FilterMap === impl FilterMap { @@ -709,6 +1091,14 @@ impl FilterState { } } + /// Run a second filtering pass, e.g. for Subscribe::event_enabled. + fn and(&self, filter: FilterId, f: impl FnOnce() -> bool) -> bool { + let map = self.enabled.get(); + let enabled = map.is_enabled(filter) && f(); + self.enabled.set(map.set(filter, enabled)); + enabled + } + /// Clears the current in-progress filter state. /// /// This resets the [`FilterMap`] and current [`Interest`] as well as |