-
Notifications
You must be signed in to change notification settings - Fork 734
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
subscriber: add Filtered::filter_mut method (#1959)
Partially fixes #1629 (I think making `reload::Handle::reload` work with `Filtered` would be cleaner, but this approach seemed easier to me) I assumed opening the PR against v0.1.x is correct as I couldn't find the `Filtered` type in master. I think it'd be sensible to note the fact that `reload::Handle::reload` doesn't work with `Filtered` in the docs somewhere, should I add that? ## Motivation Changing the filter of a `Filtered` at runtime is currently only possible by replacing it with a new `Filtered` via the `reload::Handle::reload` method. This currently doesn't work as the new `Filtered` won't receive a `FilterId` (see #1629). While it would be desirable to just make that work, it would only be possible via a breaking change (according to Eliza) so this seems like a reasonable (and easy) workaround for now. (I can't judge whether this method is only useful as a workaround for the bug or if it suits the public API independently) ## Solution Offer mutable access to the `Filtered::filter` field in the public API. This can be used via the `reload::Handle::modify` method to change the filter inside the existing `Filtered`. Fixes #1629
- Loading branch information
1 parent
601bf67
commit f2ea356
Showing
4 changed files
with
853 additions
and
152 deletions.
There are no files selected for viewing
380 changes: 380 additions & 0 deletions
380
tracing-subscriber/src/filter/subscriber_filters/combinator.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,380 @@ | ||
//! Filter combinators | ||
use crate::subscribe::{Context, Filter}; | ||
use std::{cmp, fmt, marker::PhantomData}; | ||
use tracing_core::{collect::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::subscribe::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::subscribe::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::or`] method. See that | ||
/// method's documentation for details. | ||
/// | ||
/// [`Filter`]: crate::subscribe::Filter | ||
/// [`FilterExt::or`]: crate::filter::FilterExt::or | ||
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::subscriber().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::subscribe::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()) | ||
} | ||
} | ||
|
||
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::subscriber().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::subscriber().with_filter(filter)) | ||
/// .init(); | ||
/// ``` | ||
/// | ||
/// [`Filter`]: crate::subscribe::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()?)) | ||
} | ||
} | ||
|
||
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. | ||
/// | ||
/// [`Filter`]: crate::subscribe::Filter | ||
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 | ||
} | ||
} | ||
|
||
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() | ||
} | ||
} |
Oops, something went wrong.