Skip to content

Commit ffe4eb4

Browse files
CAD97hawkw
authored andcommitted
subscriber: add Filter::event_enabled (#2245)
## Motivation Like for `Subscriber` and `Layer`, allow per-layer `Filter`s to filter based on event fields. ## Solution Add `Filter::event_enabled`, plumb it through the combinator implementations, and call it from `Filtered`. The bit I'm the least confident about is the check in `Registry`'s implementation, but I *think* it matches what `event` is doing and everything seems to function correctly.
1 parent f1db358 commit ffe4eb4

File tree

6 files changed

+151
-0
lines changed

6 files changed

+151
-0
lines changed

tracing-subscriber/src/filter/layer_filters/combinator.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ where
137137
cmp::min(self.a.max_level_hint(), self.b.max_level_hint())
138138
}
139139

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+
140145
#[inline]
141146
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
142147
self.a.on_new_span(attrs, id, ctx.clone());
@@ -324,6 +329,11 @@ where
324329
Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?))
325330
}
326331

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+
327337
#[inline]
328338
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
329339
self.a.on_new_span(attrs, id, ctx.clone());
@@ -393,7 +403,16 @@ where
393403
/// If the wrapped filter would enable a span or event, it will be disabled. If
394404
/// it would disable a span or event, that span or event will be enabled.
395405
///
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+
/// implementing that method is optional, and filters which do not implement
409+
/// filtering on event field values will return `true` even for events that their
410+
/// [`enabled`] method would disable.
411+
///
396412
/// [`Filter`]: crate::layer::Filter
413+
/// [`enabled`]: crate::layer::Filter::enabled
414+
/// [`event_enabled`]: crate::layer::Filter::event_enabled
415+
/// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled
397416
pub(crate) fn new(a: A) -> Self {
398417
Self { a, _s: PhantomData }
399418
}
@@ -421,6 +440,14 @@ where
421440
None
422441
}
423442

443+
#[inline]
444+
fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
445+
// Never disable based on event_enabled; we "disabled" it in `enabled`,
446+
// so the `not` has already been applied and filtered this not out.
447+
let _ = (event, cx);
448+
true
449+
}
450+
424451
#[inline]
425452
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
426453
self.a.on_new_span(attrs, id, ctx);

tracing-subscriber/src/filter/layer_filters/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,17 @@ pub trait FilterExt<S>: layer::Filter<S> {
298298

299299
/// Inverts `self`, returning a filter that enables spans and events only if
300300
/// `self` would *not* enable them.
301+
///
302+
/// This inverts the values returned by the [`enabled`] and [`callsite_enabled`]
303+
/// methods on the wrapped filter; it does *not* invert [`event_enabled`], as
304+
/// implementing that method is optional, and filters which do not implement
305+
/// filtering on event field values will return `true` even for events that their
306+
/// [`enabled`] method would disable.
307+
///
308+
/// [`Filter`]: crate::subscribe::Filter
309+
/// [`enabled`]: crate::subscribe::Filter::enabled
310+
/// [`event_enabled`]: crate::subscribe::Filter::event_enabled
311+
/// [`callsite_enabled`]: crate::subscribe::Filter::callsite_enabled
301312
fn not(self) -> combinator::Not<Self, S>
302313
where
303314
Self: Sized,
@@ -643,6 +654,22 @@ where
643654
}
644655
}
645656

657+
fn event_enabled(&self, event: &Event<'_>, cx: Context<'_, S>) -> bool {
658+
let cx = cx.with_filter(self.id());
659+
let enabled = FILTERING
660+
.with(|filtering| filtering.and(self.id(), || self.filter.event_enabled(event, &cx)));
661+
662+
if enabled {
663+
// If the filter enabled this event, ask the wrapped subscriber if
664+
// _it_ wants it --- it might have a global filter.
665+
self.layer.event_enabled(event, cx)
666+
} else {
667+
// Otherwise, return `true`. See the comment in `enabled` for why this
668+
// is necessary.
669+
true
670+
}
671+
}
672+
646673
fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) {
647674
self.did_enable(|| {
648675
self.layer.on_event(event, cx.with_filter(self.id()));
@@ -1014,6 +1041,14 @@ impl FilterState {
10141041
}
10151042
}
10161043

1044+
/// Run a second filtering pass, e.g. for Subscribe::event_enabled.
1045+
fn and(&self, filter: FilterId, f: impl FnOnce() -> bool) -> bool {
1046+
let map = self.enabled.get();
1047+
let enabled = map.is_enabled(filter) && f();
1048+
self.enabled.set(map.set(filter, enabled));
1049+
enabled
1050+
}
1051+
10171052
/// Clears the current in-progress filter state.
10181053
///
10191054
/// This resets the [`FilterMap`] and current [`Interest`] as well as

tracing-subscriber/src/layer/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,26 @@ feature! {
13521352
Interest::sometimes()
13531353
}
13541354

1355+
/// Called before the filtered [`Layer]'s [`on_event`], to determine if
1356+
/// `on_event` should be called.
1357+
///
1358+
/// This gives a chance to filter events based on their fields. Note,
1359+
/// however, that this *does not* override [`enabled`], and is not even
1360+
/// called if [`enabled`] returns `false`.
1361+
///
1362+
/// ## Default Implementation
1363+
///
1364+
/// By default, this method returns `true`, indicating that no events are
1365+
/// filtered out based on their fields.
1366+
///
1367+
/// [`enabled`]: crate::layer::Filter::enabled
1368+
/// [`on_event`]: crate::layer::Layer::on_event
1369+
#[inline] // collapse this to a constant please mrs optimizer
1370+
fn event_enabled(&self, event: &Event<'_>, cx: &Context<'_, S>) -> bool {
1371+
let _ = (event, cx);
1372+
true
1373+
}
1374+
13551375
/// Returns an optional hint of the highest [verbosity level][level] that
13561376
/// this `Filter` will enable.
13571377
///

tracing-subscriber/src/registry/sharded.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,13 @@ impl Subscriber for Registry {
275275

276276
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {}
277277

278+
fn event_enabled(&self, _event: &Event<'_>) -> bool {
279+
if self.has_per_layer_filters() {
280+
return FilterState::event_enabled();
281+
}
282+
true
283+
}
284+
278285
/// This is intentionally not implemented, as recording events
279286
/// is the responsibility of layers atop of this registry.
280287
fn event(&self, _: &Event<'_>) {}

tracing-subscriber/tests/layer_filters/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use self::support::*;
55
mod boxed;
66
mod downcast_raw;
77
mod filter_scopes;
8+
mod per_event;
89
mod targets;
910
mod trees;
1011
mod vec;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use crate::support::*;
2+
use tracing::Level;
3+
use tracing_subscriber::{field::Visit, layer::Filter, prelude::*};
4+
5+
struct FilterEvent;
6+
7+
impl<S> Filter<S> for FilterEvent {
8+
fn enabled(
9+
&self,
10+
_meta: &tracing::Metadata<'_>,
11+
_cx: &tracing_subscriber::layer::Context<'_, S>,
12+
) -> bool {
13+
true
14+
}
15+
16+
fn event_enabled(
17+
&self,
18+
event: &tracing::Event<'_>,
19+
_cx: &tracing_subscriber::layer::Context<'_, S>,
20+
) -> bool {
21+
struct ShouldEnable(bool);
22+
impl Visit for ShouldEnable {
23+
fn record_bool(&mut self, field: &tracing_core::Field, value: bool) {
24+
if field.name() == "enable" {
25+
self.0 = value;
26+
}
27+
}
28+
29+
fn record_debug(
30+
&mut self,
31+
_field: &tracing_core::Field,
32+
_value: &dyn core::fmt::Debug,
33+
) {
34+
}
35+
}
36+
let mut should_enable = ShouldEnable(false);
37+
event.record(&mut should_enable);
38+
should_enable.0
39+
}
40+
}
41+
42+
#[test]
43+
fn per_subscriber_event_field_filtering() {
44+
let (expect, handle) = layer::mock()
45+
.event(event::mock().at_level(Level::TRACE))
46+
.event(event::mock().at_level(Level::INFO))
47+
.done()
48+
.run_with_handle();
49+
50+
let _subscriber = tracing_subscriber::registry()
51+
.with(expect.with_filter(FilterEvent))
52+
.set_default();
53+
54+
tracing::trace!(enable = true, "hello trace");
55+
tracing::debug!("hello debug");
56+
tracing::info!(enable = true, "hello info");
57+
tracing::warn!(enable = false, "hello warn");
58+
tracing::error!("hello error");
59+
60+
handle.assert_finished();
61+
}

0 commit comments

Comments
 (0)