Skip to content

Commit acb078f

Browse files
authored
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 `Collect::event_enabled` method, taking an `Event` and returning `bool`. This is called before the `Collect::event` method, and if it returns `false`, `Collect::event` is not called. For simple collectors (e.g. not using `Subscribe` layers), the `event_enabled` method is not particulary necessary, as the collector can just skip the `event` call. However, this branch also adds a new `Subscribe::event_enabled` method, with the signature: ```rust fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, C>) -> bool; ``` This is called before `Subscribe::on_event`, and if `event_enabled` returns `false`, `on_event` is not called (nor is `Collect::event`). This allows filter subscribers to filter out an `Event` based on its fields. Closes #2007
1 parent 201d21a commit acb078f

File tree

7 files changed

+203
-1
lines changed

7 files changed

+203
-1
lines changed

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

+26
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ use core::ptr::NonNull;
5757
/// See also the [documentation on the callsite registry][cs-reg] for details
5858
/// on [`register_callsite`].
5959
///
60+
/// - [`event_enabled`] is called once before every call to the [`event`]
61+
/// method. This can be used to implement filtering on events once their field
62+
/// values are known, but before any processing is done in the `event` method.
6063
/// - [`clone_span`] is called every time a span ID is cloned, and [`try_close`]
6164
/// is called when a span ID is dropped. By default, these functions do
6265
/// nothing. However, they can be used to implement reference counting for
@@ -72,6 +75,8 @@ use core::ptr::NonNull;
7275
/// [`clone_span`]: Collect::clone_span
7376
/// [`try_close`]: Collect::try_close
7477
/// [cs-reg]: crate::callsite#registering-callsites
78+
/// [`event`]: Collect::event
79+
/// [`event_enabled`]: Collect::event_enabled
7580
pub trait Collect: 'static {
7681
// === Span registry methods ==============================================
7782

@@ -288,6 +293,17 @@ pub trait Collect: 'static {
288293
/// follow from _b_), it may silently do nothing.
289294
fn record_follows_from(&self, span: &span::Id, follows: &span::Id);
290295

296+
/// Determine if an [`Event`] should be recorded.
297+
///
298+
/// By default, this returns `true` and collectors can filter events in
299+
/// [`event`][Self::event] without any penalty. However, when `event` is
300+
/// more complicated, this can be used to determine if `event` should be
301+
/// called at all, separating out the decision from the processing.
302+
fn event_enabled(&self, event: &Event<'_>) -> bool {
303+
let _ = event;
304+
true
305+
}
306+
291307
/// Records that an [`Event`] has occurred.
292308
///
293309
/// This method will be invoked when an Event is constructed by
@@ -688,6 +704,11 @@ where
688704
self.as_ref().record_follows_from(span, follows)
689705
}
690706

707+
#[inline]
708+
fn event_enabled(&self, event: &Event<'_>) -> bool {
709+
self.as_ref().event_enabled(event)
710+
}
711+
691712
#[inline]
692713
fn event(&self, event: &Event<'_>) {
693714
self.as_ref().event(event)
@@ -762,6 +783,11 @@ where
762783
self.as_ref().record_follows_from(span, follows)
763784
}
764785

786+
#[inline]
787+
fn event_enabled(&self, event: &Event<'_>) -> bool {
788+
self.as_ref().event_enabled(event)
789+
}
790+
765791
#[inline]
766792
fn event(&self, event: &Event<'_>) {
767793
self.as_ref().event(event)

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,10 @@ impl Dispatch {
682682
/// [`event`]: super::collect::Collect::event
683683
#[inline]
684684
pub fn event(&self, event: &Event<'_>) {
685-
self.collector().event(event)
685+
let collector = self.collector();
686+
if collector.event_enabled(event) {
687+
collector.event(event);
688+
}
686689
}
687690

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

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

+5
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@ where
393393
self.inner.record_follows_from(span, follows)
394394
}
395395

396+
#[inline]
397+
fn event_enabled(&self, event: &Event<'_>) -> bool {
398+
self.inner.event_enabled(event)
399+
}
400+
396401
#[inline]
397402
fn event(&self, event: &Event<'_>) {
398403
self.inner.event(event);

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

+5
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ where
106106
try_lock!(self.inner.read()).on_follows_from(span, follows, ctx)
107107
}
108108

109+
#[inline]
110+
fn event_enabled(&self, event: &Event<'_>, ctx: subscribe::Context<'_, C>) -> bool {
111+
try_lock!(self.inner.read(), else return false).event_enabled(event, ctx)
112+
}
113+
109114
#[inline]
110115
fn on_event(&self, event: &Event<'_>, ctx: subscribe::Context<'_, C>) {
111116
try_lock!(self.inner.read()).on_event(event, ctx)

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

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

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

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

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

+61
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,28 @@
417417
//! [`Interest::never()`] from its [`register_callsite`] method, filter
418418
//! evaluation will short-circuit and the span or event will be disabled.
419419
//!
420+
//! ### Enabling Interest
421+
//!
422+
//! Whenever an tracing event (or span) is emitted, it goes through a number of
423+
//! steps to determine how and how much it should be processed. The earlier an
424+
//! event is disabled, the less work has to be done to process the event, so
425+
//! subscribers that implement filtering should attempt to disable unwanted
426+
//! events as early as possible. In order, each event checks:
427+
//!
428+
//! - [`register_callsite`], once per callsite (roughly: once per time that
429+
//! `event!` or `span!` is written in the source code; this is cached at the
430+
//! callsite). See [`Collect::register_callsite`] and
431+
//! [`tracing_core::callsite`] for a summary of how this behaves.
432+
//! - [`enabled`], once per emitted event (roughly: once per time that `event!`
433+
//! or `span!` is *executed*), and only if `register_callsite` regesters an
434+
//! [`Interest::sometimes`]. This is the main customization point to globally
435+
//! filter events based on their [`Metadata`]. If an event can be disabled
436+
//! based only on [`Metadata`], it should be, as this allows the construction
437+
//! of the actual `Event`/`Span` to be skipped.
438+
//! - For events only (and not spans), [`event_enabled`] is called just before
439+
//! processing the event. This gives subscribers one last chance to say that
440+
//! an event should be filtered out, now that the event's fields are known.
441+
//!
420442
//! ## Per-Subscriber Filtering
421443
//!
422444
//! **Note**: per-subscriber filtering APIs currently require the [`"registry"` crate
@@ -639,6 +661,7 @@
639661
//! [the current span]: Context::current_span
640662
//! [`register_callsite`]: Subscribe::register_callsite
641663
//! [`enabled`]: Subscribe::enabled
664+
//! [`event_enabled`]: Subscribe::event_enabled
642665
//! [`on_enter`]: Subscribe::on_enter
643666
//! [`Subscribe::register_callsite`]: Subscribe::register_callsite
644667
//! [`Subscribe::enabled`]: Subscribe::enabled
@@ -827,6 +850,31 @@ where
827850
// seems like a good future-proofing measure as it may grow other methods later...
828851
fn on_follows_from(&self, _span: &span::Id, _follows: &span::Id, _ctx: Context<'_, C>) {}
829852

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

@@ -1455,6 +1503,14 @@ where
14551503
}
14561504
}
14571505

1506+
#[inline]
1507+
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, C>) -> bool {
1508+
match self {
1509+
Some(ref inner) => inner.event_enabled(event, ctx),
1510+
None => false,
1511+
}
1512+
}
1513+
14581514
#[inline]
14591515
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, C>) {
14601516
if let Some(ref inner) = self {
@@ -1534,6 +1590,11 @@ macro_rules! subscriber_impl_body {
15341590
self.deref().on_follows_from(span, follows, ctx)
15351591
}
15361592

1593+
#[inline]
1594+
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, C>) -> bool {
1595+
self.deref().event_enabled(event, ctx)
1596+
}
1597+
15371598
#[inline]
15381599
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, C>) {
15391600
self.deref().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::{collect::with_default, Collect, Event, Metadata};
5+
use tracing_subscriber::{prelude::*, registry, subscribe::Context, Subscribe};
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> Subscribe<C> for TrackingLayer
15+
where
16+
C: Collect + 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 collector = 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(collector, || {
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 collector = 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(collector, || {
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 collector = 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(collector, || {
77+
tracing::error!("hiya!");
78+
});
79+
80+
assert_eq!(0, *count.lock().unwrap());
81+
}

0 commit comments

Comments
 (0)