tracing_subscriber/filter/layer_filters/
combinator.rs

1//! Filter combinators
2use crate::layer::{Context, Filter};
3use std::{cmp, fmt, marker::PhantomData};
4use tracing_core::{
5    span::{Attributes, Id, Record},
6    subscriber::Interest,
7    LevelFilter, Metadata,
8};
9
10/// Combines two [`Filter`]s so that spans and events are enabled if and only if
11/// *both* filters return `true`.
12///
13/// This type is typically returned by the [`FilterExt::and`] method. See that
14/// method's documentation for details.
15///
16/// [`Filter`]: crate::layer::Filter
17/// [`FilterExt::and`]: crate::filter::FilterExt::and
18pub struct And<A, B, S> {
19    a: A,
20    b: B,
21    _s: PhantomData<fn(S)>,
22}
23
24/// Combines two [`Filter`]s so that spans and events are enabled if *either* filter
25/// returns `true`.
26///
27/// This type is typically returned by the [`FilterExt::or`] method. See that
28/// method's documentation for details.
29///
30/// [`Filter`]: crate::layer::Filter
31/// [`FilterExt::or`]: crate::filter::FilterExt::or
32pub struct Or<A, B, S> {
33    a: A,
34    b: B,
35    _s: PhantomData<fn(S)>,
36}
37
38/// Inverts the result of a [`Filter`].
39///
40/// If the wrapped filter would enable a span or event, it will be disabled. If
41/// it would disable a span or event, that span or event will be enabled.
42///
43/// This type is typically returned by the [`FilterExt::not`] method. See that
44/// method's documentation for details.
45///
46/// [`Filter`]: crate::layer::Filter
47/// [`FilterExt::not`]: crate::filter::FilterExt::not
48pub struct Not<A, S> {
49    a: A,
50    _s: PhantomData<fn(S)>,
51}
52
53// === impl And ===
54
55impl<A, B, S> And<A, B, S>
56where
57    A: Filter<S>,
58    B: Filter<S>,
59{
60    /// Combines two [`Filter`]s so that spans and events are enabled if and only if
61    /// *both* filters return `true`.
62    ///
63    /// # Examples
64    ///
65    /// Enabling spans or events if they have both a particular target *and* are
66    /// above a certain level:
67    ///
68    /// ```ignore
69    /// use tracing_subscriber::{
70    ///     filter::{filter_fn, LevelFilter, combinator::And},
71    ///     prelude::*,
72    /// };
73    ///
74    /// // Enables spans and events with targets starting with `interesting_target`:
75    /// let target_filter = filter_fn(|meta| {
76    ///     meta.target().starts_with("interesting_target")
77    /// });
78    ///
79    /// // Enables spans and events with levels `INFO` and below:
80    /// let level_filter = LevelFilter::INFO;
81    ///
82    /// // Combine the two filters together so that a span or event is only enabled
83    /// // if *both* filters would enable it:
84    /// let filter = And::new(level_filter, target_filter);
85    ///
86    /// tracing_subscriber::registry()
87    ///     .with(tracing_subscriber::fmt::layer().with_filter(filter))
88    ///     .init();
89    ///
90    /// // This event will *not* be enabled:
91    /// tracing::info!("an event with an uninteresting target");
92    ///
93    /// // This event *will* be enabled:
94    /// tracing::info!(target: "interesting_target", "a very interesting event");
95    ///
96    /// // This event will *not* be enabled:
97    /// tracing::debug!(target: "interesting_target", "interesting debug event...");
98    /// ```
99    ///
100    /// [`Filter`]: crate::layer::Filter
101    pub(crate) fn new(a: A, b: B) -> Self {
102        Self {
103            a,
104            b,
105            _s: PhantomData,
106        }
107    }
108}
109
110impl<A, B, S> Filter<S> for And<A, B, S>
111where
112    A: Filter<S>,
113    B: Filter<S>,
114{
115    #[inline]
116    fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
117        self.a.enabled(meta, cx) && self.b.enabled(meta, cx)
118    }
119
120    fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
121        let a = self.a.callsite_enabled(meta);
122        if a.is_never() {
123            return a;
124        }
125
126        let b = self.b.callsite_enabled(meta);
127
128        if !b.is_always() {
129            return b;
130        }
131
132        a
133    }
134
135    fn max_level_hint(&self) -> Option<LevelFilter> {
136        // If either hint is `None`, return `None`. Otherwise, return the most restrictive.
137        cmp::min(self.a.max_level_hint(), self.b.max_level_hint())
138    }
139
140    #[inline]
141    fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
142        self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx)
143    }
144
145    #[inline]
146    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
147        self.a.on_new_span(attrs, id, ctx.clone());
148        self.b.on_new_span(attrs, id, ctx)
149    }
150
151    #[inline]
152    fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
153        self.a.on_record(id, values, ctx.clone());
154        self.b.on_record(id, values, ctx);
155    }
156
157    #[inline]
158    fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
159        self.a.on_enter(id, ctx.clone());
160        self.b.on_enter(id, ctx);
161    }
162
163    #[inline]
164    fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
165        self.a.on_exit(id, ctx.clone());
166        self.b.on_exit(id, ctx);
167    }
168
169    #[inline]
170    fn on_close(&self, id: Id, ctx: Context<'_, S>) {
171        self.a.on_close(id.clone(), ctx.clone());
172        self.b.on_close(id, ctx);
173    }
174}
175
176impl<A, B, S> Clone for And<A, B, S>
177where
178    A: Clone,
179    B: Clone,
180{
181    fn clone(&self) -> Self {
182        Self {
183            a: self.a.clone(),
184            b: self.b.clone(),
185            _s: PhantomData,
186        }
187    }
188}
189
190impl<A, B, S> fmt::Debug for And<A, B, S>
191where
192    A: fmt::Debug,
193    B: fmt::Debug,
194{
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        f.debug_struct("And")
197            .field("a", &self.a)
198            .field("b", &self.b)
199            .finish()
200    }
201}
202
203// === impl Or ===
204
205impl<A, B, S> Or<A, B, S>
206where
207    A: Filter<S>,
208    B: Filter<S>,
209{
210    /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter
211    /// returns `true`.
212    ///
213    /// # Examples
214    ///
215    /// Enabling spans and events at the `INFO` level and above, and all spans
216    /// and events with a particular target:
217    ///
218    /// ```ignore
219    /// use tracing_subscriber::{
220    ///     filter::{filter_fn, LevelFilter, combinator::Or},
221    ///     prelude::*,
222    /// };
223    ///
224    /// // Enables spans and events with targets starting with `interesting_target`:
225    /// let target_filter = filter_fn(|meta| {
226    ///     meta.target().starts_with("interesting_target")
227    /// });
228    ///
229    /// // Enables spans and events with levels `INFO` and below:
230    /// let level_filter = LevelFilter::INFO;
231    ///
232    /// // Combine the two filters together so that a span or event is enabled
233    /// // if it is at INFO or lower, or if it has a target starting with
234    /// // `interesting_target`.
235    /// let filter = Or::new(level_filter, target_filter);
236    ///
237    /// tracing_subscriber::registry()
238    ///     .with(tracing_subscriber::fmt::layer().with_filter(filter))
239    ///     .init();
240    ///
241    /// // This event will *not* be enabled:
242    /// tracing::debug!("an uninteresting event");
243    ///
244    /// // This event *will* be enabled:
245    /// tracing::info!("an uninteresting INFO event");
246    ///
247    /// // This event *will* be enabled:
248    /// tracing::info!(target: "interesting_target", "a very interesting event");
249    ///
250    /// // This event *will* be enabled:
251    /// tracing::debug!(target: "interesting_target", "interesting debug event...");
252    /// ```
253    ///
254    /// Enabling a higher level for a particular target by using `Or` in
255    /// conjunction with the [`And`] combinator:
256    ///
257    /// ```ignore
258    /// use tracing_subscriber::{
259    ///     filter::{filter_fn, LevelFilter, combinator},
260    ///     prelude::*,
261    /// };
262    ///
263    /// // This filter will enable spans and events with targets beginning with
264    /// // `my_crate`:
265    /// let my_crate = filter_fn(|meta| {
266    ///     meta.target().starts_with("my_crate")
267    /// });
268    ///
269    /// // Combine the `my_crate` filter with a `LevelFilter` to produce a filter
270    /// // that will enable the `INFO` level and lower for spans and events with
271    /// // `my_crate` targets:
272    /// let filter = combinator::And::new(my_crate, LevelFilter::INFO);
273    ///
274    /// // If a span or event *doesn't* have a target beginning with
275    /// // `my_crate`, enable it if it has the `WARN` level or lower:
276    /// // let filter = combinator::Or::new(filter, LevelFilter::WARN);
277    ///
278    /// tracing_subscriber::registry()
279    ///     .with(tracing_subscriber::fmt::layer().with_filter(filter))
280    ///     .init();
281    /// ```
282    ///
283    /// [`Filter`]: crate::layer::Filter
284    pub(crate) fn new(a: A, b: B) -> Self {
285        Self {
286            a,
287            b,
288            _s: PhantomData,
289        }
290    }
291}
292
293impl<A, B, S> Filter<S> for Or<A, B, S>
294where
295    A: Filter<S>,
296    B: Filter<S>,
297{
298    #[inline]
299    fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
300        self.a.enabled(meta, cx) || self.b.enabled(meta, cx)
301    }
302
303    fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
304        let a = self.a.callsite_enabled(meta);
305        let b = self.b.callsite_enabled(meta);
306
307        // If either filter will always enable the span or event, return `always`.
308        if a.is_always() || b.is_always() {
309            return Interest::always();
310        }
311
312        // Okay, if either filter will sometimes enable the span or event,
313        // return `sometimes`.
314        if a.is_sometimes() || b.is_sometimes() {
315            return Interest::sometimes();
316        }
317
318        debug_assert!(
319            a.is_never() && b.is_never(),
320            "if neither filter was `always` or `sometimes`, both must be `never` (a={:?}; b={:?})",
321            a,
322            b,
323        );
324        Interest::never()
325    }
326
327    fn max_level_hint(&self) -> Option<LevelFilter> {
328        // If either hint is `None`, return `None`. Otherwise, return the less restrictive.
329        Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?))
330    }
331
332    #[inline]
333    fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
334        self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx)
335    }
336
337    #[inline]
338    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
339        self.a.on_new_span(attrs, id, ctx.clone());
340        self.b.on_new_span(attrs, id, ctx)
341    }
342
343    #[inline]
344    fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
345        self.a.on_record(id, values, ctx.clone());
346        self.b.on_record(id, values, ctx);
347    }
348
349    #[inline]
350    fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
351        self.a.on_enter(id, ctx.clone());
352        self.b.on_enter(id, ctx);
353    }
354
355    #[inline]
356    fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
357        self.a.on_exit(id, ctx.clone());
358        self.b.on_exit(id, ctx);
359    }
360
361    #[inline]
362    fn on_close(&self, id: Id, ctx: Context<'_, S>) {
363        self.a.on_close(id.clone(), ctx.clone());
364        self.b.on_close(id, ctx);
365    }
366}
367
368impl<A, B, S> Clone for Or<A, B, S>
369where
370    A: Clone,
371    B: Clone,
372{
373    fn clone(&self) -> Self {
374        Self {
375            a: self.a.clone(),
376            b: self.b.clone(),
377            _s: PhantomData,
378        }
379    }
380}
381
382impl<A, B, S> fmt::Debug for Or<A, B, S>
383where
384    A: fmt::Debug,
385    B: fmt::Debug,
386{
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        f.debug_struct("Or")
389            .field("a", &self.a)
390            .field("b", &self.b)
391            .finish()
392    }
393}
394
395// === impl Not ===
396
397impl<A, S> Not<A, S>
398where
399    A: Filter<S>,
400{
401    /// Inverts the result of a [`Filter`].
402    ///
403    /// If the wrapped filter would enable a span or event, it will be disabled. If
404    /// it would disable a span or event, that span or event will be enabled.
405    ///
406    /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`]
407    /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as
408    /// filters which do not implement filtering on event field values will return
409    /// the default `true` even for events that their [`enabled`] method disables.
410    ///
411    /// Consider a normal filter defined as:
412    ///
413    /// ```ignore (pseudo-code)
414    /// // for spans
415    /// match callsite_enabled() {
416    ///     ALWAYS => on_span(),
417    ///     SOMETIMES => if enabled() { on_span() },
418    ///     NEVER => (),
419    /// }
420    /// // for events
421    /// match callsite_enabled() {
422    ///    ALWAYS => on_event(),
423    ///    SOMETIMES => if enabled() && event_enabled() { on_event() },
424    ///    NEVER => (),
425    /// }
426    /// ```
427    ///
428    /// and an inverted filter defined as:
429    ///
430    /// ```ignore (pseudo-code)
431    /// // for spans
432    /// match callsite_enabled() {
433    ///     ALWAYS => (),
434    ///     SOMETIMES => if !enabled() { on_span() },
435    ///     NEVER => on_span(),
436    /// }
437    /// // for events
438    /// match callsite_enabled() {
439    ///     ALWAYS => (),
440    ///     SOMETIMES => if !enabled() { on_event() },
441    ///     NEVER => on_event(),
442    /// }
443    /// ```
444    ///
445    /// A proper inversion would do `!(enabled() && event_enabled())` (or
446    /// `!enabled() || !event_enabled()`), but because of the implicit `&&`
447    /// relation between `enabled` and `event_enabled`, it is difficult to
448    /// short circuit and not call the wrapped `event_enabled`.
449    ///
450    /// A combinator which remembers the result of `enabled` in order to call
451    /// `event_enabled` only when `enabled() == true` is possible, but requires
452    /// additional thread-local mutable state to support a very niche use case.
453    //
454    //  Also, it'd mean the wrapped layer's `enabled()` always gets called and
455    //  globally applied to events where it doesn't today, since we can't know
456    //  what `event_enabled` will say until we have the event to call it with.
457    ///
458    /// [`Filter`]: crate::layer::Filter
459    /// [`enabled`]: crate::layer::Filter::enabled
460    /// [`event_enabled`]: crate::layer::Filter::event_enabled
461    /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled
462    pub(crate) fn new(a: A) -> Self {
463        Self { a, _s: PhantomData }
464    }
465}
466
467impl<A, S> Filter<S> for Not<A, S>
468where
469    A: Filter<S>,
470{
471    #[inline]
472    fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
473        !self.a.enabled(meta, cx)
474    }
475
476    fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
477        match self.a.callsite_enabled(meta) {
478            i if i.is_always() => Interest::never(),
479            i if i.is_never() => Interest::always(),
480            _ => Interest::sometimes(),
481        }
482    }
483
484    fn max_level_hint(&self) -> Option<LevelFilter> {
485        // TODO(eliza): figure this out???
486        None
487    }
488
489    #[inline]
490    fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
491        // Never disable based on event_enabled; we "disabled" it in `enabled`,
492        // so the `not` has already been applied and filtered this not out.
493        let _ = (event, cx);
494        true
495    }
496
497    #[inline]
498    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
499        self.a.on_new_span(attrs, id, ctx);
500    }
501
502    #[inline]
503    fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
504        self.a.on_record(id, values, ctx.clone());
505    }
506
507    #[inline]
508    fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
509        self.a.on_enter(id, ctx);
510    }
511
512    #[inline]
513    fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
514        self.a.on_exit(id, ctx);
515    }
516
517    #[inline]
518    fn on_close(&self, id: Id, ctx: Context<'_, S>) {
519        self.a.on_close(id, ctx);
520    }
521}
522
523impl<A, S> Clone for Not<A, S>
524where
525    A: Clone,
526{
527    fn clone(&self) -> Self {
528        Self {
529            a: self.a.clone(),
530            _s: PhantomData,
531        }
532    }
533}
534
535impl<A, S> fmt::Debug for Not<A, S>
536where
537    A: fmt::Debug,
538{
539    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
540        f.debug_tuple("Not").field(&self.a).finish()
541    }
542}