//! A [filter] that enables or disables spans and events based on their [target] and [level]. //! //! See [`Targets`] for details. //! //! [target]: tracing_core::Metadata::target //! [level]: tracing_core::Level //! [filter]: crate::layer#filtering-with-layers use crate::{ filter::{ directive::{DirectiveSet, ParseError, StaticDirective}, LevelFilter, }, layer, }; #[cfg(not(feature = "std"))] use alloc::string::String; use core::{ iter::{Extend, FilterMap, FromIterator}, slice, str::FromStr, }; use tracing_core::{Interest, Metadata, Subscriber}; /// A filter that enables or disables spans and events based on their [target] /// and [level]. /// /// Targets are typically equal to the Rust module path of the code where the /// span or event was recorded, although they may be overridden. /// /// This type can be used for both [per-layer filtering][plf] (using its /// [`Filter`] implementation) and [global filtering][global] (using its /// [`Layer`] implementation). /// /// See the [documentation on filtering with layers][filtering] for details. /// /// # Filtering With `Targets` /// /// A `Targets` filter consists of one or more [target] prefixes, paired with /// [`LevelFilter`]s. If a span or event's [target] begins with one of those /// prefixes, and its [level] is at or below the [`LevelFilter`] enabled for /// that prefix, then the span or event will be enabled. /// /// This is similar to the behavior implemented by the [`env_logger` crate] in /// the `log` ecosystem. /// /// The [`EnvFilter`] type also provided by this crate is very similar to `Targets`, /// but is capable of a more sophisticated form of filtering where events may /// also be enabled or disabled based on the span they are recorded in. /// `Targets` can be thought of as a lighter-weight form of [`EnvFilter`] that /// can be used instead when this dynamic filtering is not required. /// /// # Examples /// /// A `Targets` filter can be constructed by programmatically adding targets and /// levels to enable: /// /// ``` /// use tracing_subscriber::{filter, prelude::*}; /// use tracing_core::Level; /// /// let filter = filter::Targets::new() /// // Enable the `INFO` level for anything in `my_crate` /// .with_target("my_crate", Level::INFO) /// // Enable the `DEBUG` level for a specific module. /// .with_target("my_crate::interesting_module", Level::DEBUG); /// /// // Build a new subscriber with the `fmt` layer using the `Targets` /// // filter we constructed above. /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer()) /// .with(filter) /// .init(); /// ``` /// /// [`LevelFilter::OFF`] can be used to disable a particular target: /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// // Disable all traces from `annoying_module`. /// .with_target("my_crate::annoying_module", LevelFilter::OFF); /// # drop(filter); /// ``` /// /// Alternatively, `Targets` implements [`std::str::FromStr`], allowing it to be /// parsed from a comma-delimited list of `target=level` pairs. For example: /// /// ```rust /// # fn main() -> Result<(), Box> { /// use tracing_subscriber::filter; /// use tracing_core::Level; /// /// let filter = "my_crate=info,my_crate::interesting_module=trace,other_crate=debug" /// .parse::()?; /// /// // The parsed filter is identical to a filter constructed using `with_target`: /// assert_eq!( /// filter, /// filter::Targets::new() /// .with_target("my_crate", Level::INFO) /// .with_target("my_crate::interesting_module", Level::TRACE) /// .with_target("other_crate", Level::DEBUG) /// ); /// # Ok(()) } /// ``` /// /// This is particularly useful when the list of enabled targets is configurable /// by the user at runtime. /// /// The `Targets` filter can be used as a [per-layer filter][plf] *and* as a /// [global filter]: /// /// ```rust /// use tracing_subscriber::{ /// fmt, /// filter::{Targets, LevelFilter}, /// prelude::*, /// }; /// use tracing_core::Level; /// use std::{sync::Arc, fs::File}; /// # fn docs() -> Result<(), Box> { /// /// // A layer that logs events to stdout using the human-readable "pretty" /// // format. /// let stdout_log = fmt::layer().pretty(); /// /// // A layer that logs events to a file, using the JSON format. /// let file = File::create("debug_log.json")?; /// let debug_log = fmt::layer() /// .with_writer(Arc::new(file)) /// .json(); /// /// tracing_subscriber::registry() /// // Only log INFO and above to stdout, unless the span or event /// // has the `my_crate::cool_module` target prefix. /// .with(stdout_log /// .with_filter( /// Targets::default() /// .with_target("my_crate::cool_module", Level::DEBUG) /// .with_default(Level::INFO) /// ) /// ) /// // Log everything enabled by the global filter to `debug_log.json`. /// .with(debug_log) /// // Configure a global filter for the whole subscriber stack. This will /// // control what spans and events are recorded by both the `debug_log` /// // and the `stdout_log` layers, and `stdout_log` will *additionally* be /// // filtered by its per-layer filter. /// .with( /// Targets::default() /// .with_target("my_crate", Level::TRACE) /// .with_target("other_crate", Level::INFO) /// .with_target("other_crate::annoying_module", LevelFilter::OFF) /// .with_target("third_crate", Level::DEBUG) /// ).init(); /// # Ok(()) } ///``` /// /// [target]: tracing_core::Metadata::target /// [level]: tracing_core::Level /// [`Filter`]: crate::layer::Filter /// [`Layer`]: crate::layer::Layer /// [plf]: crate::layer#per-layer-filtering /// [global]: crate::layer#global-filtering /// [filtering]: crate::layer#filtering-with-layers /// [`env_logger` crate]: https://docs.rs/env_logger/0.9.0/env_logger/index.html#enabling-logging /// [`EnvFilter`]: crate::filter::EnvFilter #[derive(Debug, Default, Clone, PartialEq)] pub struct Targets(DirectiveSet); impl Targets { /// Returns a new `Targets` filter. /// /// This filter will enable no targets. Call [`with_target`] or [`with_targets`] /// to add enabled targets, and [`with_default`] to change the default level /// enabled for spans and events that didn't match any of the provided targets. /// /// [`with_target`]: Targets::with_target /// [`with_targets`]: Targets::with_targets /// [`with_default`]: Targets::with_default pub fn new() -> Self { Self::default() } /// Enables spans and events with [target]s starting with the provided target /// prefix if they are at or below the provided [`LevelFilter`]. /// /// # Examples /// /// ``` /// use tracing_subscriber::filter; /// use tracing_core::Level; /// /// let filter = filter::Targets::new() /// // Enable the `INFO` level for anything in `my_crate` /// .with_target("my_crate", Level::INFO) /// // Enable the `DEBUG` level for a specific module. /// .with_target("my_crate::interesting_module", Level::DEBUG); /// # drop(filter); /// ``` /// /// [`LevelFilter::OFF`] can be used to disable a particular target: /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// // Disable all traces from `annoying_module`. /// .with_target("my_crate::interesting_module", LevelFilter::OFF); /// # drop(filter); /// ``` /// /// [target]: tracing_core::Metadata::target pub fn with_target(mut self, target: impl Into, level: impl Into) -> Self { self.0.add(StaticDirective::new( Some(target.into()), Default::default(), level.into(), )); self } /// Adds [target]s from an iterator of [target]-[`LevelFilter`] pairs to this filter. /// /// # Examples /// /// ``` /// use tracing_subscriber::filter; /// use tracing_core::Level; /// /// let filter = filter::Targets::new() /// .with_targets(vec![ /// ("my_crate", Level::INFO), /// ("my_crate::some_module", Level::DEBUG), /// ("my_crate::other_module::cool_stuff", Level::TRACE), /// ("other_crate", Level::WARN) /// ]); /// # drop(filter); /// ``` /// /// [`LevelFilter::OFF`] can be used to disable a particular target: /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// // Disable all traces from `annoying_module`. /// .with_target("my_crate::interesting_module", LevelFilter::OFF); /// # drop(filter); /// ``` /// /// [target]: tracing_core::Metadata::target pub fn with_targets(mut self, targets: impl IntoIterator) -> Self where String: From, LevelFilter: From, { self.extend(targets); self } /// Sets the default level to enable for spans and events whose targets did /// not match any of the configured prefixes. /// /// By default, this is [`LevelFilter::OFF`]. This means that spans and /// events will only be enabled if they match one of the configured target /// prefixes. If this is changed to a different [`LevelFilter`], spans and /// events with targets that did not match any of the configured prefixes /// will be enabled if their level is at or below the provided level. pub fn with_default(mut self, level: impl Into) -> Self { self.0 .add(StaticDirective::new(None, Default::default(), level.into())); self } /// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter. /// /// The order of iteration is undefined. /// /// # Examples /// /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// .with_target("my_crate::interesting_module", Level::DEBUG); /// /// let mut targets: Vec<_> = filter.iter().collect(); /// targets.sort(); /// /// assert_eq!(targets, vec![ /// ("my_crate", LevelFilter::INFO), /// ("my_crate::interesting_module", LevelFilter::DEBUG), /// ]); /// ``` /// /// [target]: tracing_core::Metadata::target pub fn iter(&self) -> Iter<'_> { self.into_iter() } #[inline] fn interested(&self, metadata: &'static Metadata<'static>) -> Interest { if self.0.enabled(metadata) { Interest::always() } else { Interest::never() } } } impl Extend<(T, L)> for Targets where T: Into, L: Into, { fn extend>(&mut self, iter: I) { let iter = iter.into_iter().map(|(target, level)| { StaticDirective::new(Some(target.into()), Default::default(), level.into()) }); self.0.extend(iter); } } impl FromIterator<(T, L)> for Targets where T: Into, L: Into, { fn from_iter>(iter: I) -> Self { let mut this = Self::default(); this.extend(iter); this } } impl FromStr for Targets { type Err = ParseError; fn from_str(s: &str) -> Result { s.split(',') .map(StaticDirective::from_str) .collect::>() .map(Self) } } impl layer::Layer for Targets where S: Subscriber, { fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { self.0.enabled(metadata) } fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { self.interested(metadata) } fn max_level_hint(&self) -> Option { Some(self.0.max_level) } } #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl layer::Filter for Targets { fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { self.0.enabled(metadata) } fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { self.interested(metadata) } fn max_level_hint(&self) -> Option { Some(self.0.max_level) } } impl IntoIterator for Targets { type Item = (String, LevelFilter); type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter::new(self) } } impl<'a> IntoIterator for &'a Targets { type Item = (&'a str, LevelFilter); type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { Iter::new(self) } } /// An owning iterator over the [target]-[level] pairs of a `Targets` filter. /// /// This struct is created by the `IntoIterator` trait implementation of [`Targets`]. /// /// # Examples /// /// Merge the targets from one `Targets` with another: /// /// ``` /// use tracing_subscriber::filter::Targets; /// use tracing_core::Level; /// /// let mut filter = Targets::new().with_target("my_crate", Level::INFO); /// let overrides = Targets::new().with_target("my_crate::interesting_module", Level::DEBUG); /// /// filter.extend(overrides); /// # drop(filter); /// ``` /// /// [target]: tracing_core::Metadata::target /// [level]: tracing_core::Level #[derive(Debug)] pub struct IntoIter( #[allow(clippy::type_complexity)] // alias indirection would probably make this more confusing FilterMap< as IntoIterator>::IntoIter, fn(StaticDirective) -> Option<(String, LevelFilter)>, >, ); impl IntoIter { fn new(targets: Targets) -> Self { Self(targets.0.into_iter().filter_map(|directive| { let level = directive.level; directive.target.map(|target| (target, level)) })) } } impl Iterator for IntoIter { type Item = (String, LevelFilter); fn next(&mut self) -> Option { self.0.next() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } /// A borrowing iterator over the [target]-[level] pairs of a `Targets` filter. /// /// This struct is created by [`iter`] method of [`Targets`], or from the `IntoIterator` /// implementation for `&Targets`. /// /// [target]: tracing_core::Metadata::target /// [level]: tracing_core::Level /// [`iter`]: Targets::iter #[derive(Debug)] pub struct Iter<'a>( FilterMap< slice::Iter<'a, StaticDirective>, fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>, >, ); impl<'a> Iter<'a> { fn new(targets: &'a Targets) -> Self { Self(targets.0.iter().filter_map(|directive| { directive .target .as_deref() .map(|target| (target, directive.level)) })) } } impl<'a> Iterator for Iter<'a> { type Item = (&'a str, LevelFilter); fn next(&mut self) -> Option { self.0.next() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } #[cfg(test)] mod tests { use super::*; feature! { #![not(feature = "std")] use alloc::{vec, vec::Vec, string::ToString}; // `dbg!` is only available with `libstd`; just nop it out when testing // with alloc only. macro_rules! dbg { ($x:expr) => { $x } } } fn expect_parse(s: &str) -> Targets { match dbg!(s).parse::() { Err(e) => panic!("string {:?} did not parse successfully: {}", s, e), Ok(e) => e, } } fn expect_parse_ralith(s: &str) { let dirs = expect_parse(s).0.into_vec(); assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("server".to_string())); assert_eq!(dirs[0].level, LevelFilter::DEBUG); assert_eq!(dirs[0].field_names, Vec::::new()); assert_eq!(dirs[1].target, Some("common".to_string())); assert_eq!(dirs[1].level, LevelFilter::INFO); assert_eq!(dirs[1].field_names, Vec::::new()); } fn expect_parse_level_directives(s: &str) { let dirs = expect_parse(s).0.into_vec(); assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::OFF); assert_eq!(dirs[0].field_names, Vec::::new()); assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string())); assert_eq!(dirs[1].level, LevelFilter::INFO); assert_eq!(dirs[1].field_names, Vec::::new()); assert_eq!(dirs[2].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[2].level, LevelFilter::WARN); assert_eq!(dirs[2].field_names, Vec::::new()); assert_eq!(dirs[3].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[3].level, LevelFilter::ERROR); assert_eq!(dirs[3].field_names, Vec::::new()); assert_eq!(dirs[4].target, Some("crate3".to_string())); assert_eq!(dirs[4].level, LevelFilter::TRACE); assert_eq!(dirs[4].field_names, Vec::::new()); assert_eq!(dirs[5].target, Some("crate2".to_string())); assert_eq!(dirs[5].level, LevelFilter::DEBUG); assert_eq!(dirs[5].field_names, Vec::::new()); } #[test] fn parse_ralith() { expect_parse_ralith("common=info,server=debug"); } #[test] fn parse_ralith_uc() { expect_parse_ralith("common=INFO,server=DEBUG"); } #[test] fn parse_ralith_mixed() { expect_parse("common=iNfo,server=dEbUg"); } #[test] fn expect_parse_valid() { let dirs = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .0 .into_vec(); assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[0].level, LevelFilter::TRACE); assert_eq!(dirs[0].field_names, Vec::::new()); assert_eq!(dirs[1].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[1].level, LevelFilter::ERROR); assert_eq!(dirs[1].field_names, Vec::::new()); assert_eq!(dirs[2].target, Some("crate3".to_string())); assert_eq!(dirs[2].level, LevelFilter::OFF); assert_eq!(dirs[2].field_names, Vec::::new()); assert_eq!(dirs[3].target, Some("crate2".to_string())); assert_eq!(dirs[3].level, LevelFilter::DEBUG); assert_eq!(dirs[3].field_names, Vec::::new()); } #[test] fn parse_level_directives() { expect_parse_level_directives( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off", ) } #[test] fn parse_uppercase_level_directives() { expect_parse_level_directives( "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", ) } #[test] fn parse_numeric_level_directives() { expect_parse_level_directives( "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\ crate3=5,crate3::mod2::mod1=0", ) } #[test] fn targets_iter() { let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .with_default(LevelFilter::WARN); let mut targets: Vec<_> = filter.iter().collect(); targets.sort(); assert_eq!( targets, vec![ ("crate1::mod1", LevelFilter::ERROR), ("crate1::mod2", LevelFilter::TRACE), ("crate2", LevelFilter::DEBUG), ("crate3", LevelFilter::OFF), ] ); } #[test] fn targets_into_iter() { let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .with_default(LevelFilter::WARN); let mut targets: Vec<_> = filter.into_iter().collect(); targets.sort(); assert_eq!( targets, vec![ ("crate1::mod1".to_string(), LevelFilter::ERROR), ("crate1::mod2".to_string(), LevelFilter::TRACE), ("crate2".to_string(), LevelFilter::DEBUG), ("crate3".to_string(), LevelFilter::OFF), ] ); } #[test] // `println!` is only available with `libstd`. #[cfg(feature = "std")] fn size_of_filters() { fn print_sz(s: &str) { let filter = s.parse::().expect("filter should parse"); println!( "size_of_val({:?})\n -> {}B", s, std::mem::size_of_val(&filter) ); } print_sz("info"); print_sz("foo=debug"); print_sz( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off", ); } }