//! 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: A, b: B, _s: PhantomData, } /// 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: A, b: B, _s: PhantomData, } /// 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: A, _s: PhantomData, } // === impl And === impl And where A: Filter, B: Filter, { /// 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 Filter for And where A: Filter, B: Filter, { #[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 { // 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 Clone for And where A: Clone, B: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), _s: PhantomData, } } } impl fmt::Debug for And 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 Or where A: Filter, B: Filter, { /// 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 Filter for Or where A: Filter, B: Filter, { #[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 { // 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 Clone for Or where A: Clone, B: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), _s: PhantomData, } } } impl fmt::Debug for Or 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 Not where A: Filter, { /// 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 Filter for Not where A: Filter, { #[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 { // 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 Clone for Not where A: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), _s: PhantomData, } } } impl fmt::Debug for Not where A: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Not").field(&self.a).finish() } }