Skip to content

Commit b6f7bbd

Browse files
committed
subscriber: increase EnvFilter test coverage
With a view to replacing the env filter parsing in #3243, this change adds some additional tests to improve our confidence in not breaking existing behavior. Tests for empty and invalid directives is added, as well as tests for directives overriding less specific directives. One of the latter tests (`more_specific_dynamic_filter_less_verbose`) currently fails, which is a known issue reported in #1388. The test is in place with `#[should_panic]` and can be reverted to a normal test when that behavior is fixed. The documentation on `parse`, `parse_lossy`, `from_env_lossy` and `try_from_env` has also been made more explicit as to the result of an empty directive (the default directive is used). This was already documented on `with_default_directive`.
1 parent dfc2c8b commit b6f7bbd

File tree

2 files changed

+232
-0
lines changed

2 files changed

+232
-0
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ impl Builder {
133133

134134
/// Returns a new [`EnvFilter`] from the directives in the given string,
135135
/// *ignoring* any that are invalid.
136+
///
137+
/// If `parse_lossy` is called with an empty string, then the
138+
/// [default directive] is used instead.
139+
///
140+
/// [default directive]: Self::with_default_directive
136141
pub fn parse_lossy<S: AsRef<str>>(&self, dirs: S) -> EnvFilter {
137142
let directives = dirs
138143
.as_ref()
@@ -150,6 +155,11 @@ impl Builder {
150155

151156
/// Returns a new [`EnvFilter`] from the directives in the given string,
152157
/// or an error if any are invalid.
158+
///
159+
/// If `parse` is called with an empty string, then the [default directive]
160+
/// is used instead.
161+
///
162+
/// [default directive]: Self::with_default_directive
153163
pub fn parse<S: AsRef<str>>(&self, dirs: S) -> Result<EnvFilter, directive::ParseError> {
154164
let dirs = dirs.as_ref();
155165
if dirs.is_empty() {
@@ -165,6 +175,11 @@ impl Builder {
165175

166176
/// Returns a new [`EnvFilter`] from the directives in the configured
167177
/// environment variable, ignoring any directives that are invalid.
178+
///
179+
/// If the environment variable is empty, then the [default directive]
180+
/// is used instead.
181+
///
182+
/// [default directive]: Self::with_default_directive
168183
pub fn from_env_lossy(&self) -> EnvFilter {
169184
let var = env::var(self.env_var_name()).unwrap_or_default();
170185
self.parse_lossy(var)
@@ -174,6 +189,11 @@ impl Builder {
174189
/// environment variable. If the environment variable is unset, no directive is added.
175190
///
176191
/// An error is returned if the environment contains invalid directives.
192+
///
193+
/// If the environment variable is empty, then the [default directive]
194+
/// is used instead.
195+
///
196+
/// [default directive]: Self::with_default_directive
177197
pub fn from_env(&self) -> Result<EnvFilter, FromEnvError> {
178198
let var = env::var(self.env_var_name()).unwrap_or_default();
179199
self.parse(var).map_err(Into::into)
@@ -182,6 +202,11 @@ impl Builder {
182202
/// Returns a new [`EnvFilter`] from the directives in the configured
183203
/// environment variable, or an error if the environment variable is not set
184204
/// or contains invalid directives.
205+
///
206+
/// If the environment variable is empty, then the [default directive]
207+
/// is used instead.
208+
///
209+
/// [default directive]: Self::with_default_directive
185210
pub fn try_from_env(&self) -> Result<EnvFilter, FromEnvError> {
186211
let var = env::var(self.env_var_name())?;
187212
self.parse(var).map_err(Into::into)

tracing-subscriber/tests/env_filter/main.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,210 @@ fn method_name_resolution() {
195195
let filter = EnvFilter::new("hello_world=info");
196196
filter.max_level_hint();
197197
}
198+
199+
#[test]
200+
fn parse_invalid_string() {
201+
assert!(EnvFilter::builder().parse(",!").is_err());
202+
}
203+
204+
#[test]
205+
fn parse_empty_string_no_default_directive() {
206+
let filter = EnvFilter::builder().parse("").expect("filter should parse");
207+
let (collector, finished) = collector::mock().only().run_with_handle();
208+
let subscriber = collector.with(filter);
209+
210+
with_default(subscriber, || {
211+
tracing::trace!("this should be disabled");
212+
tracing::debug!("this should be disabled");
213+
tracing::info!("this should be disabled");
214+
tracing::warn!("this should be disabled");
215+
tracing::error!("this should be disabled");
216+
});
217+
218+
finished.assert_finished();
219+
}
220+
221+
#[test]
222+
fn parse_empty_string_with_default_directive() {
223+
let filter = EnvFilter::builder()
224+
.with_default_directive(LevelFilter::INFO.into())
225+
.parse("")
226+
.expect("filter should parse");
227+
let (collector, finished) = collector::mock()
228+
.event(expect::event().at_level(Level::INFO))
229+
.event(expect::event().at_level(Level::WARN))
230+
.event(expect::event().at_level(Level::ERROR))
231+
.only()
232+
.run_with_handle();
233+
let subscriber = collector.with(filter);
234+
235+
with_default(subscriber, || {
236+
tracing::trace!("this should be disabled");
237+
tracing::debug!("this should be disabled");
238+
tracing::info!("this shouldn't be disabled");
239+
tracing::warn!("this shouldn't be disabled");
240+
tracing::error!("this shouldn't be disabled");
241+
});
242+
243+
finished.assert_finished();
244+
}
245+
246+
#[test]
247+
fn new_invalid_string() {
248+
let filter = EnvFilter::new(",!");
249+
let (collector, finished) = collector::mock()
250+
.event(expect::event().at_level(Level::ERROR))
251+
.only()
252+
.run_with_handle();
253+
let subscriber = collector.with(filter);
254+
255+
with_default(subscriber, || {
256+
tracing::trace!("this should be disabled");
257+
tracing::debug!("this should be disabled");
258+
tracing::info!("this should be disabled");
259+
tracing::warn!("this should be disabled");
260+
tracing::error!("this shouldn't be disabled");
261+
});
262+
263+
finished.assert_finished();
264+
}
265+
266+
#[test]
267+
fn new_empty_string() {
268+
let filter = EnvFilter::new("");
269+
let (collector, finished) = collector::mock()
270+
.event(expect::event().at_level(Level::ERROR))
271+
.only()
272+
.run_with_handle();
273+
let subscriber = collector.with(filter);
274+
275+
with_default(subscriber, || {
276+
tracing::trace!("this should be disabled");
277+
tracing::debug!("this should be disabled");
278+
tracing::info!("this should be disabled");
279+
tracing::warn!("this should be disabled");
280+
tracing::error!("this shouldn't be disabled");
281+
});
282+
283+
finished.assert_finished();
284+
}
285+
286+
#[test]
287+
fn more_specific_static_filter_more_verbose() {
288+
let filter = EnvFilter::new("info,hello=debug");
289+
let (collector, finished) = collector::mock()
290+
.event(expect::event().at_level(Level::INFO))
291+
.event(expect::event().at_level(Level::DEBUG).with_target("hello"))
292+
.only()
293+
.run_with_handle();
294+
let subscriber = collector.with(filter);
295+
296+
with_default(subscriber, || {
297+
tracing::info!("should be enabled");
298+
tracing::debug!("should be disabled");
299+
tracing::debug!(target: "hello", "should be enabled");
300+
});
301+
302+
finished.assert_finished();
303+
}
304+
305+
#[test]
306+
fn more_specific_static_filter_less_verbose() {
307+
let filter = EnvFilter::new("info,hello=warn");
308+
let (collector, finished) = collector::mock()
309+
.event(expect::event().at_level(Level::INFO))
310+
.event(
311+
expect::event()
312+
.at_level(Level::WARN)
313+
.with_target("env_filter"),
314+
)
315+
.only()
316+
.run_with_handle();
317+
let subscriber = collector.with(filter);
318+
319+
with_default(subscriber, || {
320+
tracing::info!("should be enabled");
321+
tracing::warn!("should be enabled");
322+
tracing::info!(target: "hello", "should be disabled");
323+
});
324+
325+
finished.assert_finished();
326+
}
327+
328+
#[test]
329+
fn more_specific_dynamic_filter_more_verbose() {
330+
let filter = EnvFilter::new("info,[{hello=4}]=debug");
331+
let (collector, finished) = collector::mock()
332+
.new_span(expect::span().at_level(Level::INFO))
333+
.drop_span("enabled info")
334+
.new_span(
335+
expect::span()
336+
.at_level(Level::DEBUG)
337+
.with_fields(expect::field("hello").with_value(&4_u64)),
338+
)
339+
.drop_span("enabled debug")
340+
.event(expect::event().with_fields(expect::msg("marker")))
341+
.only()
342+
.run_with_handle();
343+
let subscriber = collector.with(filter);
344+
345+
with_default(subscriber, || {
346+
tracing::info_span!("enabled info");
347+
tracing::debug_span!("disabled debug");
348+
tracing::debug_span!("enabled debug", hello = &4_u64);
349+
350+
// .only() doesn't work when we don't enter/exit spans
351+
tracing::info!("marker");
352+
});
353+
354+
finished.assert_finished();
355+
}
356+
357+
/// This is a negative test. This functionality should work, but doesn't.
358+
///
359+
/// If an improvement to `EnvFilter` fixes this test, then the `#[should_panic]`
360+
/// can be removed and the test kept as it is. If the test requires some sort of
361+
/// modification, then care should be taken.
362+
///
363+
/// Fixing this test would resolve https://github.com/tokio-rs/tracing/issues/1388
364+
/// (and probably a few more issues as well).
365+
#[test]
366+
#[should_panic(
367+
expected = "[more_specific_dynamic_filter_less_verbose] expected a new span \
368+
at level `Level(Warn)`,\n[more_specific_dynamic_filter_less_verbose] but \
369+
got one at level `Level(Info)` instead."
370+
)]
371+
fn more_specific_dynamic_filter_less_verbose() {
372+
let filter = EnvFilter::new("info,[{hello=4}]=warn");
373+
let (collector, finished) = collector::mock()
374+
.new_span(expect::span().at_level(Level::INFO))
375+
.drop_span("enabled info")
376+
.new_span(
377+
expect::span()
378+
.at_level(Level::WARN)
379+
.with_fields(expect::field("hello").with_value(&100_u64)),
380+
)
381+
.drop_span("enabled hello=100 warn")
382+
.new_span(
383+
expect::span()
384+
.at_level(Level::WARN)
385+
.with_fields(expect::field("hello").with_value(&4_u64)),
386+
)
387+
.drop_span("enabled hello=4 warn")
388+
.event(expect::event().with_fields(expect::msg("marker")))
389+
.only()
390+
.run_with_handle();
391+
let subscriber = collector.with(filter);
392+
393+
with_default(subscriber, || {
394+
tracing::info_span!("enabled info");
395+
tracing::warn_span!("enabled hello=100 warn", hello = &100_u64);
396+
tracing::info_span!("disabled hello=4 info", hello = &4_u64);
397+
tracing::warn_span!("enabled hello=4 warn", hello = &4_u64);
398+
399+
// .only() doesn't work when we don't enter/exit spans
400+
tracing::info!("marker");
401+
});
402+
403+
finished.assert_finished();
404+
}

0 commit comments

Comments
 (0)