Skip to content

Commit a06c85f

Browse files
CAD97hawkw
authored andcommitted
core, subscriber: add event_enabled to filter events on fields (#2008)
## Motivation Allow filter layers to filter on the contents of events (see #2007). ## Solution This branch adds a new `Subscriber::event_enabled` method, taking an `Event` and returning `bool`. This is called before the `Subscriber::event` method, and if it returns `false`, `Subscriber::event` is not called. For simple subscriber (e.g. not using `Layer`s), the `event_enabled` method is not particulary necessary, as the subscriber can just skip the `event` call. However, this branch also adds a new `Layer::event_enabled` method, with the signature: ```rust fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool; ``` This is called before `Layer::on_event`, and if `event_enabled` returns `false`, `on_event` is not called (nor is `Subscriber::event`). This allows filter `Layer`s to filter out an `Event` based on its fields. Closes #2007
1 parent 36ca66c commit a06c85f

File tree

7 files changed

+206
-1
lines changed

7 files changed

+206
-1
lines changed

Diff for: tracing-core/src/dispatcher.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,9 @@ impl Dispatch {
511511
/// [`event`]: super::subscriber::Subscriber::event
512512
#[inline]
513513
pub fn event(&self, event: &Event<'_>) {
514-
self.subscriber.event(event)
514+
if self.subscriber.event_enabled(event) {
515+
self.subscriber.event(event);
516+
}
515517
}
516518

517519
/// Records that a span has been can_enter.

Diff for: tracing-core/src/subscriber.rs

+26
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ use crate::stdlib::{
6060
/// See also the [documentation on the callsite registry][cs-reg] for details
6161
/// on [`register_callsite`].
6262
///
63+
/// - [`event_enabled`] is called once before every call to the [`event`]
64+
/// method. This can be used to implement filtering on events once their field
65+
/// values are known, but before any processing is done in the `event` method.
6366
/// - [`clone_span`] is called every time a span ID is cloned, and [`try_close`]
6467
/// is called when a span ID is dropped. By default, these functions do
6568
/// nothing. However, they can be used to implement reference counting for
@@ -75,6 +78,8 @@ use crate::stdlib::{
7578
/// [`clone_span`]: Subscriber::clone_span
7679
/// [`try_close`]: Subscriber::try_close
7780
/// [cs-reg]: crate::callsite#registering-callsites
81+
/// [`event`]: Subscriber::event
82+
/// [`event_enabled`]: Subscriber::event_enabled
7883
pub trait Subscriber: 'static {
7984
// === Span registry methods ==============================================
8085

@@ -291,6 +296,17 @@ pub trait Subscriber: 'static {
291296
/// follow from _b_), it may silently do nothing.
292297
fn record_follows_from(&self, span: &span::Id, follows: &span::Id);
293298

299+
/// Determine if an [`Event`] should be recorded.
300+
///
301+
/// By default, this returns `true` and `Subscriber`s can filter events in
302+
/// [`event`][Self::event] without any penalty. However, when `event` is
303+
/// more complicated, this can be used to determine if `event` should be
304+
/// called at all, separating out the decision from the processing.
305+
fn event_enabled(&self, event: &Event<'_>) -> bool {
306+
let _ = event;
307+
true
308+
}
309+
294310
/// Records that an [`Event`] has occurred.
295311
///
296312
/// This method will be invoked when an Event is constructed by
@@ -696,6 +712,11 @@ where
696712
self.as_ref().record_follows_from(span, follows)
697713
}
698714

715+
#[inline]
716+
fn event_enabled(&self, event: &Event<'_>) -> bool {
717+
self.as_ref().event_enabled(event)
718+
}
719+
699720
#[inline]
700721
fn event(&self, event: &Event<'_>) {
701722
self.as_ref().event(event)
@@ -776,6 +797,11 @@ where
776797
self.as_ref().record_follows_from(span, follows)
777798
}
778799

800+
#[inline]
801+
fn event_enabled(&self, event: &Event<'_>) -> bool {
802+
self.as_ref().event_enabled(event)
803+
}
804+
779805
#[inline]
780806
fn event(&self, event: &Event<'_>) {
781807
self.as_ref().event(event)

Diff for: tracing-subscriber/src/fmt/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,11 @@ where
397397
self.inner.record_follows_from(span, follows)
398398
}
399399

400+
#[inline]
401+
fn event_enabled(&self, event: &Event<'_>) -> bool {
402+
self.inner.event_enabled(event)
403+
}
404+
400405
#[inline]
401406
fn event(&self, event: &Event<'_>) {
402407
self.inner.event(event);

Diff for: tracing-subscriber/src/layer/layered.rs

+21
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ where
139139
self.layer.on_follows_from(span, follows, self.ctx());
140140
}
141141

142+
fn event_enabled(&self, event: &Event<'_>) -> bool {
143+
if self.layer.event_enabled(event, self.ctx()) {
144+
// if the outer layer enables the event, ask the inner subscriber.
145+
self.inner.event_enabled(event)
146+
} else {
147+
// otherwise, the event is disabled by this layer
148+
false
149+
}
150+
}
151+
142152
fn event(&self, event: &Event<'_>) {
143153
self.inner.event(event);
144154
self.layer.on_event(event, self.ctx());
@@ -278,6 +288,17 @@ where
278288
self.layer.on_follows_from(span, follows, ctx);
279289
}
280290

291+
#[inline]
292+
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
293+
if self.layer.event_enabled(event, ctx.clone()) {
294+
// if the outer layer enables the event, ask the inner subscriber.
295+
self.inner.event_enabled(event, ctx)
296+
} else {
297+
// otherwise, the event is disabled by this layer
298+
false
299+
}
300+
}
301+
281302
#[inline]
282303
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
283304
self.inner.on_event(event, ctx.clone());

Diff for: tracing-subscriber/src/layer/mod.rs

+65
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,28 @@
415415
//! [`Interest::never()`] from its [`register_callsite`] method, filter
416416
//! evaluation will short-circuit and the span or event will be disabled.
417417
//!
418+
//! ### Enabling Interest
419+
//!
420+
//! Whenever an tracing event (or span) is emitted, it goes through a number of
421+
//! steps to determine how and how much it should be processed. The earlier an
422+
//! event is disabled, the less work has to be done to process the event, so
423+
//! `Layer`s that implement filtering should attempt to disable unwanted
424+
//! events as early as possible. In order, each event checks:
425+
//!
426+
//! - [`register_callsite`], once per callsite (roughly: once per time that
427+
//! `event!` or `span!` is written in the source code; this is cached at the
428+
//! callsite). See [`Subscriber::register_callsite`] and
429+
//! [`tracing_core::callsite`] for a summary of how this behaves.
430+
//! - [`enabled`], once per emitted event (roughly: once per time that `event!`
431+
//! or `span!` is *executed*), and only if `register_callsite` regesters an
432+
//! [`Interest::sometimes`]. This is the main customization point to globally
433+
//! filter events based on their [`Metadata`]. If an event can be disabled
434+
//! based only on [`Metadata`], it should be, as this allows the construction
435+
//! of the actual `Event`/`Span` to be skipped.
436+
//! - For events only (and not spans), [`event_enabled`] is called just before
437+
//! processing the event. This gives layers one last chance to say that
438+
//! an event should be filtered out, now that the event's fields are known.
439+
//!
418440
//! ## Per-Layer Filtering
419441
//!
420442
//! **Note**: per-layer filtering APIs currently require the [`"registry"` crate
@@ -634,6 +656,7 @@
634656
//! [the current span]: Context::current_span
635657
//! [`register_callsite`]: Layer::register_callsite
636658
//! [`enabled`]: Layer::enabled
659+
//! [`event_enabled`]: Layer::event_enabled
637660
//! [`on_enter`]: Layer::on_enter
638661
//! [`Layer::register_callsite`]: Layer::register_callsite
639662
//! [`Layer::enabled`]: Layer::enabled
@@ -832,6 +855,31 @@ where
832855
// seems like a good future-proofing measure as it may grow other methods later...
833856
fn on_follows_from(&self, _span: &span::Id, _follows: &span::Id, _ctx: Context<'_, S>) {}
834857

858+
/// Called before [`on_event`], to determine if `on_event` should be called.
859+
///
860+
/// <div class="example-wrap" style="display:inline-block">
861+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
862+
///
863+
/// **Note**: This method determines whether an event is globally enabled,
864+
/// *not* whether the individual `Layer` will be notified about the
865+
/// event. This is intended to be used by `Layer`s that implement
866+
/// filtering for the entire stack. `Layer`s which do not wish to be
867+
/// notified about certain events but do not wish to globally disable them
868+
/// should ignore those events in their [on_event][Self::on_event].
869+
///
870+
/// </pre></div>
871+
///
872+
/// See [the trait-level documentation] for more information on filtering
873+
/// with `Layer`s.
874+
///
875+
/// [`on_event`]: Self::on_event
876+
/// [`Interest`]: tracing_core::Interest
877+
/// [the trait-level documentation]: #filtering-with-layers
878+
#[inline] // collapse this to a constant please mrs optimizer
879+
fn event_enabled(&self, _event: &Event<'_>, _ctx: Context<'_, S>) -> bool {
880+
true
881+
}
882+
835883
/// Notifies this layer that an event has occurred.
836884
fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {}
837885

@@ -1460,6 +1508,14 @@ where
14601508
}
14611509
}
14621510

1511+
#[inline]
1512+
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
1513+
match self {
1514+
Some(ref inner) => inner.event_enabled(event, ctx),
1515+
None => false,
1516+
}
1517+
}
1518+
14631519
#[inline]
14641520
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
14651521
if let Some(ref inner) = self {
@@ -1548,6 +1604,11 @@ feature! {
15481604
self.deref().on_follows_from(span, follows, ctx)
15491605
}
15501606

1607+
#[inline]
1608+
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
1609+
self.deref().event_enabled(event, ctx)
1610+
}
1611+
15511612
#[inline]
15521613
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
15531614
self.deref().on_event(event, ctx)
@@ -1629,6 +1690,10 @@ feature! {
16291690
self.iter().all(|l| l.enabled(metadata, ctx.clone()))
16301691
}
16311692

1693+
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
1694+
self.iter().all(|l| l.event_enabled(event, ctx.clone()))
1695+
}
1696+
16321697
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
16331698
for l in self {
16341699
l.on_new_span(attrs, id, ctx.clone());

Diff for: tracing-subscriber/src/reload.rs

+5
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ where
134134
try_lock!(self.inner.read()).on_follows_from(span, follows, ctx)
135135
}
136136

137+
#[inline]
138+
fn event_enabled(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) -> bool {
139+
try_lock!(self.inner.read(), else return false).event_enabled(event, ctx)
140+
}
141+
137142
#[inline]
138143
fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) {
139144
try_lock!(self.inner.read()).on_event(event, ctx)

Diff for: tracing-subscriber/tests/event_enabling.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#![cfg(feature = "registry")]
2+
3+
use std::sync::{Arc, Mutex};
4+
use tracing::{subscriber::with_default, Event, Metadata, Subscriber};
5+
use tracing_subscriber::{layer::Context, prelude::*, registry, Layer};
6+
7+
struct TrackingLayer {
8+
enabled: bool,
9+
event_enabled_count: Arc<Mutex<usize>>,
10+
event_enabled: bool,
11+
on_event_count: Arc<Mutex<usize>>,
12+
}
13+
14+
impl<C> Layer<C> for TrackingLayer
15+
where
16+
C: Subscriber + Send + Sync + 'static,
17+
{
18+
fn enabled(&self, _metadata: &Metadata<'_>, _ctx: Context<'_, C>) -> bool {
19+
self.enabled
20+
}
21+
22+
fn event_enabled(&self, _event: &Event<'_>, _ctx: Context<'_, C>) -> bool {
23+
*self.event_enabled_count.lock().unwrap() += 1;
24+
self.event_enabled
25+
}
26+
27+
fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, C>) {
28+
*self.on_event_count.lock().unwrap() += 1;
29+
}
30+
}
31+
32+
#[test]
33+
fn event_enabled_is_only_called_once() {
34+
let event_enabled_count = Arc::new(Mutex::default());
35+
let count = event_enabled_count.clone();
36+
let subscriber = registry().with(TrackingLayer {
37+
enabled: true,
38+
event_enabled_count,
39+
event_enabled: true,
40+
on_event_count: Arc::new(Mutex::default()),
41+
});
42+
with_default(subscriber, || {
43+
tracing::error!("hiya!");
44+
});
45+
46+
assert_eq!(1, *count.lock().unwrap());
47+
}
48+
49+
#[test]
50+
fn event_enabled_not_called_when_not_enabled() {
51+
let event_enabled_count = Arc::new(Mutex::default());
52+
let count = event_enabled_count.clone();
53+
let subscriber = registry().with(TrackingLayer {
54+
enabled: false,
55+
event_enabled_count,
56+
event_enabled: true,
57+
on_event_count: Arc::new(Mutex::default()),
58+
});
59+
with_default(subscriber, || {
60+
tracing::error!("hiya!");
61+
});
62+
63+
assert_eq!(0, *count.lock().unwrap());
64+
}
65+
66+
#[test]
67+
fn event_disabled_does_disable_event() {
68+
let on_event_count = Arc::new(Mutex::default());
69+
let count = on_event_count.clone();
70+
let subscriber = registry().with(TrackingLayer {
71+
enabled: true,
72+
event_enabled_count: Arc::new(Mutex::default()),
73+
event_enabled: false,
74+
on_event_count,
75+
});
76+
with_default(subscriber, || {
77+
tracing::error!("hiya!");
78+
});
79+
80+
assert_eq!(0, *count.lock().unwrap());
81+
}

0 commit comments

Comments
 (0)