blob: 3934a13267c7c2d2bf80f7aeccf4389c46a69ae8 [file] [log] [blame]
//! 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()
}
}