Skip to content

Commit ed1c104

Browse files
tfreiberg-fastlyhawkw
authored andcommitted
subscriber: impl Filter for EnvFilter (tokio-rs#1983)
Filtering by span and field requires using `EnvFilter` rather than `Targets`. Per-layer filtering requires the `Filter` trait, which `EnvFilter` does not implement. Implement the `Filter` trait for `EnvFilter`. PR tokio-rs#1973 adds additiional methods to the `Filter` trait, which are necessary for `EnvFilter` to implement dynamic span filtering. Now that those methods are added, we can provide a `Filter` impl for `EnvFilter`. In addition, we changed the globally-scoped `thread_local!` macro to use a `ThreadLocal` struct as a field, so that multiple `EnvFilter`s used as per-layer filters don't share a single span scope. Fixes tokio-rs#1868 Follow-up on tokio-rs#1973 Co-authored-by: Eliza Weisman <[email protected]>
1 parent 36056db commit ed1c104

File tree

3 files changed

+341
-2
lines changed

3 files changed

+341
-2
lines changed

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,35 @@ impl EnvFilter {
586586
return;
587587
}
588588

589-
let mut spans = try_lock!(self.by_id.write());
590-
spans.remove(&id);
589+
#[inline]
590+
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
591+
self.register_callsite(meta)
592+
}
593+
594+
#[inline]
595+
fn max_level_hint(&self) -> Option<LevelFilter> {
596+
EnvFilter::max_level_hint(self)
597+
}
598+
599+
#[inline]
600+
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
601+
self.on_new_span(attrs, id)
602+
}
603+
604+
#[inline]
605+
fn on_enter(&self, id: &span::Id, _: Context<'_, S>) {
606+
self.on_enter(id);
607+
}
608+
609+
#[inline]
610+
fn on_exit(&self, id: &span::Id, _: Context<'_, S>) {
611+
self.on_exit(id);
612+
}
613+
614+
#[inline]
615+
fn on_close(&self, id: span::Id, _: Context<'_, S>) {
616+
self.on_close(id);
617+
}
591618
}
592619

593620
/// Informs the filter that the span with the provided `id` recorded the
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
//! Tests for using `EnvFilter` as a per-layer filter (rather than a global
2+
//! `Layer` filter).
3+
#![cfg(feature = "registry")]
4+
use super::*;
5+
6+
#[test]
7+
fn level_filter_event() {
8+
let filter: EnvFilter = "info".parse().expect("filter should parse");
9+
let (layer, handle) = layer::mock()
10+
.event(event::mock().at_level(Level::INFO))
11+
.event(event::mock().at_level(Level::WARN))
12+
.event(event::mock().at_level(Level::ERROR))
13+
.done()
14+
.run_with_handle();
15+
16+
let _subscriber = tracing_subscriber::registry()
17+
.with(layer.with_filter(filter))
18+
.set_default();
19+
20+
tracing::trace!("this should be disabled");
21+
tracing::info!("this shouldn't be");
22+
tracing::debug!(target: "foo", "this should also be disabled");
23+
tracing::warn!(target: "foo", "this should be enabled");
24+
tracing::error!("this should be enabled too");
25+
26+
handle.assert_finished();
27+
}
28+
29+
#[test]
30+
fn same_name_spans() {
31+
let filter: EnvFilter = "[foo{bar}]=trace,[foo{baz}]=trace"
32+
.parse()
33+
.expect("filter should parse");
34+
let (layer, handle) = layer::mock()
35+
.new_span(
36+
span::mock()
37+
.named("foo")
38+
.at_level(Level::TRACE)
39+
.with_field(field::mock("bar")),
40+
)
41+
.new_span(
42+
span::mock()
43+
.named("foo")
44+
.at_level(Level::TRACE)
45+
.with_field(field::mock("baz")),
46+
)
47+
.done()
48+
.run_with_handle();
49+
50+
let _subscriber = tracing_subscriber::registry()
51+
.with(layer.with_filter(filter))
52+
.set_default();
53+
54+
tracing::trace_span!("foo", bar = 1);
55+
tracing::trace_span!("foo", baz = 1);
56+
57+
handle.assert_finished();
58+
}
59+
60+
#[test]
61+
fn level_filter_event_with_target() {
62+
let filter: EnvFilter = "info,stuff=debug".parse().expect("filter should parse");
63+
let (layer, handle) = layer::mock()
64+
.event(event::mock().at_level(Level::INFO))
65+
.event(event::mock().at_level(Level::DEBUG).with_target("stuff"))
66+
.event(event::mock().at_level(Level::WARN).with_target("stuff"))
67+
.event(event::mock().at_level(Level::ERROR))
68+
.event(event::mock().at_level(Level::ERROR).with_target("stuff"))
69+
.done()
70+
.run_with_handle();
71+
72+
let _subscriber = tracing_subscriber::registry()
73+
.with(layer.with_filter(filter))
74+
.set_default();
75+
76+
tracing::trace!("this should be disabled");
77+
tracing::info!("this shouldn't be");
78+
tracing::debug!(target: "stuff", "this should be enabled");
79+
tracing::debug!("but this shouldn't");
80+
tracing::trace!(target: "stuff", "and neither should this");
81+
tracing::warn!(target: "stuff", "this should be enabled");
82+
tracing::error!("this should be enabled too");
83+
tracing::error!(target: "stuff", "this should be enabled also");
84+
85+
handle.assert_finished();
86+
}
87+
88+
#[test]
89+
fn level_filter_event_with_target_and_span() {
90+
let filter: EnvFilter = "stuff[cool_span]=debug"
91+
.parse()
92+
.expect("filter should parse");
93+
94+
let cool_span = span::named("cool_span");
95+
let (layer, handle) = layer::mock()
96+
.enter(cool_span.clone())
97+
.event(
98+
event::mock()
99+
.at_level(Level::DEBUG)
100+
.in_scope(vec![cool_span.clone()]),
101+
)
102+
.exit(cool_span)
103+
.done()
104+
.run_with_handle();
105+
106+
let _subscriber = tracing_subscriber::registry()
107+
.with(layer.with_filter(filter))
108+
.set_default();
109+
110+
{
111+
let _span = tracing::info_span!(target: "stuff", "cool_span").entered();
112+
tracing::debug!("this should be enabled");
113+
}
114+
115+
tracing::debug!("should also be disabled");
116+
117+
{
118+
let _span = tracing::info_span!("uncool_span").entered();
119+
tracing::debug!("this should be disabled");
120+
}
121+
122+
handle.assert_finished();
123+
}
124+
125+
#[test]
126+
fn not_order_dependent() {
127+
// this test reproduces tokio-rs/tracing#623
128+
129+
let filter: EnvFilter = "stuff=debug,info".parse().expect("filter should parse");
130+
let (layer, finished) = layer::mock()
131+
.event(event::mock().at_level(Level::INFO))
132+
.event(event::mock().at_level(Level::DEBUG).with_target("stuff"))
133+
.event(event::mock().at_level(Level::WARN).with_target("stuff"))
134+
.event(event::mock().at_level(Level::ERROR))
135+
.event(event::mock().at_level(Level::ERROR).with_target("stuff"))
136+
.done()
137+
.run_with_handle();
138+
139+
let _subscriber = tracing_subscriber::registry()
140+
.with(layer.with_filter(filter))
141+
.set_default();
142+
143+
tracing::trace!("this should be disabled");
144+
tracing::info!("this shouldn't be");
145+
tracing::debug!(target: "stuff", "this should be enabled");
146+
tracing::debug!("but this shouldn't");
147+
tracing::trace!(target: "stuff", "and neither should this");
148+
tracing::warn!(target: "stuff", "this should be enabled");
149+
tracing::error!("this should be enabled too");
150+
tracing::error!(target: "stuff", "this should be enabled also");
151+
152+
finished.assert_finished();
153+
}
154+
155+
#[test]
156+
fn add_directive_enables_event() {
157+
// this test reproduces tokio-rs/tracing#591
158+
159+
// by default, use info level
160+
let mut filter = EnvFilter::new(LevelFilter::INFO.to_string());
161+
162+
// overwrite with a more specific directive
163+
filter = filter.add_directive("hello=trace".parse().expect("directive should parse"));
164+
165+
let (layer, finished) = layer::mock()
166+
.event(event::mock().at_level(Level::INFO).with_target("hello"))
167+
.event(event::mock().at_level(Level::TRACE).with_target("hello"))
168+
.done()
169+
.run_with_handle();
170+
171+
let _subscriber = tracing_subscriber::registry()
172+
.with(layer.with_filter(filter))
173+
.set_default();
174+
175+
tracing::info!(target: "hello", "hello info");
176+
tracing::trace!(target: "hello", "hello trace");
177+
178+
finished.assert_finished();
179+
}
180+
181+
#[test]
182+
fn span_name_filter_is_dynamic() {
183+
let filter: EnvFilter = "info,[cool_span]=debug"
184+
.parse()
185+
.expect("filter should parse");
186+
let cool_span = span::named("cool_span");
187+
let uncool_span = span::named("uncool_span");
188+
let (layer, finished) = layer::mock()
189+
.event(event::mock().at_level(Level::INFO))
190+
.enter(cool_span.clone())
191+
.event(
192+
event::mock()
193+
.at_level(Level::DEBUG)
194+
.in_scope(vec![cool_span.clone()]),
195+
)
196+
.enter(uncool_span.clone())
197+
.event(
198+
event::mock()
199+
.at_level(Level::WARN)
200+
.in_scope(vec![uncool_span.clone()]),
201+
)
202+
.event(
203+
event::mock()
204+
.at_level(Level::DEBUG)
205+
.in_scope(vec![uncool_span.clone()]),
206+
)
207+
.exit(uncool_span.clone())
208+
.exit(cool_span)
209+
.enter(uncool_span.clone())
210+
.event(
211+
event::mock()
212+
.at_level(Level::WARN)
213+
.in_scope(vec![uncool_span.clone()]),
214+
)
215+
.event(
216+
event::mock()
217+
.at_level(Level::ERROR)
218+
.in_scope(vec![uncool_span.clone()]),
219+
)
220+
.exit(uncool_span)
221+
.done()
222+
.run_with_handle();
223+
224+
let _subscriber = tracing_subscriber::registry()
225+
.with(layer.with_filter(filter))
226+
.set_default();
227+
228+
tracing::trace!("this should be disabled");
229+
tracing::info!("this shouldn't be");
230+
let cool_span = tracing::info_span!("cool_span");
231+
let uncool_span = tracing::info_span!("uncool_span");
232+
233+
{
234+
let _enter = cool_span.enter();
235+
tracing::debug!("i'm a cool event");
236+
tracing::trace!("i'm cool, but not cool enough");
237+
let _enter2 = uncool_span.enter();
238+
tracing::warn!("warning: extremely cool!");
239+
tracing::debug!("i'm still cool");
240+
}
241+
242+
{
243+
let _enter = uncool_span.enter();
244+
tracing::warn!("warning: not that cool");
245+
tracing::trace!("im not cool enough");
246+
tracing::error!("uncool error");
247+
}
248+
249+
finished.assert_finished();
250+
}
251+
252+
#[test]
253+
fn multiple_dynamic_filters() {
254+
// Test that multiple dynamic (span) filters only apply to the layers
255+
// they're attached to.
256+
let (layer1, handle1) = {
257+
let span = span::named("span1");
258+
let filter: EnvFilter = "[span1]=debug".parse().expect("filter 1 should parse");
259+
let (layer, handle) = layer::named("layer1")
260+
.enter(span.clone())
261+
.event(
262+
event::mock()
263+
.at_level(Level::DEBUG)
264+
.in_scope(vec![span.clone()]),
265+
)
266+
.exit(span)
267+
.done()
268+
.run_with_handle();
269+
(layer.with_filter(filter), handle)
270+
};
271+
272+
let (layer2, handle2) = {
273+
let span = span::named("span2");
274+
let filter: EnvFilter = "[span2]=info".parse().expect("filter 2 should parse");
275+
let (layer, handle) = layer::named("layer2")
276+
.enter(span.clone())
277+
.event(
278+
event::mock()
279+
.at_level(Level::INFO)
280+
.in_scope(vec![span.clone()]),
281+
)
282+
.exit(span)
283+
.done()
284+
.run_with_handle();
285+
(layer.with_filter(filter), handle)
286+
};
287+
288+
let _subscriber = tracing_subscriber::registry()
289+
.with(layer1)
290+
.with(layer2)
291+
.set_default();
292+
293+
tracing::info_span!("span1").in_scope(|| {
294+
tracing::debug!("hello from span 1");
295+
tracing::trace!("not enabled");
296+
});
297+
298+
tracing::info_span!("span2").in_scope(|| {
299+
tracing::info!("hello from span 2");
300+
tracing::debug!("not enabled");
301+
});
302+
303+
handle1.assert_finished();
304+
handle2.assert_finished();
305+
}

tracing/tests/support/span.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ pub fn mock() -> MockSpan {
2424
}
2525
}
2626

27+
pub fn named<I>(name: I) -> MockSpan
28+
where
29+
I: Into<String>,
30+
{
31+
mock().named(name)
32+
}
33+
2734
impl MockSpan {
2835
pub fn named<I>(self, name: I) -> Self
2936
where

0 commit comments

Comments
 (0)