From d2ad8ab4f2ba42668d20c4548882538d7bcd54da Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Mon, 25 Jul 2022 18:08:05 +0100 Subject: [PATCH] subscriber: add `Targets::default_level` method (#2242) ## Motivation This makes it possible to fully "override" some base `Targets` filter with another (e.g. user-supplied) filter. Without some way to obtain the default, only explicit targets can be overridden (via `IntoIter` and friends). See also https://github.com/tokio-rs/tracing/issues/1790#issuecomment-999739222 ## Solution We can add a method to `Targets` that filters the underlying `DirectiveSet` for the default directive. This works because `DirectiveSet::add` will replace directives with the same `target`/`field_names`, which is always `None`/`vec![]` for the directive added by `with_default` (and in fact we are only concerned with `target`, since no other `Targets` API allows adding directives with a `None` target). Ideally the method would be named `default`, corresponding to `with_default`, however this conflicts with `Default::default` and so would be a breaking change (and harm ergonomics). `default_level` seemed a better name than `get_default`, since "getters" of this style are generally considered unidiomatic[citation needed]. Example usage: ```rust let mut filter = Targets::new().with_target("some_module", LevelFilter::INFO); // imagine this came from `RUST_LOG` or similar let override: Targets = "trace".parse().unwrap(); // merge the override if let Some(default) = override.default_level() { filter = filter.with_default(default); } for (target, level) in override.iter() { filter = filter.with_target(target, level); } ``` Closes #1790 --- tracing-subscriber/src/filter/targets.rs | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tracing-subscriber/src/filter/targets.rs b/tracing-subscriber/src/filter/targets.rs index 2a30d2db60..e1407114b5 100644 --- a/tracing-subscriber/src/filter/targets.rs +++ b/tracing-subscriber/src/filter/targets.rs @@ -277,6 +277,62 @@ impl Targets { self } + /// Returns the default level for this filter, if one is set. + /// + /// The default level is used to filter any spans or events with targets + /// that do not match any of the configured set of prefixes. + /// + /// The default level can be set for a filter either by using + /// [`with_default`](Self::with_default) or when parsing from a filter string that includes a + /// level without a target (e.g. `"trace"`). + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::filter::{LevelFilter, Targets}; + /// + /// let filter = Targets::new().with_default(LevelFilter::INFO); + /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + /// + /// let filter: Targets = "info".parse().unwrap(); + /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + /// ``` + /// + /// The default level is `None` if no default is set: + /// + /// ``` + /// use tracing_subscriber::filter::Targets; + /// + /// let filter = Targets::new(); + /// assert_eq!(filter.default_level(), None); + /// + /// let filter: Targets = "my_crate=info".parse().unwrap(); + /// assert_eq!(filter.default_level(), None); + /// ``` + /// + /// Note that an unset default level (`None`) behaves like [`LevelFilter::OFF`] when the filter is + /// used, but it could also be set explicitly which may be useful to distinguish (such as when + /// merging multiple `Targets`). + /// + /// ``` + /// use tracing_subscriber::filter::{LevelFilter, Targets}; + /// + /// let filter = Targets::new().with_default(LevelFilter::OFF); + /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + /// + /// let filter: Targets = "off".parse().unwrap(); + /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + /// ``` + pub fn default_level(&self) -> Option { + self.0.directives().into_iter().find_map(|d| { + if d.target.is_none() { + Some(d.level) + } else { + None + } + }) + } + /// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter. /// /// The order of iteration is undefined. @@ -685,6 +741,21 @@ mod tests { ); } + #[test] + fn targets_default_level() { + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); + assert_eq!(filter.default_level(), None); + + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::OFF); + assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::OFF) + .with_default(LevelFilter::INFO); + assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + } + #[test] // `println!` is only available with `libstd`. #[cfg(feature = "std")]