diff options
Diffstat (limited to 'vendor/tracing-subscriber/src/filter')
-rw-r--r-- | vendor/tracing-subscriber/src/filter/directive.rs | 456 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/builder.rs | 325 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/directive.rs | 860 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/field.rs | 626 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/mod.rs | 991 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/filter_fn.rs | 749 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs | 542 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/layer_filters/mod.rs | 1220 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/level.rs | 27 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/mod.rs | 66 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/targets.rs | 781 |
11 files changed, 6643 insertions, 0 deletions
diff --git a/vendor/tracing-subscriber/src/filter/directive.rs b/vendor/tracing-subscriber/src/filter/directive.rs new file mode 100644 index 000000000..2ae3f0f24 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/directive.rs @@ -0,0 +1,456 @@ +use crate::filter::level::{self, LevelFilter}; +#[cfg(not(feature = "smallvec"))] +use alloc::vec; +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr}; +use tracing_core::{Level, Metadata}; +/// Indicates that a string could not be parsed as a filtering directive. +#[derive(Debug)] +pub struct ParseError { + kind: ParseErrorKind, +} + +/// A directive which will statically enable or disable a given callsite. +/// +/// Unlike a dynamic directive, this can be cached by the callsite. +#[derive(Debug, PartialEq, Eq, Clone)] +pub(crate) struct StaticDirective { + pub(in crate::filter) target: Option<String>, + pub(in crate::filter) field_names: Vec<String>, + pub(in crate::filter) level: LevelFilter, +} + +#[cfg(feature = "smallvec")] +pub(crate) type FilterVec<T> = smallvec::SmallVec<[T; 8]>; +#[cfg(not(feature = "smallvec"))] +pub(crate) type FilterVec<T> = Vec<T>; + +#[derive(Debug, PartialEq, Clone)] +pub(in crate::filter) struct DirectiveSet<T> { + directives: FilterVec<T>, + pub(in crate::filter) max_level: LevelFilter, +} + +pub(in crate::filter) trait Match { + fn cares_about(&self, meta: &Metadata<'_>) -> bool; + fn level(&self) -> &LevelFilter; +} + +#[derive(Debug)] +enum ParseErrorKind { + #[cfg(feature = "std")] + Field(Box<dyn std::error::Error + Send + Sync>), + Level(level::ParseError), + Other(Option<&'static str>), +} + +// === impl DirectiveSet === + +impl<T> DirectiveSet<T> { + #[cfg(feature = "env-filter")] + pub(crate) fn is_empty(&self) -> bool { + self.directives.is_empty() + } + + pub(crate) fn iter(&self) -> slice::Iter<'_, T> { + self.directives.iter() + } +} + +impl<T: Ord> Default for DirectiveSet<T> { + fn default() -> Self { + Self { + directives: FilterVec::new(), + max_level: LevelFilter::OFF, + } + } +} + +impl<T: Match + Ord> DirectiveSet<T> { + pub(crate) fn directives(&self) -> impl Iterator<Item = &T> { + self.directives.iter() + } + + pub(crate) fn directives_for<'a>( + &'a self, + metadata: &'a Metadata<'a>, + ) -> impl Iterator<Item = &'a T> + 'a { + self.directives().filter(move |d| d.cares_about(metadata)) + } + + pub(crate) fn add(&mut self, directive: T) { + // does this directive enable a more verbose level than the current + // max? if so, update the max level. + let level = *directive.level(); + if level > self.max_level { + self.max_level = level; + } + // insert the directive into the vec of directives, ordered by + // specificity (length of target + number of field filters). this + // ensures that, when finding a directive to match a span or event, we + // search the directive set in most specific first order. + match self.directives.binary_search(&directive) { + Ok(i) => self.directives[i] = directive, + Err(i) => self.directives.insert(i, directive), + } + } + + #[cfg(test)] + pub(in crate::filter) fn into_vec(self) -> FilterVec<T> { + self.directives + } +} + +impl<T: Match + Ord> FromIterator<T> for DirectiveSet<T> { + fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { + let mut this = Self::default(); + this.extend(iter); + this + } +} + +impl<T: Match + Ord> Extend<T> for DirectiveSet<T> { + fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { + for directive in iter.into_iter() { + self.add(directive); + } + } +} + +impl<T> IntoIterator for DirectiveSet<T> { + type Item = T; + + #[cfg(feature = "smallvec")] + type IntoIter = smallvec::IntoIter<[T; 8]>; + #[cfg(not(feature = "smallvec"))] + type IntoIter = vec::IntoIter<T>; + + fn into_iter(self) -> Self::IntoIter { + self.directives.into_iter() + } +} + +// === impl Statics === + +impl DirectiveSet<StaticDirective> { + pub(crate) fn enabled(&self, meta: &Metadata<'_>) -> bool { + let level = meta.level(); + match self.directives_for(meta).next() { + Some(d) => d.level >= *level, + None => false, + } + } + + /// Same as `enabled` above, but skips `Directive`'s with fields. + pub(crate) fn target_enabled(&self, target: &str, level: &Level) -> bool { + match self.directives_for_target(target).next() { + Some(d) => d.level >= *level, + None => false, + } + } + + pub(crate) fn directives_for_target<'a>( + &'a self, + target: &'a str, + ) -> impl Iterator<Item = &'a StaticDirective> + 'a { + self.directives() + .filter(move |d| d.cares_about_target(target)) + } +} + +// === impl StaticDirective === + +impl StaticDirective { + pub(in crate::filter) fn new( + target: Option<String>, + field_names: Vec<String>, + level: LevelFilter, + ) -> Self { + Self { + target, + field_names, + level, + } + } + + pub(in crate::filter) fn cares_about_target(&self, to_check: &str) -> bool { + // Does this directive have a target filter, and does it match the + // metadata's target? + if let Some(ref target) = self.target { + if !to_check.starts_with(&target[..]) { + return false; + } + } + + if !self.field_names.is_empty() { + return false; + } + + true + } +} + +impl Ord for StaticDirective { + fn cmp(&self, other: &StaticDirective) -> Ordering { + // We attempt to order directives by how "specific" they are. This + // ensures that we try the most specific directives first when + // attempting to match a piece of metadata. + + // First, we compare based on whether a target is specified, and the + // lengths of those targets if both have targets. + let ordering = self + .target + .as_ref() + .map(String::len) + .cmp(&other.target.as_ref().map(String::len)) + // Then we compare how many field names are matched by each directive. + .then_with(|| self.field_names.len().cmp(&other.field_names.len())) + // Finally, we fall back to lexicographical ordering if the directives are + // equally specific. Although this is no longer semantically important, + // we need to define a total ordering to determine the directive's place + // in the BTreeMap. + .then_with(|| { + self.target + .cmp(&other.target) + .then_with(|| self.field_names[..].cmp(&other.field_names[..])) + }) + .reverse(); + + #[cfg(debug_assertions)] + { + if ordering == Ordering::Equal { + debug_assert_eq!( + self.target, other.target, + "invariant violated: Ordering::Equal must imply a.target == b.target" + ); + debug_assert_eq!( + self.field_names, other.field_names, + "invariant violated: Ordering::Equal must imply a.field_names == b.field_names" + ); + } + } + + ordering + } +} + +impl PartialOrd for StaticDirective { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Match for StaticDirective { + fn cares_about(&self, meta: &Metadata<'_>) -> bool { + // Does this directive have a target filter, and does it match the + // metadata's target? + if let Some(ref target) = self.target { + if !meta.target().starts_with(&target[..]) { + return false; + } + } + + if meta.is_event() && !self.field_names.is_empty() { + let fields = meta.fields(); + for name in &self.field_names { + if fields.field(name).is_none() { + return false; + } + } + } + + true + } + + fn level(&self) -> &LevelFilter { + &self.level + } +} + +impl Default for StaticDirective { + fn default() -> Self { + StaticDirective { + target: None, + field_names: Vec::new(), + level: LevelFilter::ERROR, + } + } +} + +impl fmt::Display for StaticDirective { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut wrote_any = false; + if let Some(ref target) = self.target { + fmt::Display::fmt(target, f)?; + wrote_any = true; + } + + if !self.field_names.is_empty() { + f.write_str("[")?; + + let mut fields = self.field_names.iter(); + if let Some(field) = fields.next() { + write!(f, "{{{}", field)?; + for field in fields { + write!(f, ",{}", field)?; + } + f.write_str("}")?; + } + + f.write_str("]")?; + wrote_any = true; + } + + if wrote_any { + f.write_str("=")?; + } + + fmt::Display::fmt(&self.level, f) + } +} + +impl FromStr for StaticDirective { + type Err = ParseError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + // This method parses a filtering directive in one of the following + // forms: + // + // * `foo=trace` (TARGET=LEVEL) + // * `foo[{bar,baz}]=info` (TARGET[{FIELD,+}]=LEVEL) + // * `trace` (bare LEVEL) + // * `foo` (bare TARGET) + let mut split = s.split('='); + let part0 = split + .next() + .ok_or_else(|| ParseError::msg("string must not be empty"))?; + + // Directive includes an `=`: + // * `foo=trace` + // * `foo[{bar}]=trace` + // * `foo[{bar,baz}]=trace` + if let Some(part1) = split.next() { + if split.next().is_some() { + return Err(ParseError::msg( + "too many '=' in filter directive, expected 0 or 1", + )); + } + + let mut split = part0.split("[{"); + let target = split.next().map(String::from); + let mut field_names = Vec::new(); + // Directive includes fields: + // * `foo[{bar}]=trace` + // * `foo[{bar,baz}]=trace` + if let Some(maybe_fields) = split.next() { + if split.next().is_some() { + return Err(ParseError::msg( + "too many '[{' in filter directive, expected 0 or 1", + )); + } + + if !maybe_fields.ends_with("}]") { + return Err(ParseError::msg("expected fields list to end with '}]'")); + } + + let fields = maybe_fields + .trim_end_matches("}]") + .split(',') + .filter_map(|s| { + if s.is_empty() { + None + } else { + Some(String::from(s)) + } + }); + field_names.extend(fields); + }; + let level = part1.parse()?; + return Ok(Self { + level, + field_names, + target, + }); + } + + // Okay, the part after the `=` was empty, the directive is either a + // bare level or a bare target. + // * `foo` + // * `info` + Ok(match part0.parse::<LevelFilter>() { + Ok(level) => Self { + level, + target: None, + field_names: Vec::new(), + }, + Err(_) => Self { + target: Some(String::from(part0)), + level: LevelFilter::TRACE, + field_names: Vec::new(), + }, + }) + } +} + +// === impl ParseError === + +impl ParseError { + #[cfg(feature = "env-filter")] + pub(crate) fn new() -> Self { + ParseError { + kind: ParseErrorKind::Other(None), + } + } + + pub(crate) fn msg(s: &'static str) -> Self { + ParseError { + kind: ParseErrorKind::Other(Some(s)), + } + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + ParseErrorKind::Other(None) => f.pad("invalid filter directive"), + ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg), + ParseErrorKind::Level(ref l) => l.fmt(f), + #[cfg(feature = "std")] + ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseError { + fn description(&self) -> &str { + "invalid filter directive" + } + + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self.kind { + ParseErrorKind::Other(_) => None, + ParseErrorKind::Level(ref l) => Some(l), + ParseErrorKind::Field(ref n) => Some(n.as_ref()), + } + } +} + +#[cfg(feature = "std")] +impl From<Box<dyn std::error::Error + Send + Sync>> for ParseError { + fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self { + Self { + kind: ParseErrorKind::Field(e), + } + } +} + +impl From<level::ParseError> for ParseError { + fn from(l: level::ParseError) -> Self { + Self { + kind: ParseErrorKind::Level(l), + } + } +} diff --git a/vendor/tracing-subscriber/src/filter/env/builder.rs b/vendor/tracing-subscriber/src/filter/env/builder.rs new file mode 100644 index 000000000..c814707e6 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/env/builder.rs @@ -0,0 +1,325 @@ +use super::{ + directive::{self, Directive}, + EnvFilter, FromEnvError, +}; +use crate::sync::RwLock; +use std::env; +use thread_local::ThreadLocal; +use tracing::level_filters::STATIC_MAX_LEVEL; + +/// A [builder] for constructing new [`EnvFilter`]s. +/// +/// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html +#[derive(Debug, Clone)] +#[must_use] +pub struct Builder { + regex: bool, + env: Option<String>, + default_directive: Option<Directive>, +} + +impl Builder { + /// Sets whether span field values can be matched with regular expressions. + /// + /// If this is `true`, field filter directives will be interpreted as + /// regular expressions if they are not able to be interpreted as a `bool`, + /// `i64`, `u64`, or `f64` literal. If this is `false,` those field values + /// will be interpreted as literal [`std::fmt::Debug`] output instead. + /// + /// By default, regular expressions are enabled. + /// + /// **Note**: when [`EnvFilter`]s are constructed from untrusted inputs, + /// disabling regular expressions is strongly encouraged. + pub fn with_regex(self, regex: bool) -> Self { + Self { regex, ..self } + } + + /// Sets a default [filtering directive] that will be added to the filter if + /// the parsed string or environment variable contains no filter directives. + /// + /// By default, there is no default directive. + /// + /// # Examples + /// + /// If [`parse`], [`parse_lossy`], [`from_env`], or [`from_env_lossy`] are + /// called with an empty string or environment variable, the default + /// directive is used instead: + /// + /// ```rust + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// let filter = EnvFilter::builder() + /// .with_default_directive(LevelFilter::INFO.into()) + /// .parse("")?; + /// + /// assert_eq!(format!("{}", filter), "info"); + /// # Ok(()) } + /// ``` + /// + /// Note that the `lossy` variants ([`parse_lossy`] and [`from_env_lossy`]) + /// will ignore any invalid directives. If all directives in a filter + /// string or environment variable are invalid, those methods will also use + /// the default directive: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// let filter = EnvFilter::builder() + /// .with_default_directive(LevelFilter::INFO.into()) + /// .parse_lossy("some_target=fake level,foo::bar=lolwut"); + /// + /// assert_eq!(format!("{}", filter), "info"); + /// ``` + /// + /// + /// If the string or environment variable contains valid filtering + /// directives, the default directive is not used: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// let filter = EnvFilter::builder() + /// .with_default_directive(LevelFilter::INFO.into()) + /// .parse_lossy("foo=trace"); + /// + /// // The default directive is *not* used: + /// assert_eq!(format!("{}", filter), "foo=trace"); + /// ``` + /// + /// Parsing a more complex default directive from a string: + /// + /// ```rust + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// let default = "myapp=debug".parse() + /// .expect("hard-coded default directive should be valid"); + /// + /// let filter = EnvFilter::builder() + /// .with_default_directive(default) + /// .parse("")?; + /// + /// assert_eq!(format!("{}", filter), "myapp=debug"); + /// # Ok(()) } + /// ``` + /// + /// [`parse_lossy`]: Self::parse_lossy + /// [`from_env_lossy`]: Self::from_env_lossy + /// [`parse`]: Self::parse + /// [`from_env`]: Self::from_env + pub fn with_default_directive(self, default_directive: Directive) -> Self { + Self { + default_directive: Some(default_directive), + ..self + } + } + + /// Sets the name of the environment variable used by the [`from_env`], + /// [`from_env_lossy`], and [`try_from_env`] methods. + /// + /// By default, this is the value of [`EnvFilter::DEFAULT_ENV`] + /// (`RUST_LOG`). + /// + /// [`from_env`]: Self::from_env + /// [`from_env_lossy`]: Self::from_env_lossy + /// [`try_from_env`]: Self::try_from_env + pub fn with_env_var(self, var: impl ToString) -> Self { + Self { + env: Some(var.to_string()), + ..self + } + } + + /// Returns a new [`EnvFilter`] from the directives in the given string, + /// *ignoring* any that are invalid. + pub fn parse_lossy<S: AsRef<str>>(&self, dirs: S) -> EnvFilter { + let directives = dirs + .as_ref() + .split(',') + .filter(|s| !s.is_empty()) + .filter_map(|s| match Directive::parse(s, self.regex) { + Ok(d) => Some(d), + Err(err) => { + eprintln!("ignoring `{}`: {}", s, err); + None + } + }); + self.from_directives(directives) + } + + /// Returns a new [`EnvFilter`] from the directives in the given string, + /// or an error if any are invalid. + pub fn parse<S: AsRef<str>>(&self, dirs: S) -> Result<EnvFilter, directive::ParseError> { + let dirs = dirs.as_ref(); + if dirs.is_empty() { + return Ok(self.from_directives(std::iter::empty())); + } + let directives = dirs + .split(',') + .filter(|s| !s.is_empty()) + .map(|s| Directive::parse(s, self.regex)) + .collect::<Result<Vec<_>, _>>()?; + Ok(self.from_directives(directives)) + } + + /// Returns a new [`EnvFilter`] from the directives in the configured + /// environment variable, ignoring any directives that are invalid. + pub fn from_env_lossy(&self) -> EnvFilter { + let var = env::var(self.env_var_name()).unwrap_or_default(); + self.parse_lossy(var) + } + + /// Returns a new [`EnvFilter`] from the directives in the in the configured + /// environment variable, or an error if the environment variable is not set + /// or contains invalid directives. + pub fn from_env(&self) -> Result<EnvFilter, FromEnvError> { + let var = env::var(self.env_var_name()).unwrap_or_default(); + self.parse(var).map_err(Into::into) + } + + /// Returns a new [`EnvFilter`] from the directives in the in the configured + /// environment variable, or an error if the environment variable is not set + /// or contains invalid directives. + pub fn try_from_env(&self) -> Result<EnvFilter, FromEnvError> { + let var = env::var(self.env_var_name())?; + self.parse(var).map_err(Into::into) + } + + // TODO(eliza): consider making this a public API? + // Clippy doesn't love this naming, because it suggests that `from_` methods + // should not take a `Self`...but in this case, it's the `EnvFilter` that is + // being constructed "from" the directives, rather than the builder itself. + #[allow(clippy::wrong_self_convention)] + pub(super) fn from_directives( + &self, + directives: impl IntoIterator<Item = Directive>, + ) -> EnvFilter { + use tracing::Level; + + let mut directives: Vec<_> = directives.into_iter().collect(); + let mut disabled = Vec::new(); + for directive in &mut directives { + if directive.level > STATIC_MAX_LEVEL { + disabled.push(directive.clone()); + } + if !self.regex { + directive.deregexify(); + } + } + + if !disabled.is_empty() { + #[cfg(feature = "nu_ansi_term")] + use nu_ansi_term::{Color, Style}; + // NOTE: We can't use a configured `MakeWriter` because the EnvFilter + // has no knowledge of any underlying subscriber or collector, which + // may or may not use a `MakeWriter`. + let warn = |msg: &str| { + #[cfg(not(feature = "nu_ansi_term"))] + let msg = format!("warning: {}", msg); + #[cfg(feature = "nu_ansi_term")] + let msg = { + let bold = Style::new().bold(); + let mut warning = Color::Yellow.paint("warning"); + warning.style_ref_mut().is_bold = true; + format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg)) + }; + eprintln!("{}", msg); + }; + let ctx_prefixed = |prefix: &str, msg: &str| { + #[cfg(not(feature = "nu_ansi_term"))] + let msg = format!("{} {}", prefix, msg); + #[cfg(feature = "nu_ansi_term")] + let msg = { + let mut equal = Color::Fixed(21).paint("="); // dark blue + equal.style_ref_mut().is_bold = true; + format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg) + }; + eprintln!("{}", msg); + }; + let ctx_help = |msg| ctx_prefixed("help:", msg); + let ctx_note = |msg| ctx_prefixed("note:", msg); + let ctx = |msg: &str| { + #[cfg(not(feature = "nu_ansi_term"))] + let msg = format!("note: {}", msg); + #[cfg(feature = "nu_ansi_term")] + let msg = { + let mut pipe = Color::Fixed(21).paint("|"); + pipe.style_ref_mut().is_bold = true; + format!(" {} {}", pipe, msg) + }; + eprintln!("{}", msg); + }; + warn("some trace filter directives would enable traces that are disabled statically"); + for directive in disabled { + let target = if let Some(target) = &directive.target { + format!("the `{}` target", target) + } else { + "all targets".into() + }; + let level = directive + .level + .into_level() + .expect("=off would not have enabled any filters"); + ctx(&format!( + "`{}` would enable the {} level for {}", + directive, level, target + )); + } + ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL)); + let help_msg = || { + let (feature, filter) = match STATIC_MAX_LEVEL.into_level() { + Some(Level::TRACE) => unreachable!( + "if the max level is trace, no static filtering features are enabled" + ), + Some(Level::DEBUG) => ("max_level_debug", Level::TRACE), + Some(Level::INFO) => ("max_level_info", Level::DEBUG), + Some(Level::WARN) => ("max_level_warn", Level::INFO), + Some(Level::ERROR) => ("max_level_error", Level::WARN), + None => return ("max_level_off", String::new()), + }; + (feature, format!("{} ", filter)) + }; + let (feature, earlier_level) = help_msg(); + ctx_help(&format!( + "to enable {}logging, remove the `{}` feature from the `tracing` crate", + earlier_level, feature + )); + } + + let (dynamics, statics) = Directive::make_tables(directives); + let has_dynamics = !dynamics.is_empty(); + + let mut filter = EnvFilter { + statics, + dynamics, + has_dynamics, + by_id: RwLock::new(Default::default()), + by_cs: RwLock::new(Default::default()), + scope: ThreadLocal::new(), + regex: self.regex, + }; + + if !has_dynamics && filter.statics.is_empty() { + if let Some(ref default) = self.default_directive { + filter = filter.add_directive(default.clone()); + } + } + + filter + } + + fn env_var_name(&self) -> &str { + self.env.as_deref().unwrap_or(EnvFilter::DEFAULT_ENV) + } +} + +impl Default for Builder { + fn default() -> Self { + Self { + regex: true, + env: None, + default_directive: None, + } + } +} diff --git a/vendor/tracing-subscriber/src/filter/env/directive.rs b/vendor/tracing-subscriber/src/filter/env/directive.rs new file mode 100644 index 000000000..f062e6ef9 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/env/directive.rs @@ -0,0 +1,860 @@ +pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective}; +use crate::filter::{ + directive::{DirectiveSet, Match}, + env::{field, FieldMap}, + level::LevelFilter, +}; +use once_cell::sync::Lazy; +use regex::Regex; +use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr}; +use tracing_core::{span, Level, Metadata}; + +/// A single filtering directive. +// TODO(eliza): add a builder for programmatically constructing directives? +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] +pub struct Directive { + in_span: Option<String>, + fields: Vec<field::Match>, + pub(crate) target: Option<String>, + pub(crate) level: LevelFilter, +} + +/// A set of dynamic filtering directives. +pub(super) type Dynamics = DirectiveSet<Directive>; + +/// A set of static filtering directives. +pub(super) type Statics = DirectiveSet<StaticDirective>; + +pub(crate) type CallsiteMatcher = MatchSet<field::CallsiteMatch>; +pub(crate) type SpanMatcher = MatchSet<field::SpanMatch>; + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct MatchSet<T> { + field_matches: FilterVec<T>, + base_level: LevelFilter, +} + +impl Directive { + pub(super) fn has_name(&self) -> bool { + self.in_span.is_some() + } + + pub(super) fn has_fields(&self) -> bool { + !self.fields.is_empty() + } + + pub(super) fn to_static(&self) -> Option<StaticDirective> { + if !self.is_static() { + return None; + } + + // TODO(eliza): these strings are all immutable; we should consider + // `Arc`ing them to make this more efficient... + let field_names = self.fields.iter().map(field::Match::name).collect(); + + Some(StaticDirective::new( + self.target.clone(), + field_names, + self.level, + )) + } + + fn is_static(&self) -> bool { + !self.has_name() && !self.fields.iter().any(field::Match::has_value) + } + + pub(super) fn is_dynamic(&self) -> bool { + self.has_name() || self.has_fields() + } + + pub(crate) fn field_matcher(&self, meta: &Metadata<'_>) -> Option<field::CallsiteMatch> { + let fieldset = meta.fields(); + let fields = self + .fields + .iter() + .filter_map( + |field::Match { + ref name, + ref value, + }| { + if let Some(field) = fieldset.field(name) { + let value = value.as_ref().cloned()?; + Some(Ok((field, value))) + } else { + Some(Err(())) + } + }, + ) + .collect::<Result<FieldMap<_>, ()>>() + .ok()?; + Some(field::CallsiteMatch { + fields, + level: self.level, + }) + } + + pub(super) fn make_tables( + directives: impl IntoIterator<Item = Directive>, + ) -> (Dynamics, Statics) { + // TODO(eliza): this could be made more efficient... + let (dyns, stats): (Vec<Directive>, Vec<Directive>) = + directives.into_iter().partition(Directive::is_dynamic); + let statics = stats + .into_iter() + .filter_map(|d| d.to_static()) + .chain(dyns.iter().filter_map(Directive::to_static)) + .collect(); + (Dynamics::from_iter(dyns), statics) + } + + pub(super) fn deregexify(&mut self) { + for field in &mut self.fields { + field.value = match field.value.take() { + Some(field::ValueMatch::Pat(pat)) => { + Some(field::ValueMatch::Debug(pat.into_debug_match())) + } + x => x, + } + } + } + + pub(super) fn parse(from: &str, regex: bool) -> Result<Self, ParseError> { + static DIRECTIVE_RE: Lazy<Regex> = Lazy::new(|| Regex::new( + r"(?x) + ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ | + # ^^^. + # `note: we match log level names case-insensitively + ^ + (?: # target name or span name + (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\]) + ){1,2} + (?: # level or nothing + =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))? + # ^^^. + # `note: we match log level names case-insensitively + )? + $ + " + ) + .unwrap()); + static SPAN_PART_RE: Lazy<Regex> = + Lazy::new(|| Regex::new(r#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap()); + static FIELD_FILTER_RE: Lazy<Regex> = + // TODO(eliza): this doesn't _currently_ handle value matchers that include comma + // characters. We should fix that. + Lazy::new(|| Regex::new(r#"(?x) + ( + # field name + [[:word:]][[[:word:]]\.]* + # value part (optional) + (?:=[^,]+)? + ) + # trailing comma or EOS + (?:,\s?|$) + "#).unwrap()); + + let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?; + + if let Some(level) = caps + .name("global_level") + .and_then(|s| s.as_str().parse().ok()) + { + return Ok(Directive { + level, + ..Default::default() + }); + } + + let target = caps.name("target").and_then(|c| { + let s = c.as_str(); + if s.parse::<LevelFilter>().is_ok() { + None + } else { + Some(s.to_owned()) + } + }); + + let (in_span, fields) = caps + .name("span") + .and_then(|cap| { + let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']'); + let caps = SPAN_PART_RE.captures(cap)?; + let span = caps.name("name").map(|c| c.as_str().to_owned()); + let fields = caps + .name("fields") + .map(|c| { + FIELD_FILTER_RE + .find_iter(c.as_str()) + .map(|c| field::Match::parse(c.as_str(), regex)) + .collect::<Result<Vec<_>, _>>() + }) + .unwrap_or_else(|| Ok(Vec::new())); + Some((span, fields)) + }) + .unwrap_or_else(|| (None, Ok(Vec::new()))); + + let level = caps + .name("level") + .and_then(|l| l.as_str().parse().ok()) + // Setting the target without the level enables every level for that target + .unwrap_or(LevelFilter::TRACE); + + Ok(Self { + level, + target, + in_span, + fields: fields?, + }) + } +} + +impl Match for Directive { + fn cares_about(&self, meta: &Metadata<'_>) -> bool { + // Does this directive have a target filter, and does it match the + // metadata's target? + if let Some(ref target) = self.target { + if !meta.target().starts_with(&target[..]) { + return false; + } + } + + // Do we have a name filter, and does it match the metadata's name? + // TODO(eliza): put name globbing here? + if let Some(ref name) = self.in_span { + if name != meta.name() { + return false; + } + } + + // Does the metadata define all the fields that this directive cares about? + let actual_fields = meta.fields(); + for expected_field in &self.fields { + // Does the actual field set (from the metadata) contain this field? + if actual_fields.field(&expected_field.name).is_none() { + return false; + } + } + + true + } + + fn level(&self) -> &LevelFilter { + &self.level + } +} + +impl FromStr for Directive { + type Err = ParseError; + fn from_str(from: &str) -> Result<Self, Self::Err> { + Directive::parse(from, true) + } +} + +impl Default for Directive { + fn default() -> Self { + Directive { + level: LevelFilter::OFF, + target: None, + in_span: None, + fields: Vec::new(), + } + } +} + +impl PartialOrd for Directive { + fn partial_cmp(&self, other: &Directive) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Directive { + fn cmp(&self, other: &Directive) -> Ordering { + // We attempt to order directives by how "specific" they are. This + // ensures that we try the most specific directives first when + // attempting to match a piece of metadata. + + // First, we compare based on whether a target is specified, and the + // lengths of those targets if both have targets. + let ordering = self + .target + .as_ref() + .map(String::len) + .cmp(&other.target.as_ref().map(String::len)) + // Next compare based on the presence of span names. + .then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some())) + // Then we compare how many fields are defined by each + // directive. + .then_with(|| self.fields.len().cmp(&other.fields.len())) + // Finally, we fall back to lexicographical ordering if the directives are + // equally specific. Although this is no longer semantically important, + // we need to define a total ordering to determine the directive's place + // in the BTreeMap. + .then_with(|| { + self.target + .cmp(&other.target) + .then_with(|| self.in_span.cmp(&other.in_span)) + .then_with(|| self.fields[..].cmp(&other.fields[..])) + }) + .reverse(); + + #[cfg(debug_assertions)] + { + if ordering == Ordering::Equal { + debug_assert_eq!( + self.target, other.target, + "invariant violated: Ordering::Equal must imply a.target == b.target" + ); + debug_assert_eq!( + self.in_span, other.in_span, + "invariant violated: Ordering::Equal must imply a.in_span == b.in_span" + ); + debug_assert_eq!( + self.fields, other.fields, + "invariant violated: Ordering::Equal must imply a.fields == b.fields" + ); + } + } + + ordering + } +} + +impl fmt::Display for Directive { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut wrote_any = false; + if let Some(ref target) = self.target { + fmt::Display::fmt(target, f)?; + wrote_any = true; + } + + if self.in_span.is_some() || !self.fields.is_empty() { + f.write_str("[")?; + + if let Some(ref span) = self.in_span { + fmt::Display::fmt(span, f)?; + } + + let mut fields = self.fields.iter(); + if let Some(field) = fields.next() { + write!(f, "{{{}", field)?; + for field in fields { + write!(f, ",{}", field)?; + } + f.write_str("}")?; + } + + f.write_str("]")?; + wrote_any = true; + } + + if wrote_any { + f.write_str("=")?; + } + + fmt::Display::fmt(&self.level, f) + } +} + +impl From<LevelFilter> for Directive { + fn from(level: LevelFilter) -> Self { + Self { + level, + ..Self::default() + } + } +} + +impl From<Level> for Directive { + fn from(level: Level) -> Self { + LevelFilter::from_level(level).into() + } +} + +// === impl Dynamics === + +impl Dynamics { + pub(crate) fn matcher(&self, metadata: &Metadata<'_>) -> Option<CallsiteMatcher> { + let mut base_level = None; + let field_matches = self + .directives_for(metadata) + .filter_map(|d| { + if let Some(f) = d.field_matcher(metadata) { + return Some(f); + } + match base_level { + Some(ref b) if d.level > *b => base_level = Some(d.level), + None => base_level = Some(d.level), + _ => {} + } + None + }) + .collect(); + + if let Some(base_level) = base_level { + Some(CallsiteMatcher { + field_matches, + base_level, + }) + } else if !field_matches.is_empty() { + Some(CallsiteMatcher { + field_matches, + base_level: base_level.unwrap_or(LevelFilter::OFF), + }) + } else { + None + } + } + + pub(crate) fn has_value_filters(&self) -> bool { + self.directives() + .any(|d| d.fields.iter().any(|f| f.value.is_some())) + } +} + +// ===== impl DynamicMatch ===== + +impl CallsiteMatcher { + /// Create a new `SpanMatch` for a given instance of the matched callsite. + pub(crate) fn to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher { + let field_matches = self + .field_matches + .iter() + .map(|m| { + let m = m.to_span_match(); + attrs.record(&mut m.visitor()); + m + }) + .collect(); + SpanMatcher { + field_matches, + base_level: self.base_level, + } + } +} + +impl SpanMatcher { + /// Returns the level currently enabled for this callsite. + pub(crate) fn level(&self) -> LevelFilter { + self.field_matches + .iter() + .filter_map(field::SpanMatch::filter) + .max() + .unwrap_or(self.base_level) + } + + pub(crate) fn record_update(&self, record: &span::Record<'_>) { + for m in &self.field_matches { + record.record(&mut m.visitor()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn parse_directives(dirs: impl AsRef<str>) -> Vec<Directive> { + dirs.as_ref() + .split(',') + .filter_map(|s| s.parse().ok()) + .collect() + } + + fn expect_parse(dirs: impl AsRef<str>) -> Vec<Directive> { + dirs.as_ref() + .split(',') + .map(|s| { + s.parse() + .unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err)) + }) + .collect() + } + + #[test] + fn directive_ordering_by_target_len() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse( + "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn", + ); + dirs.sort_unstable(); + + let expected = vec![ + "a_really_long_name_with_no_colons", + "foo::bar::baz", + "foo::bar", + "foo", + ]; + let sorted = dirs + .iter() + .map(|d| d.target.as_ref().unwrap()) + .collect::<Vec<_>>(); + + assert_eq!(expected, sorted); + } + #[test] + fn directive_ordering_by_span() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn"); + dirs.sort_unstable(); + + let expected = vec!["baz::quux", "bar", "foo", "a"]; + let sorted = dirs + .iter() + .map(|d| d.target.as_ref().unwrap()) + .collect::<Vec<_>>(); + + assert_eq!(expected, sorted); + } + + #[test] + fn directive_ordering_uses_lexicographic_when_equal() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info"); + dirs.sort_unstable(); + + let expected = vec![ + ("span", Some("b")), + ("span", Some("a")), + ("c", None), + ("b", None), + ("a", None), + ]; + let sorted = dirs + .iter() + .map(|d| { + ( + d.target.as_ref().unwrap().as_ref(), + d.in_span.as_ref().map(String::as_ref), + ) + }) + .collect::<Vec<_>>(); + + assert_eq!(expected, sorted); + } + + // TODO: this test requires the parser to support directives with multiple + // fields, which it currently can't handle. We should enable this test when + // that's implemented. + #[test] + #[ignore] + fn directive_ordering_by_field_num() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse( + "b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info" + ); + dirs.sort_unstable(); + + let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"]; + let sorted = dirs + .iter() + .map(|d| d.target.as_ref().unwrap()) + .collect::<Vec<_>>(); + + assert_eq!(expected, sorted); + } + + #[test] + fn parse_directives_ralith() { + let dirs = parse_directives("common=trace,server=trace"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("common".to_string())); + assert_eq!(dirs[0].level, LevelFilter::TRACE); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("server".to_string())); + assert_eq!(dirs[1].level, LevelFilter::TRACE); + assert_eq!(dirs[1].in_span, None); + } + + #[test] + fn parse_directives_ralith_uc() { + let dirs = parse_directives("common=INFO,server=DEBUG"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("common".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("server".to_string())); + assert_eq!(dirs[1].level, LevelFilter::DEBUG); + assert_eq!(dirs[1].in_span, None); + } + + #[test] + fn parse_directives_ralith_mixed() { + let dirs = parse_directives("common=iNfo,server=dEbUg"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("common".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("server".to_string())); + assert_eq!(dirs[1].level, LevelFilter::DEBUG); + assert_eq!(dirs[1].in_span, None); + } + + #[test] + fn parse_directives_valid() { + let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); + assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::TRACE); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate2".to_string())); + assert_eq!(dirs[2].level, LevelFilter::DEBUG); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate3".to_string())); + assert_eq!(dirs[3].level, LevelFilter::OFF); + assert_eq!(dirs[3].in_span, None); + } + + #[test] + + fn parse_level_directives() { + let dirs = parse_directives( + "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ + crate2=debug,crate3=trace,crate3::mod2::mod1=off", + ); + assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::INFO); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].in_span, None); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].in_span, None); + + assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); + assert_eq!(dirs[5].level, LevelFilter::OFF); + assert_eq!(dirs[5].in_span, None); + } + + #[test] + fn parse_uppercase_level_directives() { + let dirs = parse_directives( + "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ + crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", + ); + assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::INFO); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].in_span, None); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].in_span, None); + + assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); + assert_eq!(dirs[5].level, LevelFilter::OFF); + assert_eq!(dirs[5].in_span, None); + } + + #[test] + fn parse_numeric_level_directives() { + let dirs = parse_directives( + "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\ + crate3=5,crate3::mod2::mod1=0", + ); + assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::INFO); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].in_span, None); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].in_span, None); + + assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); + assert_eq!(dirs[5].level, LevelFilter::OFF); + assert_eq!(dirs[5].in_span, None); + } + + #[test] + fn parse_directives_invalid_crate() { + // test parse_directives with multiple = in specification + let dirs = parse_directives("crate1::mod1=warn=info,crate2=debug"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::DEBUG); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_invalid_level() { + // test parse_directives with 'noNumber' as log level + let dirs = parse_directives("crate1::mod1=noNumber,crate2=debug"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::DEBUG); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_string_level() { + // test parse_directives with 'warn' as log level + let dirs = parse_directives("crate1::mod1=wrong,crate2=warn"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::WARN); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_empty_level() { + // test parse_directives with '' as log level + let dirs = parse_directives("crate1::mod1=wrong,crate2="); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::TRACE); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_global() { + // test parse_directives with no crate + let dirs = parse_directives("warn,crate2=debug"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, None); + assert_eq!(dirs[0].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[1].target, Some("crate2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::DEBUG); + assert_eq!(dirs[1].in_span, None); + } + + // helper function for tests below + fn test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter) { + let dirs = parse_directives(directive_to_test); + assert_eq!( + dirs.len(), + 1, + "\ninput: \"{}\"; parsed: {:#?}", + directive_to_test, + dirs + ); + assert_eq!(dirs[0].target, None); + assert_eq!(dirs[0].level, level_expected); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_global_bare_warn_lc() { + // test parse_directives with no crate, in isolation, all lowercase + test_parse_bare_level("warn", LevelFilter::WARN); + } + + #[test] + fn parse_directives_global_bare_warn_uc() { + // test parse_directives with no crate, in isolation, all uppercase + test_parse_bare_level("WARN", LevelFilter::WARN); + } + + #[test] + fn parse_directives_global_bare_warn_mixed() { + // test parse_directives with no crate, in isolation, mixed case + test_parse_bare_level("wArN", LevelFilter::WARN); + } + + #[test] + fn parse_directives_valid_with_spans() { + let dirs = parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug"); + assert_eq!(dirs.len(), 3, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, Some("foo".to_string())); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::TRACE); + assert_eq!(dirs[1].in_span, Some("bar".to_string())); + + assert_eq!(dirs[2].target, Some("crate2".to_string())); + assert_eq!(dirs[2].level, LevelFilter::DEBUG); + assert_eq!(dirs[2].in_span, Some("baz".to_string())); + } + + #[test] + fn parse_directives_with_dash_in_target_name() { + let dirs = parse_directives("target-name=info"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("target-name".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_with_dash_in_span_name() { + // Reproduces https://github.com/tokio-rs/tracing/issues/1367 + + let dirs = parse_directives("target[span-name]=info"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("target".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, Some("span-name".to_string())); + } + + #[test] + fn parse_directives_with_special_characters_in_span_name() { + let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}"; + + let dirs = parse_directives(format!("target[{}]=info", span_name)); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("target".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, Some(span_name.to_string())); + } + + #[test] + fn parse_directives_with_invalid_span_chars() { + let invalid_span_name = "]{"; + + let dirs = parse_directives(format!("target[{}]=info", invalid_span_name)); + assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs); + } +} diff --git a/vendor/tracing-subscriber/src/filter/env/field.rs b/vendor/tracing-subscriber/src/filter/env/field.rs new file mode 100644 index 000000000..1394fd04a --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/env/field.rs @@ -0,0 +1,626 @@ +use matchers::Pattern; +use std::{ + cmp::Ordering, + error::Error, + fmt::{self, Write}, + str::FromStr, + sync::{ + atomic::{AtomicBool, Ordering::*}, + Arc, + }, +}; + +use super::{FieldMap, LevelFilter}; +use tracing_core::field::{Field, Visit}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub(crate) struct Match { + pub(crate) name: String, // TODO: allow match patterns for names? + pub(crate) value: Option<ValueMatch>, +} + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct CallsiteMatch { + pub(crate) fields: FieldMap<ValueMatch>, + pub(crate) level: LevelFilter, +} + +#[derive(Debug)] +pub(crate) struct SpanMatch { + fields: FieldMap<(ValueMatch, AtomicBool)>, + level: LevelFilter, + has_matched: AtomicBool, +} + +pub(crate) struct MatchVisitor<'a> { + inner: &'a SpanMatch, +} + +#[derive(Debug, Clone)] +pub(crate) enum ValueMatch { + /// Matches a specific `bool` value. + Bool(bool), + /// Matches a specific `f64` value. + F64(f64), + /// Matches a specific `u64` value. + U64(u64), + /// Matches a specific `i64` value. + I64(i64), + /// Matches any `NaN` `f64` value. + NaN, + /// Matches any field whose `fmt::Debug` output is equal to a fixed string. + Debug(MatchDebug), + /// Matches any field whose `fmt::Debug` output matches a regular expression + /// pattern. + Pat(Box<MatchPattern>), +} + +impl Eq for ValueMatch {} + +impl PartialEq for ValueMatch { + fn eq(&self, other: &Self) -> bool { + use ValueMatch::*; + match (self, other) { + (Bool(a), Bool(b)) => a.eq(b), + (F64(a), F64(b)) => { + debug_assert!(!a.is_nan()); + debug_assert!(!b.is_nan()); + + a.eq(b) + } + (U64(a), U64(b)) => a.eq(b), + (I64(a), I64(b)) => a.eq(b), + (NaN, NaN) => true, + (Pat(a), Pat(b)) => a.eq(b), + _ => false, + } + } +} + +impl Ord for ValueMatch { + fn cmp(&self, other: &Self) -> Ordering { + use ValueMatch::*; + match (self, other) { + (Bool(this), Bool(that)) => this.cmp(that), + (Bool(_), _) => Ordering::Less, + + (F64(this), F64(that)) => this + .partial_cmp(that) + .expect("`ValueMatch::F64` may not contain `NaN` values"), + (F64(_), Bool(_)) => Ordering::Greater, + (F64(_), _) => Ordering::Less, + + (NaN, NaN) => Ordering::Equal, + (NaN, Bool(_)) | (NaN, F64(_)) => Ordering::Greater, + (NaN, _) => Ordering::Less, + + (U64(this), U64(that)) => this.cmp(that), + (U64(_), Bool(_)) | (U64(_), F64(_)) | (U64(_), NaN) => Ordering::Greater, + (U64(_), _) => Ordering::Less, + + (I64(this), I64(that)) => this.cmp(that), + (I64(_), Bool(_)) | (I64(_), F64(_)) | (I64(_), NaN) | (I64(_), U64(_)) => { + Ordering::Greater + } + (I64(_), _) => Ordering::Less, + + (Pat(this), Pat(that)) => this.cmp(that), + (Pat(_), _) => Ordering::Greater, + + (Debug(this), Debug(that)) => this.cmp(that), + (Debug(_), _) => Ordering::Greater, + } + } +} + +impl PartialOrd for ValueMatch { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +/// Matches a field's `fmt::Debug` output against a regular expression pattern. +/// +/// This is used for matching all non-literal field value filters when regular +/// expressions are enabled. +#[derive(Debug, Clone)] +pub(crate) struct MatchPattern { + pub(crate) matcher: Pattern, + pattern: Arc<str>, +} + +/// Matches a field's `fmt::Debug` output against a fixed string pattern. +/// +/// This is used for matching all non-literal field value filters when regular +/// expressions are disabled. +#[derive(Debug, Clone)] +pub(crate) struct MatchDebug { + pattern: Arc<str>, +} + +/// Indicates that a field name specified in a filter directive was invalid. +#[derive(Clone, Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] +pub struct BadName { + name: String, +} + +// === impl Match === + +impl Match { + pub(crate) fn has_value(&self) -> bool { + self.value.is_some() + } + + // TODO: reference count these strings? + pub(crate) fn name(&self) -> String { + self.name.clone() + } + + pub(crate) fn parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>> { + let mut parts = s.split('='); + let name = parts + .next() + .ok_or_else(|| BadName { + name: "".to_string(), + })? + // TODO: validate field name + .to_string(); + let value = parts + .next() + .map(|part| match regex { + true => ValueMatch::parse_regex(part), + false => Ok(ValueMatch::parse_non_regex(part)), + }) + .transpose()?; + Ok(Match { name, value }) + } +} + +impl fmt::Display for Match { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.name, f)?; + if let Some(ref value) = self.value { + write!(f, "={}", value)?; + } + Ok(()) + } +} + +impl Ord for Match { + fn cmp(&self, other: &Self) -> Ordering { + // Ordering for `Match` directives is based first on _whether_ a value + // is matched or not. This is semantically meaningful --- we would + // prefer to check directives that match values first as they are more + // specific. + let has_value = match (self.value.as_ref(), other.value.as_ref()) { + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + _ => Ordering::Equal, + }; + // If both directives match a value, we fall back to the field names in + // length + lexicographic ordering, and if these are equal as well, we + // compare the match directives. + // + // This ordering is no longer semantically meaningful but is necessary + // so that the directives can be stored in the `BTreeMap` in a defined + // order. + has_value + .then_with(|| self.name.cmp(&other.name)) + .then_with(|| self.value.cmp(&other.value)) + } +} + +impl PartialOrd for Match { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +// === impl ValueMatch === + +fn value_match_f64(v: f64) -> ValueMatch { + if v.is_nan() { + ValueMatch::NaN + } else { + ValueMatch::F64(v) + } +} + +impl ValueMatch { + /// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular + /// expressions. + /// + /// This returns an error if the string didn't contain a valid `bool`, + /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular + /// expression. + fn parse_regex(s: &str) -> Result<Self, matchers::Error> { + s.parse::<bool>() + .map(ValueMatch::Bool) + .or_else(|_| s.parse::<u64>().map(ValueMatch::U64)) + .or_else(|_| s.parse::<i64>().map(ValueMatch::I64)) + .or_else(|_| s.parse::<f64>().map(value_match_f64)) + .or_else(|_| { + s.parse::<MatchPattern>() + .map(|p| ValueMatch::Pat(Box::new(p))) + }) + } + + /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed + /// string. + /// + /// This does *not* return an error, because any string that isn't a valid + /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected + /// `fmt::Debug` output. + fn parse_non_regex(s: &str) -> Self { + s.parse::<bool>() + .map(ValueMatch::Bool) + .or_else(|_| s.parse::<u64>().map(ValueMatch::U64)) + .or_else(|_| s.parse::<i64>().map(ValueMatch::I64)) + .or_else(|_| s.parse::<f64>().map(value_match_f64)) + .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s))) + } +} + +impl fmt::Display for ValueMatch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f), + ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f), + } + } +} + +// === impl MatchPattern === + +impl FromStr for MatchPattern { + type Err = matchers::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let matcher = s.parse::<Pattern>()?; + Ok(Self { + matcher, + pattern: s.to_owned().into(), + }) + } +} + +impl fmt::Display for MatchPattern { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self.pattern, f) + } +} + +impl AsRef<str> for MatchPattern { + #[inline] + fn as_ref(&self) -> &str { + self.pattern.as_ref() + } +} + +impl MatchPattern { + #[inline] + fn str_matches(&self, s: &impl AsRef<str>) -> bool { + self.matcher.matches(s) + } + + #[inline] + fn debug_matches(&self, d: &impl fmt::Debug) -> bool { + self.matcher.debug_matches(d) + } + + pub(super) fn into_debug_match(self) -> MatchDebug { + MatchDebug { + pattern: self.pattern, + } + } +} + +impl PartialEq for MatchPattern { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.pattern == other.pattern + } +} + +impl Eq for MatchPattern {} + +impl PartialOrd for MatchPattern { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.pattern.cmp(&other.pattern)) + } +} + +impl Ord for MatchPattern { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.pattern.cmp(&other.pattern) + } +} + +// === impl MatchDebug === + +impl MatchDebug { + fn new(s: &str) -> Self { + Self { + pattern: s.to_owned().into(), + } + } + + #[inline] + fn debug_matches(&self, d: &impl fmt::Debug) -> bool { + // Naively, we would probably match a value's `fmt::Debug` output by + // formatting it to a string, and then checking if the string is equal + // to the expected pattern. However, this would require allocating every + // time we want to match a field value against a `Debug` matcher, which + // can be avoided. + // + // Instead, we implement `fmt::Write` for a type that, rather than + // actually _writing_ the strings to something, matches them against the + // expected pattern, and returns an error if the pattern does not match. + struct Matcher<'a> { + pattern: &'a str, + } + + impl fmt::Write for Matcher<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + // If the string is longer than the remaining expected string, + // we know it won't match, so bail. + if s.len() > self.pattern.len() { + return Err(fmt::Error); + } + + // If the expected string begins with the string that was + // written, we are still potentially a match. Advance the + // position in the expected pattern to chop off the matched + // output, and continue. + if self.pattern.starts_with(s) { + self.pattern = &self.pattern[s.len()..]; + return Ok(()); + } + + // Otherwise, the expected string doesn't include the string + // that was written at the current position, so the `fmt::Debug` + // output doesn't match! Return an error signalling that this + // doesn't match. + Err(fmt::Error) + } + } + let mut matcher = Matcher { + pattern: &self.pattern, + }; + + // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This + // returns an error if the `fmt::Debug` implementation wrote any + // characters that did not match the expected pattern. + write!(matcher, "{:?}", d).is_ok() + } +} + +impl fmt::Display for MatchDebug { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self.pattern, f) + } +} + +impl AsRef<str> for MatchDebug { + #[inline] + fn as_ref(&self) -> &str { + self.pattern.as_ref() + } +} + +impl PartialEq for MatchDebug { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.pattern == other.pattern + } +} + +impl Eq for MatchDebug {} + +impl PartialOrd for MatchDebug { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.pattern.cmp(&other.pattern)) + } +} + +impl Ord for MatchDebug { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.pattern.cmp(&other.pattern) + } +} + +// === impl BadName === + +impl Error for BadName {} + +impl fmt::Display for BadName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid field name `{}`", self.name) + } +} + +impl CallsiteMatch { + pub(crate) fn to_span_match(&self) -> SpanMatch { + let fields = self + .fields + .iter() + .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false)))) + .collect(); + SpanMatch { + fields, + level: self.level, + has_matched: AtomicBool::new(false), + } + } +} + +impl SpanMatch { + pub(crate) fn visitor(&self) -> MatchVisitor<'_> { + MatchVisitor { inner: self } + } + + #[inline] + pub(crate) fn is_matched(&self) -> bool { + if self.has_matched.load(Acquire) { + return true; + } + self.is_matched_slow() + } + + #[inline(never)] + fn is_matched_slow(&self) -> bool { + let matched = self + .fields + .values() + .all(|(_, matched)| matched.load(Acquire)); + if matched { + self.has_matched.store(true, Release); + } + matched + } + + #[inline] + pub(crate) fn filter(&self) -> Option<LevelFilter> { + if self.is_matched() { + Some(self.level) + } else { + None + } + } +} + +impl<'a> Visit for MatchVisitor<'a> { + fn record_f64(&mut self, field: &Field, value: f64) { + match self.inner.fields.get(field) { + Some((ValueMatch::NaN, ref matched)) if value.is_nan() => { + matched.store(true, Release); + } + Some((ValueMatch::F64(ref e), ref matched)) + if (value - *e).abs() < std::f64::EPSILON => + { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_i64(&mut self, field: &Field, value: i64) { + use std::convert::TryInto; + + match self.inner.fields.get(field) { + Some((ValueMatch::I64(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_u64(&mut self, field: &Field, value: u64) { + match self.inner.fields.get(field) { + Some((ValueMatch::U64(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_bool(&mut self, field: &Field, value: bool) { + match self.inner.fields.get(field) { + Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_str(&mut self, field: &Field, value: &str) { + match self.inner.fields.get(field) { + Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => { + matched.store(true, Release); + } + Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release) + } + _ => {} + } + } + + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + match self.inner.fields.get(field) { + Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release); + } + Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release) + } + _ => {} + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[derive(Debug)] + #[allow(dead_code)] + struct MyStruct { + answer: usize, + question: &'static str, + } + + #[test] + fn debug_struct_match() { + let my_struct = MyStruct { + answer: 42, + question: "life, the universe, and everything", + }; + + let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; + + assert_eq!( + format!("{:?}", my_struct), + pattern, + "`MyStruct`'s `Debug` impl doesn't output the expected string" + ); + + let matcher = MatchDebug { + pattern: pattern.into(), + }; + assert!(matcher.debug_matches(&my_struct)) + } + + #[test] + fn debug_struct_not_match() { + let my_struct = MyStruct { + answer: 42, + question: "what shall we have for lunch?", + }; + + let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; + + assert_eq!( + format!("{:?}", my_struct), + "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }", + "`MyStruct`'s `Debug` impl doesn't output the expected string" + ); + + let matcher = MatchDebug { + pattern: pattern.into(), + }; + assert!(!matcher.debug_matches(&my_struct)) + } +} diff --git a/vendor/tracing-subscriber/src/filter/env/mod.rs b/vendor/tracing-subscriber/src/filter/env/mod.rs new file mode 100644 index 000000000..81a9ae2bd --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/env/mod.rs @@ -0,0 +1,991 @@ +//! A `Layer` that enables or disables spans and events based on a set of +//! filtering directives. + +// these are publicly re-exported, but the compiler doesn't realize +// that for some reason. +#[allow(unreachable_pub)] +pub use self::{builder::Builder, directive::Directive, field::BadName as BadFieldName}; +mod builder; +mod directive; +mod field; + +use crate::{ + filter::LevelFilter, + layer::{Context, Layer}, + sync::RwLock, +}; +use directive::ParseError; +use std::{cell::RefCell, collections::HashMap, env, error::Error, fmt, str::FromStr}; +use thread_local::ThreadLocal; +use tracing_core::{ + callsite, + field::Field, + span, + subscriber::{Interest, Subscriber}, + Metadata, +}; + +/// A [`Layer`] which filters spans and events based on a set of filter +/// directives. +/// +/// `EnvFilter` implements both the [`Layer`](#impl-Layer<S>) and [`Filter`] traits, so it may +/// be used for both [global filtering][global] and [per-layer filtering][plf], +/// respectively. See [the documentation on filtering with `Layer`s][filtering] +/// for details. +/// +/// The [`Targets`] type implements a similar form of filtering, but without the +/// ability to dynamically enable events based on the current span context, and +/// without filtering on field values. When these features are not required, +/// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`]. +/// +/// # Directives +/// +/// A filter consists of one or more comma-separated directives which match on [`Span`]s and [`Event`]s. +/// Each directive may have a corresponding maximum verbosity [`level`] which +/// enables (e.g., _selects for_) spans and events that match. Like `log`, +/// `tracing` considers less exclusive levels (like `trace` or `info`) to be more +/// verbose than more exclusive levels (like `error` or `warn`). +/// +/// The directive syntax is similar to that of [`env_logger`]'s. At a high level, the syntax for directives +/// consists of several parts: +/// +/// ```text +/// target[span{field=value}]=level +/// ``` +/// +/// Each component (`target`, `span`, `field`, `value`, and `level`) will be covered in turn. +/// +/// - `target` matches the event or span's target. In general, this is the module path and/or crate name. +/// Examples of targets `h2`, `tokio::net`, or `tide::server`. For more information on targets, +/// please refer to [`Metadata`]'s documentation. +/// - `span` matches on the span's name. If a `span` directive is provided alongside a `target`, +/// the `span` directive will match on spans _within_ the `target`. +/// - `field` matches on [fields] within spans. Field names can also be supplied without a `value` +/// and will match on any [`Span`] or [`Event`] that has a field with that name. +/// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`. +/// - `value` matches on the value of a span's field. If a value is a numeric literal or a bool, +/// it will match _only_ on that value. Otherwise, this filter matches the +/// [`std::fmt::Debug`] output from the value. +/// - `level` sets a maximum verbosity level accepted by this directive. +/// +/// When a field value directive (`[{<FIELD NAME>=<FIELD_VALUE>}]=...`) matches a +/// value's [`std::fmt::Debug`] output (i.e., the field value in the directive +/// is not a `bool`, `i64`, `u64`, or `f64` literal), the matched pattern may be +/// interpreted as either a regular expression or as the precise expected +/// output of the field's [`std::fmt::Debug`] implementation. By default, these +/// filters are interpreted as regular expressions, but this can be disabled +/// using the [`Builder::with_regex`] builder method to use precise matching +/// instead. +/// +/// When field value filters are interpreted as regular expressions, the +/// [`regex-automata` crate's regular expression syntax][re-syntax] is +/// supported. +/// +/// **Note**: When filters are constructed from potentially untrusted inputs, +/// [disabling regular expression matching](Builder::with_regex) is strongly +/// recommended. +/// +/// ## Usage Notes +/// +/// - The portion of the directive which is included within the square brackets is `tracing`-specific. +/// - Any portion of the directive can be omitted. +/// - The sole exception are the `field` and `value` directives. If a `value` is provided, +/// a `field` must _also_ be provided. However, the converse does not hold, as fields can +/// be matched without a value. +/// - If only a level is provided, it will set the maximum level for all `Span`s and `Event`s +/// that are not enabled by other filters. +/// - A directive without a level will enable anything that it matches. This is equivalent to `=trace`. +/// - When a crate has a dash in its name, the default target for events will be the +/// crate's module path as it appears in Rust. This means every dash will be replaced +/// with an underscore. +/// - A dash in a target will only appear when being specified explicitly: +/// `tracing::info!(target: "target-name", ...);` +/// +/// ## Example Syntax +/// +/// - `tokio::net=info` will enable all spans or events that: +/// - have the `tokio::net` target, +/// - at the level `info` or above. +/// - `warn,tokio::net=info` will enable all spans and events that: +/// - are at the level `warn` or above, *or* +/// - have the `tokio::net` target at the level `info` or above. +/// - `my_crate[span_a]=trace` will enable all spans and events that: +/// - are within the `span_a` span or named `span_a` _if_ `span_a` has the target `my_crate`, +/// - at the level `trace` or above. +/// - `[span_b{name=\"bob\"}]` will enable all spans or event that: +/// - have _any_ target, +/// - are inside a span named `span_b`, +/// - which has a field named `name` with value `bob`, +/// - at _any_ level. +/// +/// # Examples +/// +/// Parsing an `EnvFilter` from the [default environment +/// variable](EnvFilter::from_default_env) (`RUST_LOG`): +/// +/// ``` +/// use tracing_subscriber::{EnvFilter, fmt, prelude::*}; +/// +/// tracing_subscriber::registry() +/// .with(fmt::layer()) +/// .with(EnvFilter::from_default_env()) +/// .init(); +/// ``` +/// +/// Parsing an `EnvFilter` [from a user-provided environment +/// variable](EnvFilter::from_env): +/// +/// ``` +/// use tracing_subscriber::{EnvFilter, fmt, prelude::*}; +/// +/// tracing_subscriber::registry() +/// .with(fmt::layer()) +/// .with(EnvFilter::from_env("MYAPP_LOG")) +/// .init(); +/// ``` +/// +/// Using `EnvFilter` as a [per-layer filter][plf] to filter only a single +/// [`Layer`]: +/// +/// ``` +/// use tracing_subscriber::{EnvFilter, fmt, prelude::*}; +/// +/// // Parse an `EnvFilter` configuration from the `RUST_LOG` +/// // environment variable. +/// let filter = EnvFilter::from_default_env(); +/// +/// // Apply the filter to this layer *only*. +/// let filtered_layer = fmt::layer().with_filter(filter); +/// +/// // Some other layer, whose output we don't want to filter. +/// let unfiltered_layer = // ... +/// # fmt::layer(); +/// +/// tracing_subscriber::registry() +/// .with(filtered_layer) +/// .with(unfiltered_layer) +/// .init(); +/// ``` +/// # Constructing `EnvFilter`s +/// +/// An `EnvFilter` is be constructed by parsing a string containing one or more +/// directives. The [`EnvFilter::new`] constructor parses an `EnvFilter` from a +/// string, ignoring any invalid directives, while [`EnvFilter::try_new`] +/// returns an error if invalid directives are encountered. Similarly, the +/// [`EnvFilter::from_env`] and [`EnvFilter::try_from_env`] constructors parse +/// an `EnvFilter` from the value of the provided environment variable, with +/// lossy and strict validation, respectively. +/// +/// A [builder](EnvFilter::builder) interface is available to set additional +/// configuration options prior to parsing an `EnvFilter`. See the [`Builder` +/// type's documentation](Builder) for details on the options that can be +/// configured using the builder. +/// +/// [`Span`]: tracing_core::span +/// [fields]: tracing_core::Field +/// [`Event`]: tracing_core::Event +/// [`level`]: tracing_core::Level +/// [`Metadata`]: tracing_core::Metadata +/// [`Targets`]: crate::filter::Targets +/// [`env_logger`]: https://crates.io/crates/env_logger +/// [`Filter`]: #impl-Filter<S> +/// [global]: crate::layer#global-filtering +/// [plf]: crate::layer#per-layer-filtering +/// [filtering]: crate::layer#filtering-with-layers +#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] +#[derive(Debug)] +pub struct EnvFilter { + statics: directive::Statics, + dynamics: directive::Dynamics, + has_dynamics: bool, + by_id: RwLock<HashMap<span::Id, directive::SpanMatcher>>, + by_cs: RwLock<HashMap<callsite::Identifier, directive::CallsiteMatcher>>, + scope: ThreadLocal<RefCell<Vec<LevelFilter>>>, + regex: bool, +} + +type FieldMap<T> = HashMap<Field, T>; + +/// Indicates that an error occurred while parsing a `EnvFilter` from an +/// environment variable. +#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] +#[derive(Debug)] +pub struct FromEnvError { + kind: ErrorKind, +} + +#[derive(Debug)] +enum ErrorKind { + Parse(ParseError), + Env(env::VarError), +} + +impl EnvFilter { + /// `RUST_LOG` is the default environment variable used by + /// [`EnvFilter::from_default_env`] and [`EnvFilter::try_from_default_env`]. + /// + /// [`EnvFilter::from_default_env`]: EnvFilter::from_default_env() + /// [`EnvFilter::try_from_default_env`]: EnvFilter::try_from_default_env() + pub const DEFAULT_ENV: &'static str = "RUST_LOG"; + + // === constructors, etc === + + /// Returns a [builder] that can be used to configure a new [`EnvFilter`] + /// instance. + /// + /// The [`Builder`] type is used to set additional configurations, such as + /// [whether regular expressions are enabled](Builder::with_regex) or [the + /// default directive](Builder::with_default_directive) before parsing an + /// [`EnvFilter`] from a string or environment variable. + /// + /// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html + pub fn builder() -> Builder { + Builder::default() + } + + /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment + /// variable, ignoring any invalid filter directives. + /// + /// If the environment variable is empty or not set, or if it contains only + /// invalid directives, a default directive enabling the [`ERROR`] level is + /// added. + /// + /// To set additional configuration options prior to parsing the filter, use + /// the [`Builder`] type instead. + /// + /// This function is equivalent to the following: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// # fn docs() -> EnvFilter { + /// EnvFilter::builder() + /// .with_default_directive(LevelFilter::ERROR.into()) + /// .from_env_lossy() + /// # } + /// ``` + /// + /// [`ERROR`]: tracing::Level::ERROR + pub fn from_default_env() -> Self { + Self::builder() + .with_default_directive(LevelFilter::ERROR.into()) + .from_env_lossy() + } + + /// Returns a new `EnvFilter` from the value of the given environment + /// variable, ignoring any invalid filter directives. + /// + /// If the environment variable is empty or not set, or if it contains only + /// invalid directives, a default directive enabling the [`ERROR`] level is + /// added. + /// + /// To set additional configuration options prior to parsing the filter, use + /// the [`Builder`] type instead. + /// + /// This function is equivalent to the following: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// # fn docs() -> EnvFilter { + /// # let env = ""; + /// EnvFilter::builder() + /// .with_default_directive(LevelFilter::ERROR.into()) + /// .with_env_var(env) + /// .from_env_lossy() + /// # } + /// ``` + /// + /// [`ERROR`]: tracing::Level::ERROR + pub fn from_env<A: AsRef<str>>(env: A) -> Self { + Self::builder() + .with_default_directive(LevelFilter::ERROR.into()) + .with_env_var(env.as_ref()) + .from_env_lossy() + } + + /// Returns a new `EnvFilter` from the directives in the given string, + /// ignoring any that are invalid. + /// + /// If the string is empty or contains only invalid directives, a default + /// directive enabling the [`ERROR`] level is added. + /// + /// To set additional configuration options prior to parsing the filter, use + /// the [`Builder`] type instead. + /// + /// This function is equivalent to the following: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// # fn docs() -> EnvFilter { + /// # let directives = ""; + /// EnvFilter::builder() + /// .with_default_directive(LevelFilter::ERROR.into()) + /// .parse_lossy(directives) + /// # } + /// ``` + /// + /// [`ERROR`]: tracing::Level::ERROR + pub fn new<S: AsRef<str>>(directives: S) -> Self { + Self::builder() + .with_default_directive(LevelFilter::ERROR.into()) + .parse_lossy(directives) + } + + /// Returns a new `EnvFilter` from the directives in the given string, + /// or an error if any are invalid. + /// + /// If the string is empty, a default directive enabling the [`ERROR`] level + /// is added. + /// + /// To set additional configuration options prior to parsing the filter, use + /// the [`Builder`] type instead. + /// + /// This function is equivalent to the following: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// + /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::ParseError> { + /// # let directives = ""; + /// EnvFilter::builder() + /// .with_default_directive(LevelFilter::ERROR.into()) + /// .parse(directives) + /// # } + /// ``` + /// + /// [`ERROR`]: tracing::Level::ERROR + pub fn try_new<S: AsRef<str>>(dirs: S) -> Result<Self, directive::ParseError> { + Self::builder().parse(dirs) + } + + /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment + /// variable, or an error if the environment variable is unset or contains + /// any invalid filter directives. + /// + /// To set additional configuration options prior to parsing the filter, use + /// the [`Builder`] type instead. + /// + /// This function is equivalent to the following: + /// + /// ```rust + /// use tracing_subscriber::EnvFilter; + /// + /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::FromEnvError> { + /// EnvFilter::builder().try_from_env() + /// # } + /// ``` + pub fn try_from_default_env() -> Result<Self, FromEnvError> { + Self::builder().try_from_env() + } + + /// Returns a new `EnvFilter` from the value of the given environment + /// variable, or an error if the environment variable is unset or contains + /// any invalid filter directives. + /// + /// To set additional configuration options prior to parsing the filter, use + /// the [`Builder`] type instead. + /// + /// This function is equivalent to the following: + /// + /// ```rust + /// use tracing_subscriber::EnvFilter; + /// + /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::FromEnvError> { + /// # let env = ""; + /// EnvFilter::builder().with_env_var(env).try_from_env() + /// # } + /// ``` + pub fn try_from_env<A: AsRef<str>>(env: A) -> Result<Self, FromEnvError> { + Self::builder().with_env_var(env.as_ref()).try_from_env() + } + + /// Add a filtering directive to this `EnvFilter`. + /// + /// The added directive will be used in addition to any previously set + /// directives, either added using this method or provided when the filter + /// is constructed. + /// + /// Filters may be created from [`LevelFilter`] or [`Level`], which will + /// enable all traces at or below a certain verbosity level, or + /// parsed from a string specifying a directive. + /// + /// If a filter directive is inserted that matches exactly the same spans + /// and events as a previous filter, but sets a different level for those + /// spans and events, the previous directive is overwritten. + /// + /// [`LevelFilter`]: super::LevelFilter + /// [`Level`]: tracing_core::Level + /// + /// # Examples + /// + /// From [`LevelFilter`]: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// let mut filter = EnvFilter::from_default_env() + /// .add_directive(LevelFilter::INFO.into()); + /// ``` + /// + /// Or from [`Level`]: + /// + /// ```rust + /// # use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// # use tracing::Level; + /// let mut filter = EnvFilter::from_default_env() + /// .add_directive(Level::INFO.into()); + /// ``` + /// + /// Parsed from a string: + /// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, Directive}; + /// + /// # fn try_mk_filter() -> Result<(), Box<dyn ::std::error::Error>> { + /// let mut filter = EnvFilter::try_from_default_env()? + /// .add_directive("my_crate::module=trace".parse()?) + /// .add_directive("my_crate::my_other_module::something=info".parse()?); + /// # Ok(()) + /// # } + /// ``` + /// In the above example, substitute `my_crate`, `module`, etc. with the + /// name your target crate/module is imported with. This might be + /// different from the package name in Cargo.toml (`-` is replaced by `_`). + /// Example, if the package name in your Cargo.toml is `MY-FANCY-LIB`, then + /// the corresponding Rust identifier would be `MY_FANCY_LIB`: + pub fn add_directive(mut self, mut directive: Directive) -> Self { + if !self.regex { + directive.deregexify(); + } + if let Some(stat) = directive.to_static() { + self.statics.add(stat) + } else { + self.has_dynamics = true; + self.dynamics.add(directive); + } + self + } + + // === filtering methods === + + /// Returns `true` if this `EnvFilter` would enable the provided `metadata` + /// in the current context. + /// + /// This is equivalent to calling the [`Layer::enabled`] or + /// [`Filter::enabled`] methods on `EnvFilter`'s implementations of those + /// traits, but it does not require the trait to be in scope. + pub fn enabled<S>(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { + let level = metadata.level(); + + // is it possible for a dynamic filter directive to enable this event? + // if not, we can avoid the thread loca'l access + iterating over the + // spans in the current scope. + if self.has_dynamics && self.dynamics.max_level >= *level { + if metadata.is_span() { + // If the metadata is a span, see if we care about its callsite. + let enabled_by_cs = self + .by_cs + .read() + .ok() + .map(|by_cs| by_cs.contains_key(&metadata.callsite())) + .unwrap_or(false); + if enabled_by_cs { + return true; + } + } + + let enabled_by_scope = { + let scope = self.scope.get_or_default().borrow(); + for filter in &*scope { + if filter >= level { + return true; + } + } + false + }; + if enabled_by_scope { + return true; + } + } + + // is it possible for a static filter directive to enable this event? + if self.statics.max_level >= *level { + // Otherwise, fall back to checking if the callsite is + // statically enabled. + return self.statics.enabled(metadata); + } + + false + } + + /// Returns an optional hint of the highest [verbosity level][level] that + /// this `EnvFilter` will enable. + /// + /// This is equivalent to calling the [`Layer::max_level_hint`] or + /// [`Filter::max_level_hint`] methods on `EnvFilter`'s implementations of those + /// traits, but it does not require the trait to be in scope. + /// + /// [level]: tracing_core::metadata::Level + pub fn max_level_hint(&self) -> Option<LevelFilter> { + if self.dynamics.has_value_filters() { + // If we perform any filtering on span field *values*, we will + // enable *all* spans, because their field values are not known + // until recording. + return Some(LevelFilter::TRACE); + } + std::cmp::max( + self.statics.max_level.into(), + self.dynamics.max_level.into(), + ) + } + + /// Informs the filter that a new span was created. + /// + /// This is equivalent to calling the [`Layer::on_new_span`] or + /// [`Filter::on_new_span`] methods on `EnvFilter`'s implementations of those + /// traits, but it does not require the trait to be in scope. + pub fn on_new_span<S>(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) { + let by_cs = try_lock!(self.by_cs.read()); + if let Some(cs) = by_cs.get(&attrs.metadata().callsite()) { + let span = cs.to_span_match(attrs); + try_lock!(self.by_id.write()).insert(id.clone(), span); + } + } + + /// Informs the filter that the span with the provided `id` was entered. + /// + /// This is equivalent to calling the [`Layer::on_enter`] or + /// [`Filter::on_enter`] methods on `EnvFilter`'s implementations of those + /// traits, but it does not require the trait to be in scope. + pub fn on_enter<S>(&self, id: &span::Id, _: Context<'_, S>) { + // XXX: This is where _we_ could push IDs to the stack instead, and use + // that to allow changing the filter while a span is already entered. + // But that might be much less efficient... + if let Some(span) = try_lock!(self.by_id.read()).get(id) { + self.scope.get_or_default().borrow_mut().push(span.level()); + } + } + + /// Informs the filter that the span with the provided `id` was exited. + /// + /// This is equivalent to calling the [`Layer::on_exit`] or + /// [`Filter::on_exit`] methods on `EnvFilter`'s implementations of those + /// traits, but it does not require the trait to be in scope. + pub fn on_exit<S>(&self, id: &span::Id, _: Context<'_, S>) { + if self.cares_about_span(id) { + self.scope.get_or_default().borrow_mut().pop(); + } + } + + /// Informs the filter that the span with the provided `id` was closed. + /// + /// This is equivalent to calling the [`Layer::on_close`] or + /// [`Filter::on_close`] methods on `EnvFilter`'s implementations of those + /// traits, but it does not require the trait to be in scope. + pub fn on_close<S>(&self, id: span::Id, _: Context<'_, S>) { + // If we don't need to acquire a write lock, avoid doing so. + if !self.cares_about_span(&id) { + return; + } + + let mut spans = try_lock!(self.by_id.write()); + spans.remove(&id); + } + + /// Informs the filter that the span with the provided `id` recorded the + /// provided field `values`. + /// + /// This is equivalent to calling the [`Layer::on_record`] or + /// [`Filter::on_record`] methods on `EnvFilter`'s implementations of those + /// traits, but it does not require the trait to be in scope + pub fn on_record<S>(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) { + if let Some(span) = try_lock!(self.by_id.read()).get(id) { + span.record_update(values); + } + } + + fn cares_about_span(&self, span: &span::Id) -> bool { + let spans = try_lock!(self.by_id.read(), else return false); + spans.contains_key(span) + } + + fn base_interest(&self) -> Interest { + if self.has_dynamics { + Interest::sometimes() + } else { + Interest::never() + } + } + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + if self.has_dynamics && metadata.is_span() { + // If this metadata describes a span, first, check if there is a + // dynamic filter that should be constructed for it. If so, it + // should always be enabled, since it influences filtering. + if let Some(matcher) = self.dynamics.matcher(metadata) { + let mut by_cs = try_lock!(self.by_cs.write(), else return self.base_interest()); + by_cs.insert(metadata.callsite(), matcher); + return Interest::always(); + } + } + + // Otherwise, check if any of our static filters enable this metadata. + if self.statics.enabled(metadata) { + Interest::always() + } else { + self.base_interest() + } + } +} + +impl<S: Subscriber> Layer<S> for EnvFilter { + #[inline] + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + EnvFilter::register_callsite(self, metadata) + } + + #[inline] + fn max_level_hint(&self) -> Option<LevelFilter> { + EnvFilter::max_level_hint(self) + } + + #[inline] + fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { + self.enabled(metadata, ctx) + } + + #[inline] + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { + self.on_new_span(attrs, id, ctx) + } + + #[inline] + fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { + self.on_record(id, values, ctx); + } + + #[inline] + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { + self.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { + self.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { + self.on_close(id, ctx); + } +} + +feature! { + #![all(feature = "registry", feature = "std")] + use crate::layer::Filter; + + impl<S> Filter<S> for EnvFilter { + #[inline] + fn enabled(&self, meta: &Metadata<'_>, ctx: &Context<'_, S>) -> bool { + self.enabled(meta, ctx.clone()) + } + + #[inline] + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + self.register_callsite(meta) + } + + #[inline] + fn max_level_hint(&self) -> Option<LevelFilter> { + EnvFilter::max_level_hint(self) + } + + #[inline] + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { + self.on_new_span(attrs, id, ctx) + } + + #[inline] + fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { + self.on_record(id, values, ctx); + } + + #[inline] + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { + self.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { + self.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { + self.on_close(id, ctx); + } + } +} + +impl FromStr for EnvFilter { + type Err = directive::ParseError; + + fn from_str(spec: &str) -> Result<Self, Self::Err> { + Self::try_new(spec) + } +} + +impl<S> From<S> for EnvFilter +where + S: AsRef<str>, +{ + fn from(s: S) -> Self { + Self::new(s) + } +} + +impl Default for EnvFilter { + fn default() -> Self { + Builder::default().from_directives(std::iter::empty()) + } +} + +impl fmt::Display for EnvFilter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut statics = self.statics.iter(); + let wrote_statics = if let Some(next) = statics.next() { + fmt::Display::fmt(next, f)?; + for directive in statics { + write!(f, ",{}", directive)?; + } + true + } else { + false + }; + + let mut dynamics = self.dynamics.iter(); + if let Some(next) = dynamics.next() { + if wrote_statics { + f.write_str(",")?; + } + fmt::Display::fmt(next, f)?; + for directive in dynamics { + write!(f, ",{}", directive)?; + } + } + Ok(()) + } +} + +// ===== impl FromEnvError ===== + +impl From<directive::ParseError> for FromEnvError { + fn from(p: directive::ParseError) -> Self { + Self { + kind: ErrorKind::Parse(p), + } + } +} + +impl From<env::VarError> for FromEnvError { + fn from(v: env::VarError) -> Self { + Self { + kind: ErrorKind::Env(v), + } + } +} + +impl fmt::Display for FromEnvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + ErrorKind::Parse(ref p) => p.fmt(f), + ErrorKind::Env(ref e) => e.fmt(f), + } + } +} + +impl Error for FromEnvError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self.kind { + ErrorKind::Parse(ref p) => Some(p), + ErrorKind::Env(ref e) => Some(e), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tracing_core::field::FieldSet; + use tracing_core::*; + + struct NoSubscriber; + impl Subscriber for NoSubscriber { + #[inline] + fn register_callsite(&self, _: &'static Metadata<'static>) -> subscriber::Interest { + subscriber::Interest::always() + } + fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { + span::Id::from_u64(0xDEAD) + } + fn event(&self, _event: &Event<'_>) {} + fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {} + fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {} + + #[inline] + fn enabled(&self, _metadata: &Metadata<'_>) -> bool { + true + } + fn enter(&self, _span: &span::Id) {} + fn exit(&self, _span: &span::Id) {} + } + + struct Cs; + impl Callsite for Cs { + fn set_interest(&self, _interest: Interest) {} + fn metadata(&self) -> &Metadata<'_> { + unimplemented!() + } + } + + #[test] + fn callsite_enabled_no_span_directive() { + let filter = EnvFilter::new("app=debug").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&[], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_never()); + } + + #[test] + fn callsite_off() { + let filter = EnvFilter::new("app=off").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::ERROR, + None, + None, + None, + FieldSet::new(&[], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_never()); + } + + #[test] + fn callsite_enabled_includes_span_directive() { + let filter = EnvFilter::new("app[mySpan]=debug").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&[], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_always()); + } + + #[test] + fn callsite_enabled_includes_span_directive_field() { + let filter = + EnvFilter::new("app[mySpan{field=\"value\"}]=debug").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&["field"], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_always()); + } + + #[test] + fn callsite_enabled_includes_span_directive_multiple_fields() { + let filter = EnvFilter::new("app[mySpan{field=\"value\",field2=2}]=debug") + .with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&["field"], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_never()); + } + + #[test] + fn roundtrip() { + let f1: EnvFilter = + "[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug" + .parse() + .unwrap(); + let f2: EnvFilter = format!("{}", f1).parse().unwrap(); + assert_eq!(f1.statics, f2.statics); + assert_eq!(f1.dynamics, f2.dynamics); + } + + #[test] + fn size_of_filters() { + fn print_sz(s: &str) { + let filter = s.parse::<EnvFilter>().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", + ); + + print_sz("[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug"); + + print_sz( + "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ + crate2=debug,crate3=trace,crate3::mod2::mod1=off,[span1{foo=1}]=error,\ + [span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug", + ); + } + + #[test] + fn parse_empty_string() { + // There is no corresponding test for [`Builder::parse_lossy`] as failed + // parsing does not produce any observable side effects. If this test fails + // check that [`Builder::parse_lossy`] is behaving correctly as well. + assert!(EnvFilter::builder().parse("").is_ok()); + } +} diff --git a/vendor/tracing-subscriber/src/filter/filter_fn.rs b/vendor/tracing-subscriber/src/filter/filter_fn.rs new file mode 100644 index 000000000..332bf860a --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/filter_fn.rs @@ -0,0 +1,749 @@ +use crate::{ + filter::LevelFilter, + layer::{Context, Layer}, +}; +use core::{any::type_name, fmt, marker::PhantomData}; +use tracing_core::{Interest, Metadata, Subscriber}; + +/// A filter implemented by a closure or function pointer that +/// determines whether a given span or event is enabled, based on its +/// [`Metadata`]. +/// +/// 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. +/// +/// [`Metadata`]: tracing_core::Metadata +/// [`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 +#[derive(Clone)] +pub struct FilterFn<F = fn(&Metadata<'_>) -> bool> { + enabled: F, + max_level_hint: Option<LevelFilter>, +} + +/// A filter implemented by a closure or function pointer that +/// determines whether a given span or event is enabled _dynamically_, +/// potentially based on the current [span context]. +/// +/// 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. +/// +/// [span context]: crate::layer::Context +/// [`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 +pub struct DynFilterFn< + S, + // TODO(eliza): should these just be boxed functions? + F = fn(&Metadata<'_>, &Context<'_, S>) -> bool, + R = fn(&'static Metadata<'static>) -> Interest, +> { + enabled: F, + register_callsite: Option<R>, + max_level_hint: Option<LevelFilter>, + _s: PhantomData<fn(S)>, +} + +// === impl FilterFn === + +/// Constructs a [`FilterFn`], from a function or closure that returns `true` if +/// a span or event should be enabled, based on its [`Metadata`]. +/// +/// The returned [`FilterFn`] 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. +/// +/// This is equivalent to calling [`FilterFn::new`]. +/// +/// [`Metadata`]: tracing_core::Metadata +/// [`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 +/// +/// # Examples +/// +/// ``` +/// use tracing_subscriber::{ +/// layer::{Layer, SubscriberExt}, +/// filter, +/// util::SubscriberInitExt, +/// }; +/// +/// let my_filter = filter::filter_fn(|metadata| { +/// // Only enable spans or events with the target "interesting_things" +/// metadata.target() == "interesting_things" +/// }); +/// +/// let my_layer = tracing_subscriber::fmt::layer(); +/// +/// tracing_subscriber::registry() +/// .with(my_layer.with_filter(my_filter)) +/// .init(); +/// +/// // This event will not be enabled. +/// tracing::warn!("something important but uninteresting happened!"); +/// +/// // This event will be enabled. +/// tracing::debug!(target: "interesting_things", "an interesting minor detail..."); +/// ``` +pub fn filter_fn<F>(f: F) -> FilterFn<F> +where + F: Fn(&Metadata<'_>) -> bool, +{ + FilterFn::new(f) +} + +/// Constructs a [`DynFilterFn`] from a function or closure that returns `true` +/// if a span or event should be enabled within a particular [span context][`Context`]. +/// +/// This is equivalent to calling [`DynFilterFn::new`]. +/// +/// Unlike [`filter_fn`], this function takes a closure or function pointer +/// taking the [`Metadata`] for a span or event *and* the current [`Context`]. +/// This means that a [`DynFilterFn`] can choose whether to enable spans or +/// events based on information about the _current_ span (or its parents). +/// +/// If this is *not* necessary, use [`filter_fn`] instead. +/// +/// The returned [`DynFilterFn`] 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. +/// +/// # Examples +/// +/// ``` +/// use tracing_subscriber::{ +/// layer::{Layer, SubscriberExt}, +/// filter, +/// util::SubscriberInitExt, +/// }; +/// +/// // Only enable spans or events within a span named "interesting_span". +/// let my_filter = filter::dynamic_filter_fn(|metadata, cx| { +/// // If this *is* "interesting_span", make sure to enable it. +/// if metadata.is_span() && metadata.name() == "interesting_span" { +/// return true; +/// } +/// +/// // Otherwise, are we in an interesting span? +/// if let Some(current_span) = cx.lookup_current() { +/// return current_span.name() == "interesting_span"; +/// } +/// +/// false +/// }); +/// +/// let my_layer = tracing_subscriber::fmt::layer(); +/// +/// tracing_subscriber::registry() +/// .with(my_layer.with_filter(my_filter)) +/// .init(); +/// +/// // This event will not be enabled. +/// tracing::info!("something happened"); +/// +/// tracing::info_span!("interesting_span").in_scope(|| { +/// // This event will be enabled. +/// tracing::debug!("something else happened"); +/// }); +/// ``` +/// +/// [`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 +/// [`Context`]: crate::layer::Context +/// [`Metadata`]: tracing_core::Metadata +pub fn dynamic_filter_fn<S, F>(f: F) -> DynFilterFn<S, F> +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + DynFilterFn::new(f) +} + +impl<F> FilterFn<F> +where + F: Fn(&Metadata<'_>) -> bool, +{ + /// Constructs a [`FilterFn`] from a function or closure that returns `true` + /// if a span or event should be enabled, based on its [`Metadata`]. + /// + /// If determining whether a span or event should be enabled also requires + /// information about the current span context, use [`DynFilterFn`] instead. + /// + /// See the [documentation on per-layer filtering][plf] for details on using + /// [`Filter`]s. + /// + /// [`Filter`]: crate::layer::Filter + /// [plf]: crate::layer#per-layer-filtering + /// [`Metadata`]: tracing_core::Metadata + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::FilterFn, + /// util::SubscriberInitExt, + /// }; + /// + /// let my_filter = FilterFn::new(|metadata| { + /// // Only enable spans or events with the target "interesting_things" + /// metadata.target() == "interesting_things" + /// }); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// + /// // This event will not be enabled. + /// tracing::warn!("something important but uninteresting happened!"); + /// + /// // This event will be enabled. + /// tracing::debug!(target: "interesting_things", "an interesting minor detail..."); + /// ``` + pub fn new(enabled: F) -> Self { + Self { + enabled, + max_level_hint: None, + } + } + + /// Sets the highest verbosity [`Level`] the filter function will enable. + /// + /// The value passed to this method will be returned by this `FilterFn`'s + /// [`Filter::max_level_hint`] method. + /// + /// If the provided function will not enable all levels, it is recommended + /// to call this method to configure it with the most verbose level it will + /// enable. + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::{filter_fn, LevelFilter}, + /// util::SubscriberInitExt, + /// }; + /// use tracing_core::Level; + /// + /// let my_filter = filter_fn(|metadata| { + /// // Only enable spans or events with targets starting with `my_crate` + /// // and levels at or below `INFO`. + /// metadata.level() <= &Level::INFO && metadata.target().starts_with("my_crate") + /// }) + /// // Since the filter closure will only enable the `INFO` level and + /// // below, set the max level hint + /// .with_max_level_hint(LevelFilter::INFO); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// ``` + /// + /// [`Level`]: tracing_core::Level + /// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint + pub fn with_max_level_hint(self, max_level_hint: impl Into<LevelFilter>) -> Self { + Self { + max_level_hint: Some(max_level_hint.into()), + ..self + } + } + + #[inline] + pub(in crate::filter) fn is_enabled(&self, metadata: &Metadata<'_>) -> bool { + let enabled = (self.enabled)(metadata); + debug_assert!( + !enabled || self.is_below_max_level(metadata), + "FilterFn<{}> claimed it would only enable {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::<F>(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + + enabled + } + + #[inline] + pub(in crate::filter) fn is_callsite_enabled( + &self, + metadata: &'static Metadata<'static>, + ) -> Interest { + // Because `self.enabled` takes a `Metadata` only (and no `Context` + // parameter), we can reasonably assume its results are cachable, and + // just return `Interest::always`/`Interest::never`. + if (self.enabled)(metadata) { + debug_assert!( + self.is_below_max_level(metadata), + "FilterFn<{}> claimed it was only interested in {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::<F>(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + return Interest::always(); + } + + Interest::never() + } + + fn is_below_max_level(&self, metadata: &Metadata<'_>) -> bool { + self.max_level_hint + .as_ref() + .map(|hint| metadata.level() <= hint) + .unwrap_or(true) + } +} + +impl<S, F> Layer<S> for FilterFn<F> +where + F: Fn(&Metadata<'_>) -> bool + 'static, + S: Subscriber, +{ + fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { + self.is_enabled(metadata) + } + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + self.max_level_hint + } +} + +impl<F> From<F> for FilterFn<F> +where + F: Fn(&Metadata<'_>) -> bool, +{ + fn from(enabled: F) -> Self { + Self::new(enabled) + } +} + +impl<F> fmt::Debug for FilterFn<F> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterFn") + .field("enabled", &format_args!("{}", type_name::<F>())) + .field("max_level_hint", &self.max_level_hint) + .finish() + } +} + +// === impl DynFilterFn == + +impl<S, F> DynFilterFn<S, F> +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + /// Constructs a [`Filter`] from a function or closure that returns `true` + /// if a span or event should be enabled in the current [span + /// context][`Context`]. + /// + /// Unlike [`FilterFn`], a `DynFilterFn` is constructed from a closure or + /// function pointer that takes both the [`Metadata`] for a span or event + /// *and* the current [`Context`]. This means that a [`DynFilterFn`] can + /// choose whether to enable spans or events based on information about the + /// _current_ span (or its parents). + /// + /// If this is *not* necessary, use [`FilterFn`] instead. + /// + /// See the [documentation on per-layer filtering][plf] for details on using + /// [`Filter`]s. + /// + /// [`Filter`]: crate::layer::Filter + /// [plf]: crate::layer#per-layer-filtering + /// [`Context`]: crate::layer::Context + /// [`Metadata`]: tracing_core::Metadata + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::DynFilterFn, + /// util::SubscriberInitExt, + /// }; + /// + /// // Only enable spans or events within a span named "interesting_span". + /// let my_filter = DynFilterFn::new(|metadata, cx| { + /// // If this *is* "interesting_span", make sure to enable it. + /// if metadata.is_span() && metadata.name() == "interesting_span" { + /// return true; + /// } + /// + /// // Otherwise, are we in an interesting span? + /// if let Some(current_span) = cx.lookup_current() { + /// return current_span.name() == "interesting_span"; + /// } + /// + /// false + /// }); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// + /// // This event will not be enabled. + /// tracing::info!("something happened"); + /// + /// tracing::info_span!("interesting_span").in_scope(|| { + /// // This event will be enabled. + /// tracing::debug!("something else happened"); + /// }); + /// ``` + pub fn new(enabled: F) -> Self { + Self { + enabled, + register_callsite: None, + max_level_hint: None, + _s: PhantomData, + } + } +} + +impl<S, F, R> DynFilterFn<S, F, R> +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + /// Sets the highest verbosity [`Level`] the filter function will enable. + /// + /// The value passed to this method will be returned by this `DynFilterFn`'s + /// [`Filter::max_level_hint`] method. + /// + /// If the provided function will not enable all levels, it is recommended + /// to call this method to configure it with the most verbose level it will + /// enable. + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::{DynFilterFn, LevelFilter}, + /// util::SubscriberInitExt, + /// }; + /// use tracing_core::Level; + /// + /// // Only enable spans or events with levels at or below `INFO`, if + /// // we are inside a span called "interesting_span". + /// let my_filter = DynFilterFn::new(|metadata, cx| { + /// // If the level is greater than INFO, disable it. + /// if metadata.level() > &Level::INFO { + /// return false; + /// } + /// + /// // If any span in the current scope is named "interesting_span", + /// // enable this span or event. + /// for span in cx.lookup_current().iter().flat_map(|span| span.scope()) { + /// if span.name() == "interesting_span" { + /// return true; + /// } + /// } + /// + /// // Otherwise, disable it. + /// false + /// }) + /// // Since the filter closure will only enable the `INFO` level and + /// // below, set the max level hint + /// .with_max_level_hint(LevelFilter::INFO); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// ``` + /// + /// [`Level`]: tracing_core::Level + /// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint + pub fn with_max_level_hint(self, max_level_hint: impl Into<LevelFilter>) -> Self { + Self { + max_level_hint: Some(max_level_hint.into()), + ..self + } + } + + /// Adds a function for filtering callsites to this filter. + /// + /// When this filter's [`Filter::callsite_enabled`][cse] method is called, + /// the provided function will be used rather than the default. + /// + /// By default, `DynFilterFn` assumes that, because the filter _may_ depend + /// dynamically on the current [span context], its result should never be + /// cached. However, some filtering strategies may require dynamic information + /// from the current span context in *some* cases, but are able to make + /// static filtering decisions from [`Metadata`] alone in others. + /// + /// For example, consider the filter given in the example for + /// [`DynFilterFn::new`]. That filter enables all spans named + /// "interesting_span", and any events and spans that occur inside of an + /// interesting span. Since the span's name is part of its static + /// [`Metadata`], the "interesting_span" can be enabled in + /// [`callsite_enabled`][cse]: + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::DynFilterFn, + /// util::SubscriberInitExt, + /// }; + /// use tracing_core::subscriber::Interest; + /// + /// // Only enable spans or events within a span named "interesting_span". + /// let my_filter = DynFilterFn::new(|metadata, cx| { + /// // If this *is* "interesting_span", make sure to enable it. + /// if metadata.is_span() && metadata.name() == "interesting_span" { + /// return true; + /// } + /// + /// // Otherwise, are we in an interesting span? + /// if let Some(current_span) = cx.lookup_current() { + /// return current_span.name() == "interesting_span"; + /// } + /// + /// false + /// }).with_callsite_filter(|metadata| { + /// // If this is an "interesting_span", we know we will always + /// // enable it. + /// if metadata.is_span() && metadata.name() == "interesting_span" { + /// return Interest::always(); + /// } + /// + /// // Otherwise, it depends on whether or not we're in an interesting + /// // span. You'll have to ask us again for each span/event! + /// Interest::sometimes() + /// }); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// ``` + /// + /// [cse]: crate::layer::Filter::callsite_enabled + /// [`enabled`]: crate::layer::Filter::enabled + /// [`Metadata`]: tracing_core::Metadata + /// [span context]: crate::layer::Context + pub fn with_callsite_filter<R2>(self, callsite_enabled: R2) -> DynFilterFn<S, F, R2> + where + R2: Fn(&'static Metadata<'static>) -> Interest, + { + let register_callsite = Some(callsite_enabled); + let DynFilterFn { + enabled, + max_level_hint, + _s, + .. + } = self; + DynFilterFn { + enabled, + register_callsite, + max_level_hint, + _s, + } + } + + fn default_callsite_enabled(&self, metadata: &Metadata<'_>) -> Interest { + // If it's below the configured max level, assume that `enabled` will + // never enable it... + if !is_below_max_level(&self.max_level_hint, metadata) { + debug_assert!( + !(self.enabled)(metadata, &Context::none()), + "DynFilterFn<{}> claimed it would only enable {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::<F>(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + return Interest::never(); + } + + // Otherwise, since this `enabled` function is dynamic and depends on + // the current context, we don't know whether this span or event will be + // enabled or not. Ask again every time it's recorded! + Interest::sometimes() + } +} + +impl<S, F, R> DynFilterFn<S, F, R> +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, + R: Fn(&'static Metadata<'static>) -> Interest, +{ + #[inline] + fn is_enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + let enabled = (self.enabled)(metadata, cx); + debug_assert!( + !enabled || is_below_max_level(&self.max_level_hint, metadata), + "DynFilterFn<{}> claimed it would only enable {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::<F>(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + + enabled + } + + #[inline] + fn is_callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + let interest = self + .register_callsite + .as_ref() + .map(|callsite_enabled| callsite_enabled(metadata)) + .unwrap_or_else(|| self.default_callsite_enabled(metadata)); + debug_assert!( + interest.is_never() || is_below_max_level(&self.max_level_hint, metadata), + "DynFilterFn<{}, {}> claimed it was only interested in {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::<F>(), + type_name::<R>(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + + interest + } +} + +impl<S, F, R> Layer<S> for DynFilterFn<S, F, R> +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool + 'static, + R: Fn(&'static Metadata<'static>) -> Interest + 'static, + S: Subscriber, +{ + fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool { + self.is_enabled(metadata, &cx) + } + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + self.max_level_hint + } +} + +impl<S, F, R> fmt::Debug for DynFilterFn<S, F, R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("DynFilterFn"); + s.field("enabled", &format_args!("{}", type_name::<F>())); + if self.register_callsite.is_some() { + s.field( + "register_callsite", + &format_args!("Some({})", type_name::<R>()), + ); + } else { + s.field("register_callsite", &format_args!("None")); + } + + s.field("max_level_hint", &self.max_level_hint).finish() + } +} + +impl<S, F, R> Clone for DynFilterFn<S, F, R> +where + F: Clone, + R: Clone, +{ + fn clone(&self) -> Self { + Self { + enabled: self.enabled.clone(), + register_callsite: self.register_callsite.clone(), + max_level_hint: self.max_level_hint, + _s: PhantomData, + } + } +} + +impl<F, S> From<F> for DynFilterFn<S, F> +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + fn from(f: F) -> Self { + Self::new(f) + } +} + +// === PLF impls === + +feature! { + #![all(feature = "registry", feature = "std")] + use crate::layer::Filter; + + impl<S, F> Filter<S> for FilterFn<F> + where + F: Fn(&Metadata<'_>) -> bool, + { + fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, S>) -> bool { + self.is_enabled(metadata) + } + + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + self.max_level_hint + } + } + + impl<S, F, R> Filter<S> for DynFilterFn<S, F, R> + where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, + R: Fn(&'static Metadata<'static>) -> Interest, + { + fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.is_enabled(metadata, cx) + } + + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + self.max_level_hint + } + } +} + +fn is_below_max_level(hint: &Option<LevelFilter>, metadata: &Metadata<'_>) -> bool { + hint.as_ref() + .map(|hint| metadata.level() <= hint) + .unwrap_or(true) +} diff --git a/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs b/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs new file mode 100644 index 000000000..3934a1326 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs @@ -0,0 +1,542 @@ +//! Filter combinators +use crate::layer::{Context, Filter}; +use std::{cmp, fmt, marker::PhantomData}; +use tracing_core::{ + span::{Attributes, Id, Record}, + subscriber::Interest, + LevelFilter, Metadata, +}; + +/// Combines two [`Filter`]s so that spans and events are enabled if and only if +/// *both* filters return `true`. +/// +/// This type is typically returned by the [`FilterExt::and`] method. See that +/// method's documentation for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [`FilterExt::and`]: crate::filter::FilterExt::and +pub struct And<A, B, S> { + a: A, + b: B, + _s: PhantomData<fn(S)>, +} + +/// Combines two [`Filter`]s so that spans and events are enabled if *either* filter +/// returns `true`. +/// +/// This type is typically returned by the [`FilterExt::or`] method. See that +/// method's documentation for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [`FilterExt::or`]: crate::filter::FilterExt::or +pub struct Or<A, B, S> { + a: A, + b: B, + _s: PhantomData<fn(S)>, +} + +/// Inverts the result of a [`Filter`]. +/// +/// If the wrapped filter would enable a span or event, it will be disabled. If +/// it would disable a span or event, that span or event will be enabled. +/// +/// This type is typically returned by the [`FilterExt::not`] method. See that +/// method's documentation for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [`FilterExt::not`]: crate::filter::FilterExt::not +pub struct Not<A, S> { + a: A, + _s: PhantomData<fn(S)>, +} + +// === impl And === + +impl<A, B, S> And<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + /// Combines two [`Filter`]s so that spans and events are enabled if and only if + /// *both* filters return `true`. + /// + /// # Examples + /// + /// Enabling spans or events if they have both a particular target *and* are + /// above a certain level: + /// + /// ```ignore + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, combinator::And}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together so that a span or event is only enabled + /// // if *both* filters would enable it: + /// let filter = And::new(level_filter, target_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::info!("an event with an uninteresting target"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event will *not* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + pub(crate) fn new(a: A, b: B) -> Self { + Self { + a, + b, + _s: PhantomData, + } + } +} + +impl<A, B, S> Filter<S> for And<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.a.enabled(meta, cx) && self.b.enabled(meta, cx) + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + let a = self.a.callsite_enabled(meta); + if a.is_never() { + return a; + } + + let b = self.b.callsite_enabled(meta); + + if !b.is_always() { + return b; + } + + a + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + // If either hint is `None`, return `None`. Otherwise, return the most restrictive. + cmp::min(self.a.max_level_hint(), self.b.max_level_hint()) + } + + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx) + } + + #[inline] + fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + self.a.on_new_span(attrs, id, ctx.clone()); + self.b.on_new_span(attrs, id, ctx) + } + + #[inline] + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + self.a.on_record(id, values, ctx.clone()); + self.b.on_record(id, values, ctx); + } + + #[inline] + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_enter(id, ctx.clone()); + self.b.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_exit(id, ctx.clone()); + self.b.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + self.a.on_close(id.clone(), ctx.clone()); + self.b.on_close(id, ctx); + } +} + +impl<A, B, S> Clone for And<A, B, S> +where + A: Clone, + B: Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + b: self.b.clone(), + _s: PhantomData, + } + } +} + +impl<A, B, S> fmt::Debug for And<A, B, S> +where + A: fmt::Debug, + B: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("And") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } +} + +// === impl Or === + +impl<A, B, S> Or<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter + /// returns `true`. + /// + /// # Examples + /// + /// Enabling spans and events at the `INFO` level and above, and all spans + /// and events with a particular target: + /// + /// ```ignore + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, combinator::Or}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together so that a span or event is enabled + /// // if it is at INFO or lower, or if it has a target starting with + /// // `interesting_target`. + /// let filter = Or::new(level_filter, target_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::debug!("an uninteresting event"); + /// + /// // This event *will* be enabled: + /// tracing::info!("an uninteresting INFO event"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event *will* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// Enabling a higher level for a particular target by using `Or` in + /// conjunction with the [`And`] combinator: + /// + /// ```ignore + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, combinator}, + /// prelude::*, + /// }; + /// + /// // This filter will enable spans and events with targets beginning with + /// // `my_crate`: + /// let my_crate = filter_fn(|meta| { + /// meta.target().starts_with("my_crate") + /// }); + /// + /// // Combine the `my_crate` filter with a `LevelFilter` to produce a filter + /// // that will enable the `INFO` level and lower for spans and events with + /// // `my_crate` targets: + /// let filter = combinator::And::new(my_crate, LevelFilter::INFO); + /// + /// // If a span or event *doesn't* have a target beginning with + /// // `my_crate`, enable it if it has the `WARN` level or lower: + /// // let filter = combinator::Or::new(filter, LevelFilter::WARN); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + pub(crate) fn new(a: A, b: B) -> Self { + Self { + a, + b, + _s: PhantomData, + } + } +} + +impl<A, B, S> Filter<S> for Or<A, B, S> +where + A: Filter<S>, + B: Filter<S>, +{ + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.a.enabled(meta, cx) || self.b.enabled(meta, cx) + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + let a = self.a.callsite_enabled(meta); + let b = self.b.callsite_enabled(meta); + + // If either filter will always enable the span or event, return `always`. + if a.is_always() || b.is_always() { + return Interest::always(); + } + + // Okay, if either filter will sometimes enable the span or event, + // return `sometimes`. + if a.is_sometimes() || b.is_sometimes() { + return Interest::sometimes(); + } + + debug_assert!( + a.is_never() && b.is_never(), + "if neither filter was `always` or `sometimes`, both must be `never` (a={:?}; b={:?})", + a, + b, + ); + Interest::never() + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + // If either hint is `None`, return `None`. Otherwise, return the less restrictive. + Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?)) + } + + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx) + } + + #[inline] + fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + self.a.on_new_span(attrs, id, ctx.clone()); + self.b.on_new_span(attrs, id, ctx) + } + + #[inline] + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + self.a.on_record(id, values, ctx.clone()); + self.b.on_record(id, values, ctx); + } + + #[inline] + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_enter(id, ctx.clone()); + self.b.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_exit(id, ctx.clone()); + self.b.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + self.a.on_close(id.clone(), ctx.clone()); + self.b.on_close(id, ctx); + } +} + +impl<A, B, S> Clone for Or<A, B, S> +where + A: Clone, + B: Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + b: self.b.clone(), + _s: PhantomData, + } + } +} + +impl<A, B, S> fmt::Debug for Or<A, B, S> +where + A: fmt::Debug, + B: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Or") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } +} + +// === impl Not === + +impl<A, S> Not<A, S> +where + A: Filter<S>, +{ + /// Inverts the result of a [`Filter`]. + /// + /// If the wrapped filter would enable a span or event, it will be disabled. If + /// it would disable a span or event, that span or event will be enabled. + /// + /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] + /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as + /// filters which do not implement filtering on event field values will return + /// the default `true` even for events that their [`enabled`] method disables. + /// + /// Consider a normal filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => on_span(), + /// SOMETIMES => if enabled() { on_span() }, + /// NEVER => (), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => on_event(), + /// SOMETIMES => if enabled() && event_enabled() { on_event() }, + /// NEVER => (), + /// } + /// ``` + /// + /// and an inverted filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_span() }, + /// NEVER => on_span(), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_event() }, + /// NEVER => on_event(), + /// } + /// ``` + /// + /// A proper inversion would do `!(enabled() && event_enabled())` (or + /// `!enabled() || !event_enabled()`), but because of the implicit `&&` + /// relation between `enabled` and `event_enabled`, it is difficult to + /// short circuit and not call the wrapped `event_enabled`. + /// + /// A combinator which remembers the result of `enabled` in order to call + /// `event_enabled` only when `enabled() == true` is possible, but requires + /// additional thread-local mutable state to support a very niche use case. + // + // Also, it'd mean the wrapped layer's `enabled()` always gets called and + // globally applied to events where it doesn't today, since we can't know + // what `event_enabled` will say until we have the event to call it with. + /// + /// [`Filter`]: crate::layer::Filter + /// [`enabled`]: crate::layer::Filter::enabled + /// [`event_enabled`]: crate::layer::Filter::event_enabled + /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled + pub(crate) fn new(a: A) -> Self { + Self { a, _s: PhantomData } + } +} + +impl<A, S> Filter<S> for Not<A, S> +where + A: Filter<S>, +{ + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + !self.a.enabled(meta, cx) + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + match self.a.callsite_enabled(meta) { + i if i.is_always() => Interest::never(), + i if i.is_never() => Interest::always(), + _ => Interest::sometimes(), + } + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + // TODO(eliza): figure this out??? + None + } + + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + // Never disable based on event_enabled; we "disabled" it in `enabled`, + // so the `not` has already been applied and filtered this not out. + let _ = (event, cx); + true + } + + #[inline] + fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + self.a.on_new_span(attrs, id, ctx); + } + + #[inline] + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + self.a.on_record(id, values, ctx.clone()); + } + + #[inline] + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_enter(id, ctx); + } + + #[inline] + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + self.a.on_exit(id, ctx); + } + + #[inline] + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + self.a.on_close(id, ctx); + } +} + +impl<A, S> Clone for Not<A, S> +where + A: Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + _s: PhantomData, + } + } +} + +impl<A, S> fmt::Debug for Not<A, S> +where + A: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Not").field(&self.a).finish() + } +} diff --git a/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs new file mode 100644 index 000000000..e50ee6f00 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -0,0 +1,1220 @@ +//! ## Per-Layer Filtering +//! +//! Per-layer filters permit individual `Layer`s to have their own filter +//! configurations without interfering with other `Layer`s. +//! +//! This module is not public; the public APIs defined in this module are +//! re-exported in the top-level `filter` module. Therefore, this documentation +//! primarily concerns the internal implementation details. For the user-facing +//! public API documentation, see the individual public types in this module, as +//! well as the, see the `Layer` trait documentation's [per-layer filtering +//! section]][1]. +//! +//! ## How does per-layer filtering work? +//! +//! As described in the API documentation, the [`Filter`] trait defines a +//! filtering strategy for a per-layer filter. We expect there will be a variety +//! of implementations of [`Filter`], both in `tracing-subscriber` and in user +//! code. +//! +//! To actually *use* a [`Filter`] implementation, it is combined with a +//! [`Layer`] by the [`Filtered`] struct defined in this module. [`Filtered`] +//! implements [`Layer`] by calling into the wrapped [`Layer`], or not, based on +//! the filtering strategy. While there will be a variety of types that implement +//! [`Filter`], all actual *uses* of per-layer filtering will occur through the +//! [`Filtered`] struct. Therefore, most of the implementation details live +//! there. +//! +//! [1]: crate::layer#per-layer-filtering +//! [`Filter`]: crate::layer::Filter +use crate::{ + filter::LevelFilter, + layer::{self, Context, Layer}, + registry, +}; +use std::{ + any::TypeId, + cell::{Cell, RefCell}, + fmt, + marker::PhantomData, + ops::Deref, + sync::Arc, + thread_local, +}; +use tracing_core::{ + span, + subscriber::{Interest, Subscriber}, + Dispatch, Event, Metadata, +}; +pub mod combinator; + +/// A [`Layer`] that wraps an inner [`Layer`] and adds a [`Filter`] which +/// controls what spans and events are enabled for that layer. +/// +/// This is returned by the [`Layer::with_filter`] method. See the +/// [documentation on per-layer filtering][plf] for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [plf]: crate::layer#per-layer-filtering +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +#[derive(Clone)] +pub struct Filtered<L, F, S> { + filter: F, + layer: L, + id: MagicPlfDowncastMarker, + _s: PhantomData<fn(S)>, +} + +/// Uniquely identifies an individual [`Filter`] instance in the context of +/// a [`Subscriber`]. +/// +/// When adding a [`Filtered`] [`Layer`] to a [`Subscriber`], the [`Subscriber`] +/// generates a `FilterId` for that [`Filtered`] layer. The [`Filtered`] layer +/// will then use the generated ID to query whether a particular span was +/// previously enabled by that layer's [`Filter`]. +/// +/// **Note**: Currently, the [`Registry`] type provided by this crate is the +/// **only** [`Subscriber`] implementation capable of participating in per-layer +/// filtering. Therefore, the `FilterId` type cannot currently be constructed by +/// code outside of `tracing-subscriber`. In the future, new APIs will be added to `tracing-subscriber` to +/// allow non-Registry [`Subscriber`]s to also participate in per-layer +/// filtering. When those APIs are added, subscribers will be responsible +/// for generating and assigning `FilterId`s. +/// +/// [`Filter`]: crate::layer::Filter +/// [`Subscriber`]: tracing_core::Subscriber +/// [`Layer`]: crate::layer::Layer +/// [`Registry`]: crate::registry::Registry +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +#[derive(Copy, Clone)] +pub struct FilterId(u64); + +/// A bitmap tracking which [`FilterId`]s have enabled a given span or +/// event. +/// +/// This is currently a private type that's used exclusively by the +/// [`Registry`]. However, in the future, this may become a public API, in order +/// to allow user subscribers to host [`Filter`]s. +/// +/// [`Registry`]: crate::Registry +/// [`Filter`]: crate::layer::Filter +#[derive(Default, Copy, Clone, Eq, PartialEq)] +pub(crate) struct FilterMap { + bits: u64, +} + +/// The current state of `enabled` calls to per-layer filters on this +/// thread. +/// +/// When `Filtered::enabled` is called, the filter will set the bit +/// corresponding to its ID if the filter will disable the event/span being +/// filtered. When the event or span is recorded, the per-layer filter will +/// check its bit to determine if it disabled that event or span, and skip +/// forwarding the event or span to the inner layer if the bit is set. Once +/// a span or event has been skipped by a per-layer filter, it unsets its +/// bit, so that the `FilterMap` has been cleared for the next set of +/// `enabled` calls. +/// +/// FilterState is also read by the `Registry`, for two reasons: +/// +/// 1. When filtering a span, the Registry must store the `FilterMap` +/// generated by `Filtered::enabled` calls for that span as part of the +/// span's per-span data. This allows `Filtered` layers to determine +/// whether they had previously disabled a given span, and avoid showing it +/// to the wrapped layer if it was disabled. +/// +/// This allows `Filtered` layers to also filter out the spans they +/// disable from span traversals (such as iterating over parents, etc). +/// 2. If all the bits are set, then every per-layer filter has decided it +/// doesn't want to enable that span or event. In that case, the +/// `Registry`'s `enabled` method will return `false`, so that +/// recording a span or event can be skipped entirely. +#[derive(Debug)] +pub(crate) struct FilterState { + enabled: Cell<FilterMap>, + // TODO(eliza): `Interest`s should _probably_ be `Copy`. The only reason + // they're not is our Obsessive Commitment to Forwards-Compatibility. If + // this changes in tracing-core`, we can make this a `Cell` rather than + // `RefCell`... + interest: RefCell<Option<Interest>>, + + #[cfg(debug_assertions)] + counters: DebugCounters, +} + +/// Extra counters added to `FilterState` used only to make debug assertions. +#[cfg(debug_assertions)] +#[derive(Debug, Default)] +struct DebugCounters { + /// How many per-layer filters have participated in the current `enabled` + /// call? + in_filter_pass: Cell<usize>, + + /// How many per-layer filters have participated in the current `register_callsite` + /// call? + in_interest_pass: Cell<usize>, +} + +thread_local! { + pub(crate) static FILTERING: FilterState = FilterState::new(); +} + +/// Extension trait adding [combinators] for combining [`Filter`]. +/// +/// [combinators]: crate::filter::combinator +/// [`Filter`]: crate::layer::Filter +pub trait FilterExt<S>: layer::Filter<S> { + /// Combines this [`Filter`] with another [`Filter`] s so that spans and + /// events are enabled if and only if *both* filters return `true`. + /// + /// # Examples + /// + /// Enabling spans or events if they have both a particular target *and* are + /// above a certain level: + /// + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together, returning a filter that only enables + /// // spans and events that *both* filters will enable: + /// let filter = target_filter.and(level_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::info!("an event with an uninteresting target"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event will *not* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + fn and<B>(self, other: B) -> combinator::And<Self, B, S> + where + Self: Sized, + B: layer::Filter<S>, + { + combinator::And::new(self, other) + } + + /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter + /// returns `true`. + /// + /// # Examples + /// + /// Enabling spans and events at the `INFO` level and above, and all spans + /// and events with a particular target: + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// // Enables spans and events with targets starting with `interesting_target`: + /// let target_filter = filter_fn(|meta| { + /// meta.target().starts_with("interesting_target") + /// }); + /// + /// // Enables spans and events with levels `INFO` and below: + /// let level_filter = LevelFilter::INFO; + /// + /// // Combine the two filters together so that a span or event is enabled + /// // if it is at INFO or lower, or if it has a target starting with + /// // `interesting_target`. + /// let filter = level_filter.or(target_filter); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// + /// // This event will *not* be enabled: + /// tracing::debug!("an uninteresting event"); + /// + /// // This event *will* be enabled: + /// tracing::info!("an uninteresting INFO event"); + /// + /// // This event *will* be enabled: + /// tracing::info!(target: "interesting_target", "a very interesting event"); + /// + /// // This event *will* be enabled: + /// tracing::debug!(target: "interesting_target", "interesting debug event..."); + /// ``` + /// + /// Enabling a higher level for a particular target by using `or` in + /// conjunction with the [`and`] combinator: + /// + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// // This filter will enable spans and events with targets beginning with + /// // `my_crate`: + /// let my_crate = filter_fn(|meta| { + /// meta.target().starts_with("my_crate") + /// }); + /// + /// let filter = my_crate + /// // Combine the `my_crate` filter with a `LevelFilter` to produce a + /// // filter that will enable the `INFO` level and lower for spans and + /// // events with `my_crate` targets: + /// .and(LevelFilter::INFO) + /// // If a span or event *doesn't* have a target beginning with + /// // `my_crate`, enable it if it has the `WARN` level or lower: + /// .or(LevelFilter::WARN); + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// [`Filter`]: crate::layer::Filter + /// [`and`]: FilterExt::and + fn or<B>(self, other: B) -> combinator::Or<Self, B, S> + where + Self: Sized, + B: layer::Filter<S>, + { + combinator::Or::new(self, other) + } + + /// Inverts `self`, returning a filter that enables spans and events only if + /// `self` would *not* enable them. + /// + /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] + /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as + /// filters which do not implement filtering on event field values will return + /// the default `true` even for events that their [`enabled`] method disables. + /// + /// Consider a normal filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => on_span(), + /// SOMETIMES => if enabled() { on_span() }, + /// NEVER => (), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => on_event(), + /// SOMETIMES => if enabled() && event_enabled() { on_event() }, + /// NEVER => (), + /// } + /// ``` + /// + /// and an inverted filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_span() }, + /// NEVER => on_span(), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_event() }, + /// NEVER => on_event(), + /// } + /// ``` + /// + /// A proper inversion would do `!(enabled() && event_enabled())` (or + /// `!enabled() || !event_enabled()`), but because of the implicit `&&` + /// relation between `enabled` and `event_enabled`, it is difficult to + /// short circuit and not call the wrapped `event_enabled`. + /// + /// A combinator which remembers the result of `enabled` in order to call + /// `event_enabled` only when `enabled() == true` is possible, but requires + /// additional thread-local mutable state to support a very niche use case. + // + // Also, it'd mean the wrapped layer's `enabled()` always gets called and + // globally applied to events where it doesn't today, since we can't know + // what `event_enabled` will say until we have the event to call it with. + /// + /// [`Filter`]: crate::subscribe::Filter + /// [`enabled`]: crate::subscribe::Filter::enabled + /// [`event_enabled`]: crate::subscribe::Filter::event_enabled + /// [`callsite_enabled`]: crate::subscribe::Filter::callsite_enabled + fn not(self) -> combinator::Not<Self, S> + where + Self: Sized, + { + combinator::Not::new(self) + } + + /// [Boxes] `self`, erasing its concrete type. + /// + /// This is equivalent to calling [`Box::new`], but in method form, so that + /// it can be used when chaining combinator methods. + /// + /// # Examples + /// + /// When different combinations of filters are used conditionally, they may + /// have different types. For example, the following code won't compile, + /// since the `if` and `else` clause produce filters of different types: + /// + /// ```compile_fail + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// let enable_bar_target: bool = // ... + /// # false; + /// + /// let filter = if enable_bar_target { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// // If `enable_bar_target` is true, add a `filter_fn` enabling + /// // spans and events with the target `bar`: + /// .or(filter_fn(|meta| meta.target().starts_with("bar"))) + /// .and(LevelFilter::INFO) + /// } else { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// .and(LevelFilter::INFO) + /// }; + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// By using `boxed`, the types of the two different branches can be erased, + /// so the assignment to the `filter` variable is valid (as both branches + /// have the type `Box<dyn Filter<S> + Send + Sync + 'static>`). The + /// following code *does* compile: + /// + /// ``` + /// use tracing_subscriber::{ + /// filter::{filter_fn, LevelFilter, FilterExt}, + /// prelude::*, + /// }; + /// + /// let enable_bar_target: bool = // ... + /// # false; + /// + /// let filter = if enable_bar_target { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// .or(filter_fn(|meta| meta.target().starts_with("bar"))) + /// .and(LevelFilter::INFO) + /// // Boxing the filter erases its type, so both branches now + /// // have the same type. + /// .boxed() + /// } else { + /// filter_fn(|meta| meta.target().starts_with("foo")) + /// .and(LevelFilter::INFO) + /// .boxed() + /// }; + /// + /// tracing_subscriber::registry() + /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) + /// .init(); + /// ``` + /// + /// [Boxes]: std::boxed + /// [`Box::new`]: std::boxed::Box::new + fn boxed(self) -> Box<dyn layer::Filter<S> + Send + Sync + 'static> + where + Self: Sized + Send + Sync + 'static, + { + Box::new(self) + } +} + +// === impl Filter === + +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl<S> layer::Filter<S> for LevelFilter { + fn enabled(&self, meta: &Metadata<'_>, _: &Context<'_, S>) -> bool { + meta.level() <= self + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + if meta.level() <= self { + Interest::always() + } else { + Interest::never() + } + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + Some(*self) + } +} + +macro_rules! filter_impl_body { + () => { + #[inline] + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.deref().enabled(meta, cx) + } + + #[inline] + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + self.deref().callsite_enabled(meta) + } + + #[inline] + fn max_level_hint(&self) -> Option<LevelFilter> { + self.deref().max_level_hint() + } + }; +} + +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl<S> layer::Filter<S> for Arc<dyn layer::Filter<S> + Send + Sync + 'static> { + filter_impl_body!(); +} + +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl<S> layer::Filter<S> for Box<dyn layer::Filter<S> + Send + Sync + 'static> { + filter_impl_body!(); +} + +// === impl Filtered === + +impl<L, F, S> Filtered<L, F, S> { + /// Wraps the provided [`Layer`] so that it is filtered by the given + /// [`Filter`]. + /// + /// This is equivalent to calling the [`Layer::with_filter`] method. + /// + /// See the [documentation on per-layer filtering][plf] for details. + /// + /// [`Filter`]: crate::layer::Filter + /// [plf]: crate::layer#per-layer-filtering + pub fn new(layer: L, filter: F) -> Self { + Self { + layer, + filter, + id: MagicPlfDowncastMarker(FilterId::disabled()), + _s: PhantomData, + } + } + + #[inline(always)] + fn id(&self) -> FilterId { + debug_assert!( + !self.id.0.is_disabled(), + "a `Filtered` layer was used, but it had no `FilterId`; \ + was it registered with the subscriber?" + ); + self.id.0 + } + + fn did_enable(&self, f: impl FnOnce()) { + FILTERING.with(|filtering| filtering.did_enable(self.id(), f)) + } + + /// Borrows the [`Filter`](crate::layer::Filter) used by this layer. + pub fn filter(&self) -> &F { + &self.filter + } + + /// Mutably borrows the [`Filter`](crate::layer::Filter) used by this layer. + /// + /// When this layer can be mutably borrowed, this may be used to mutate the filter. + /// Generally, this will primarily be used with the + /// [`reload::Handle::modify`](crate::reload::Handle::modify) method. + /// + /// # Examples + /// + /// ``` + /// # use tracing::info; + /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*}; + /// # fn main() { + /// let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN); + /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer); + /// # + /// # // specifying the Registry type is required + /// # let _: &reload::Handle<filter::Filtered<fmt::Layer<Registry>, + /// # filter::LevelFilter, Registry>,Registry> + /// # = &reload_handle; + /// # + /// info!("This will be ignored"); + /// reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO); + /// info!("This will be logged"); + /// # } + /// ``` + pub fn filter_mut(&mut self) -> &mut F { + &mut self.filter + } + + /// Borrows the inner [`Layer`] wrapped by this `Filtered` layer. + pub fn inner(&self) -> &L { + &self.layer + } + + /// Mutably borrows the inner [`Layer`] wrapped by this `Filtered` layer. + /// + /// This method is primarily expected to be used with the + /// [`reload::Handle::modify`](crate::reload::Handle::modify) method. + /// + /// # Examples + /// + /// ``` + /// # use tracing::info; + /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*}; + /// # fn non_blocking<T: std::io::Write>(writer: T) -> (fn() -> std::io::Stdout) { + /// # std::io::stdout + /// # } + /// # fn main() { + /// let filtered_layer = fmt::layer().with_writer(non_blocking(std::io::stderr())).with_filter(filter::LevelFilter::INFO); + /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer); + /// # + /// # // specifying the Registry type is required + /// # let _: &reload::Handle<filter::Filtered<fmt::Layer<Registry, _, _, fn() -> std::io::Stdout>, + /// # filter::LevelFilter, Registry>, Registry> + /// # = &reload_handle; + /// # + /// info!("This will be logged to stderr"); + /// reload_handle.modify(|layer| *layer.inner_mut().writer_mut() = non_blocking(std::io::stdout())); + /// info!("This will be logged to stdout"); + /// # } + /// ``` + /// + /// [subscriber]: Subscribe + pub fn inner_mut(&mut self) -> &mut L { + &mut self.layer + } +} + +impl<S, L, F> Layer<S> for Filtered<L, F, S> +where + S: Subscriber + for<'span> registry::LookupSpan<'span> + 'static, + F: layer::Filter<S> + 'static, + L: Layer<S>, +{ + fn on_register_dispatch(&self, collector: &Dispatch) { + self.layer.on_register_dispatch(collector); + } + + fn on_layer(&mut self, subscriber: &mut S) { + self.id = MagicPlfDowncastMarker(subscriber.register_filter()); + self.layer.on_layer(subscriber); + } + + // TODO(eliza): can we figure out a nice way to make the `Filtered` layer + // not call `is_enabled_for` in hooks that the inner layer doesn't actually + // have real implementations of? probably not... + // + // it would be cool if there was some wild rust reflection way of checking + // if a trait impl has the default impl of a trait method or not, but that's + // almsot certainly impossible...right? + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + let interest = self.filter.callsite_enabled(metadata); + + // If the filter didn't disable the callsite, allow the inner layer to + // register it — since `register_callsite` is also used for purposes + // such as reserving/caching per-callsite data, we want the inner layer + // to be able to perform any other registration steps. However, we'll + // ignore its `Interest`. + if !interest.is_never() { + self.layer.register_callsite(metadata); + } + + // Add our `Interest` to the current sum of per-layer filter `Interest`s + // for this callsite. + FILTERING.with(|filtering| filtering.add_interest(interest)); + + // don't short circuit! if the stack consists entirely of `Layer`s with + // per-layer filters, the `Registry` will return the actual `Interest` + // value that's the sum of all the `register_callsite` calls to those + // per-layer filters. if we returned an actual `never` interest here, a + // `Layered` layer would short-circuit and not allow any `Filtered` + // layers below us if _they_ are interested in the callsite. + Interest::always() + } + + fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool { + let cx = cx.with_filter(self.id()); + let enabled = self.filter.enabled(metadata, &cx); + FILTERING.with(|filtering| filtering.set(self.id(), enabled)); + + if enabled { + // If the filter enabled this metadata, ask the wrapped layer if + // _it_ wants it --- it might have a global filter. + self.layer.enabled(metadata, cx) + } else { + // Otherwise, return `true`. The _per-layer_ filter disabled this + // metadata, but returning `false` in `Layer::enabled` will + // short-circuit and globally disable the span or event. This is + // *not* what we want for per-layer filters, as other layers may + // still want this event. Returning `true` here means we'll continue + // asking the next layer in the stack. + // + // Once all per-layer filters have been evaluated, the `Registry` + // at the root of the stack will return `false` from its `enabled` + // method if *every* per-layer filter disabled this metadata. + // Otherwise, the individual per-layer filters will skip the next + // `new_span` or `on_event` call for their layer if *they* disabled + // the span or event, but it was not globally disabled. + true + } + } + + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, cx: Context<'_, S>) { + self.did_enable(|| { + let cx = cx.with_filter(self.id()); + self.filter.on_new_span(attrs, id, cx.clone()); + self.layer.on_new_span(attrs, id, cx); + }) + } + + #[doc(hidden)] + fn max_level_hint(&self) -> Option<LevelFilter> { + self.filter.max_level_hint() + } + + fn on_record(&self, span: &span::Id, values: &span::Record<'_>, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(span, self.id()) { + self.filter.on_record(span, values, cx.clone()); + self.layer.on_record(span, values, cx) + } + } + + fn on_follows_from(&self, span: &span::Id, follows: &span::Id, cx: Context<'_, S>) { + // only call `on_follows_from` if both spans are enabled by us + if cx.is_enabled_for(span, self.id()) && cx.is_enabled_for(follows, self.id()) { + self.layer + .on_follows_from(span, follows, cx.with_filter(self.id())) + } + } + + fn event_enabled(&self, event: &Event<'_>, cx: Context<'_, S>) -> bool { + let cx = cx.with_filter(self.id()); + let enabled = FILTERING + .with(|filtering| filtering.and(self.id(), || self.filter.event_enabled(event, &cx))); + + if enabled { + // If the filter enabled this event, ask the wrapped subscriber if + // _it_ wants it --- it might have a global filter. + self.layer.event_enabled(event, cx) + } else { + // Otherwise, return `true`. See the comment in `enabled` for why this + // is necessary. + true + } + } + + fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) { + self.did_enable(|| { + self.layer.on_event(event, cx.with_filter(self.id())); + }) + } + + fn on_enter(&self, id: &span::Id, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(id, self.id()) { + self.filter.on_enter(id, cx.clone()); + self.layer.on_enter(id, cx); + } + } + + fn on_exit(&self, id: &span::Id, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(id, self.id()) { + self.filter.on_exit(id, cx.clone()); + self.layer.on_exit(id, cx); + } + } + + fn on_close(&self, id: span::Id, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(&id, self.id()) { + self.filter.on_close(id.clone(), cx.clone()); + self.layer.on_close(id, cx); + } + } + + // XXX(eliza): the existence of this method still makes me sad... + fn on_id_change(&self, old: &span::Id, new: &span::Id, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(old, self.id()) { + self.layer.on_id_change(old, new, cx) + } + } + + #[doc(hidden)] + #[inline] + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { + match id { + id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()), + id if id == TypeId::of::<L>() => Some(&self.layer as *const _ as *const ()), + id if id == TypeId::of::<F>() => Some(&self.filter as *const _ as *const ()), + id if id == TypeId::of::<MagicPlfDowncastMarker>() => { + Some(&self.id as *const _ as *const ()) + } + _ => self.layer.downcast_raw(id), + } + } +} + +impl<F, L, S> fmt::Debug for Filtered<F, L, S> +where + F: fmt::Debug, + L: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Filtered") + .field("filter", &self.filter) + .field("layer", &self.layer) + .field("id", &self.id) + .finish() + } +} + +// === impl FilterId === + +impl FilterId { + const fn disabled() -> Self { + Self(std::u64::MAX) + } + + /// Returns a `FilterId` that will consider _all_ spans enabled. + pub(crate) const fn none() -> Self { + Self(0) + } + + pub(crate) fn new(id: u8) -> Self { + assert!(id < 64, "filter IDs may not be greater than 64"); + Self(1 << id as usize) + } + + /// Combines two `FilterId`s, returning a new `FilterId` that will match a + /// [`FilterMap`] where the span was disabled by _either_ this `FilterId` + /// *or* the combined `FilterId`. + /// + /// This method is called by [`Context`]s when adding the `FilterId` of a + /// [`Filtered`] layer to the context. + /// + /// This is necessary for cases where we have a tree of nested [`Filtered`] + /// layers, like this: + /// + /// ```text + /// Filtered { + /// filter1, + /// Layered { + /// layer1, + /// Filtered { + /// filter2, + /// layer2, + /// }, + /// } + /// ``` + /// + /// We want `layer2` to be affected by both `filter1` _and_ `filter2`. + /// Without combining `FilterId`s, this works fine when filtering + /// `on_event`/`new_span`, because the outer `Filtered` layer (`filter1`) + /// won't call the inner layer's `on_event` or `new_span` callbacks if it + /// disabled the event/span. + /// + /// However, it _doesn't_ work when filtering span lookups and traversals + /// (e.g. `scope`). This is because the [`Context`] passed to `layer2` + /// would set its filter ID to the filter ID of `filter2`, and would skip + /// spans that were disabled by `filter2`. However, what if a span was + /// disabled by `filter1`? We wouldn't see it in `new_span`, but we _would_ + /// see it in lookups and traversals...which we don't want. + /// + /// When a [`Filtered`] layer adds its ID to a [`Context`], it _combines_ it + /// with any previous filter ID that the context had, rather than replacing + /// it. That way, `layer2`'s context will check if a span was disabled by + /// `filter1` _or_ `filter2`. The way we do this, instead of representing + /// `FilterId`s as a number number that we shift a 1 over by to get a mask, + /// we just store the actual mask,so we can combine them with a bitwise-OR. + /// + /// For example, if we consider the following case (pretending that the + /// masks are 8 bits instead of 64 just so i don't have to write out a bunch + /// of extra zeroes): + /// + /// - `filter1` has the filter id 1 (`0b0000_0001`) + /// - `filter2` has the filter id 2 (`0b0000_0010`) + /// + /// A span that gets disabled by filter 1 would have the [`FilterMap`] with + /// bits `0b0000_0001`. + /// + /// If the `FilterId` was internally represented as `(bits to shift + 1), + /// when `layer2`'s [`Context`] checked if it enabled the span, it would + /// make the mask `0b0000_0010` (`1 << 1`). That bit would not be set in the + /// [`FilterMap`], so it would see that it _didn't_ disable the span. Which + /// is *true*, it just doesn't reflect the tree-like shape of the actual + /// subscriber. + /// + /// By having the IDs be masks instead of shifts, though, when the + /// [`Filtered`] with `filter2` gets the [`Context`] with `filter1`'s filter ID, + /// instead of replacing it, it ors them together: + /// + /// ```ignore + /// 0b0000_0001 | 0b0000_0010 == 0b0000_0011; + /// ``` + /// + /// We then test if the span was disabled by seeing if _any_ bits in the + /// mask are `1`: + /// + /// ```ignore + /// filtermap & mask != 0; + /// 0b0000_0001 & 0b0000_0011 != 0; + /// 0b0000_0001 != 0; + /// true; + /// ``` + /// + /// [`Context`]: crate::layer::Context + pub(crate) fn and(self, FilterId(other): Self) -> Self { + // If this mask is disabled, just return the other --- otherwise, we + // would always see that every span is disabled. + if self.0 == Self::disabled().0 { + return Self(other); + } + + Self(self.0 | other) + } + + fn is_disabled(self) -> bool { + self.0 == Self::disabled().0 + } +} + +impl fmt::Debug for FilterId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // don't print a giant set of the numbers 0..63 if the filter ID is disabled. + if self.0 == Self::disabled().0 { + return f + .debug_tuple("FilterId") + .field(&format_args!("DISABLED")) + .finish(); + } + + if f.alternate() { + f.debug_struct("FilterId") + .field("ids", &format_args!("{:?}", FmtBitset(self.0))) + .field("bits", &format_args!("{:b}", self.0)) + .finish() + } else { + f.debug_tuple("FilterId").field(&FmtBitset(self.0)).finish() + } + } +} + +impl fmt::Binary for FilterId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("FilterId") + .field(&format_args!("{:b}", self.0)) + .finish() + } +} + +// === impl FilterExt === + +impl<F, S> FilterExt<S> for F where F: layer::Filter<S> {} + +// === impl FilterMap === + +impl FilterMap { + pub(crate) fn set(self, FilterId(mask): FilterId, enabled: bool) -> Self { + if mask == std::u64::MAX { + return self; + } + + if enabled { + Self { + bits: self.bits & (!mask), + } + } else { + Self { + bits: self.bits | mask, + } + } + } + + #[inline] + pub(crate) fn is_enabled(self, FilterId(mask): FilterId) -> bool { + self.bits & mask == 0 + } + + #[inline] + pub(crate) fn any_enabled(self) -> bool { + self.bits != std::u64::MAX + } +} + +impl fmt::Debug for FilterMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let alt = f.alternate(); + let mut s = f.debug_struct("FilterMap"); + s.field("disabled_by", &format_args!("{:?}", &FmtBitset(self.bits))); + + if alt { + s.field("bits", &format_args!("{:b}", self.bits)); + } + + s.finish() + } +} + +impl fmt::Binary for FilterMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterMap") + .field("bits", &format_args!("{:b}", self.bits)) + .finish() + } +} + +// === impl FilterState === + +impl FilterState { + fn new() -> Self { + Self { + enabled: Cell::new(FilterMap::default()), + interest: RefCell::new(None), + + #[cfg(debug_assertions)] + counters: DebugCounters::default(), + } + } + + fn set(&self, filter: FilterId, enabled: bool) { + #[cfg(debug_assertions)] + { + let in_current_pass = self.counters.in_filter_pass.get(); + if in_current_pass == 0 { + debug_assert_eq!(self.enabled.get(), FilterMap::default()); + } + self.counters.in_filter_pass.set(in_current_pass + 1); + debug_assert_eq!( + self.counters.in_interest_pass.get(), + 0, + "if we are in or starting a filter pass, we must not be in an interest pass." + ) + } + + self.enabled.set(self.enabled.get().set(filter, enabled)) + } + + fn add_interest(&self, interest: Interest) { + let mut curr_interest = self.interest.borrow_mut(); + + #[cfg(debug_assertions)] + { + let in_current_pass = self.counters.in_interest_pass.get(); + if in_current_pass == 0 { + debug_assert!(curr_interest.is_none()); + } + self.counters.in_interest_pass.set(in_current_pass + 1); + } + + if let Some(curr_interest) = curr_interest.as_mut() { + if (curr_interest.is_always() && !interest.is_always()) + || (curr_interest.is_never() && !interest.is_never()) + { + *curr_interest = Interest::sometimes(); + } + // If the two interests are the same, do nothing. If the current + // interest is `sometimes`, stay sometimes. + } else { + *curr_interest = Some(interest); + } + } + + pub(crate) fn event_enabled() -> bool { + FILTERING + .try_with(|this| { + let enabled = this.enabled.get().any_enabled(); + #[cfg(debug_assertions)] + { + if this.counters.in_filter_pass.get() == 0 { + debug_assert_eq!(this.enabled.get(), FilterMap::default()); + } + + // Nothing enabled this event, we won't tick back down the + // counter in `did_enable`. Reset it. + if !enabled { + this.counters.in_filter_pass.set(0); + } + } + enabled + }) + .unwrap_or(true) + } + + /// Executes a closure if the filter with the provided ID did not disable + /// the current span/event. + /// + /// This is used to implement the `on_event` and `new_span` methods for + /// `Filtered`. + fn did_enable(&self, filter: FilterId, f: impl FnOnce()) { + let map = self.enabled.get(); + if map.is_enabled(filter) { + // If the filter didn't disable the current span/event, run the + // callback. + f(); + } else { + // Otherwise, if this filter _did_ disable the span or event + // currently being processed, clear its bit from this thread's + // `FilterState`. The bit has already been "consumed" by skipping + // this callback, and we need to ensure that the `FilterMap` for + // this thread is reset when the *next* `enabled` call occurs. + self.enabled.set(map.set(filter, true)); + } + #[cfg(debug_assertions)] + { + let in_current_pass = self.counters.in_filter_pass.get(); + if in_current_pass <= 1 { + debug_assert_eq!(self.enabled.get(), FilterMap::default()); + } + self.counters + .in_filter_pass + .set(in_current_pass.saturating_sub(1)); + debug_assert_eq!( + self.counters.in_interest_pass.get(), + 0, + "if we are in a filter pass, we must not be in an interest pass." + ) + } + } + + /// Run a second filtering pass, e.g. for Subscribe::event_enabled. + fn and(&self, filter: FilterId, f: impl FnOnce() -> bool) -> bool { + let map = self.enabled.get(); + let enabled = map.is_enabled(filter) && f(); + self.enabled.set(map.set(filter, enabled)); + enabled + } + + /// Clears the current in-progress filter state. + /// + /// This resets the [`FilterMap`] and current [`Interest`] as well as + /// clearing the debug counters. + pub(crate) fn clear_enabled() { + // Drop the `Result` returned by `try_with` --- if we are in the middle + // a panic and the thread-local has been torn down, that's fine, just + // ignore it ratehr than panicking. + let _ = FILTERING.try_with(|filtering| { + filtering.enabled.set(FilterMap::default()); + + #[cfg(debug_assertions)] + filtering.counters.in_filter_pass.set(0); + }); + } + + pub(crate) fn take_interest() -> Option<Interest> { + FILTERING + .try_with(|filtering| { + #[cfg(debug_assertions)] + { + if filtering.counters.in_interest_pass.get() == 0 { + debug_assert!(filtering.interest.try_borrow().ok()?.is_none()); + } + filtering.counters.in_interest_pass.set(0); + } + filtering.interest.try_borrow_mut().ok()?.take() + }) + .ok()? + } + + pub(crate) fn filter_map(&self) -> FilterMap { + let map = self.enabled.get(); + #[cfg(debug_assertions)] + { + if self.counters.in_filter_pass.get() == 0 { + debug_assert_eq!(map, FilterMap::default()); + } + } + + map + } +} +/// This is a horrible and bad abuse of the downcasting system to expose +/// *internally* whether a layer has per-layer filtering, within +/// `tracing-subscriber`, without exposing a public API for it. +/// +/// If a `Layer` has per-layer filtering, it will downcast to a +/// `MagicPlfDowncastMarker`. Since layers which contain other layers permit +/// downcasting to recurse to their children, this will do the Right Thing with +/// layers like Reload, Option, etc. +/// +/// Why is this a wrapper around the `FilterId`, you may ask? Because +/// downcasting works by returning a pointer, and we don't want to risk +/// introducing UB by constructing pointers that _don't_ point to a valid +/// instance of the type they claim to be. In this case, we don't _intend_ for +/// this pointer to be dereferenced, so it would actually be fine to return one +/// that isn't a valid pointer...but we can't guarantee that the caller won't +/// (accidentally) dereference it, so it's better to be safe than sorry. We +/// could, alternatively, add an additional field to the type that's used only +/// for returning pointers to as as part of the evil downcasting hack, but I +/// thought it was nicer to just add a `repr(transparent)` wrapper to the +/// existing `FilterId` field, since it won't make the struct any bigger. +/// +/// Don't worry, this isn't on the test. :) +#[derive(Clone, Copy)] +#[repr(transparent)] +struct MagicPlfDowncastMarker(FilterId); +impl fmt::Debug for MagicPlfDowncastMarker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Just pretend that `MagicPlfDowncastMarker` doesn't exist for + // `fmt::Debug` purposes...if no one *sees* it in their `Debug` output, + // they don't have to know I thought this code would be a good idea. + fmt::Debug::fmt(&self.0, f) + } +} + +pub(crate) fn is_plf_downcast_marker(type_id: TypeId) -> bool { + type_id == TypeId::of::<MagicPlfDowncastMarker>() +} + +/// Does a type implementing `Subscriber` contain any per-layer filters? +pub(crate) fn subscriber_has_plf<S>(subscriber: &S) -> bool +where + S: Subscriber, +{ + (subscriber as &dyn Subscriber).is::<MagicPlfDowncastMarker>() +} + +/// Does a type implementing `Layer` contain any per-layer filters? +pub(crate) fn layer_has_plf<L, S>(layer: &L) -> bool +where + L: Layer<S>, + S: Subscriber, +{ + unsafe { + // Safety: we're not actually *doing* anything with this pointer --- we + // only care about the `Option`, which we're turning into a `bool`. So + // even if the layer decides to be evil and give us some kind of invalid + // pointer, we don't ever dereference it, so this is always safe. + layer.downcast_raw(TypeId::of::<MagicPlfDowncastMarker>()) + } + .is_some() +} + +struct FmtBitset(u64); + +impl fmt::Debug for FmtBitset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut set = f.debug_set(); + for bit in 0..64 { + // if the `bit`-th bit is set, add it to the debug set + if self.0 & (1 << bit) != 0 { + set.entry(&bit); + } + } + set.finish() + } +} diff --git a/vendor/tracing-subscriber/src/filter/level.rs b/vendor/tracing-subscriber/src/filter/level.rs new file mode 100644 index 000000000..0fa601260 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/level.rs @@ -0,0 +1,27 @@ +use tracing_core::{ + subscriber::{Interest, Subscriber}, + Metadata, +}; + +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use tracing_core::metadata::{LevelFilter, ParseLevelFilterError as ParseError}; + +// === impl LevelFilter === + +impl<S: Subscriber> crate::Layer<S> for LevelFilter { + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + if self >= metadata.level() { + Interest::always() + } else { + Interest::never() + } + } + + fn enabled(&self, metadata: &Metadata<'_>, _: crate::layer::Context<'_, S>) -> bool { + self >= metadata.level() + } + + fn max_level_hint(&self) -> Option<LevelFilter> { + Some(*self) + } +} diff --git a/vendor/tracing-subscriber/src/filter/mod.rs b/vendor/tracing-subscriber/src/filter/mod.rs new file mode 100644 index 000000000..000a27195 --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/mod.rs @@ -0,0 +1,66 @@ +//! [`Layer`]s that control which spans and events are enabled by the wrapped +//! subscriber. +//! +//! This module contains a number of types that provide implementations of +//! various strategies for filtering which spans and events are enabled. For +//! details on filtering spans and events using [`Layer`]s, see the +//! [`layer` module's documentation]. +//! +//! [`layer` module's documentation]: crate::layer#filtering-with-layers +//! [`Layer`]: crate::layer +mod filter_fn; + +feature! { + #![all(feature = "env-filter", feature = "std")] + mod env; + pub use self::env::*; +} + +feature! { + #![all(feature = "registry", feature = "std")] + mod layer_filters; + pub use self::layer_filters::*; +} + +mod level; + +pub use self::filter_fn::*; +pub use self::level::{LevelFilter, ParseError as LevelParseError}; + +#[cfg(not(all(feature = "registry", feature = "std")))] +pub(crate) use self::has_plf_stubs::*; + +feature! { + #![any(feature = "std", feature = "alloc")] + pub mod targets; + pub use self::targets::Targets; + + mod directive; + pub use self::directive::ParseError; +} + +/// Stub implementations of the per-layer-fitler detection functions for when the +/// `registry` feature is disabled. +#[cfg(not(all(feature = "registry", feature = "std")))] +mod has_plf_stubs { + pub(crate) fn is_plf_downcast_marker(_: core::any::TypeId) -> bool { + false + } + + /// Does a type implementing `Subscriber` contain any per-layer filters? + pub(crate) fn subscriber_has_plf<S>(_: &S) -> bool + where + S: tracing_core::Subscriber, + { + false + } + + /// Does a type implementing `Layer` contain any per-layer filters? + pub(crate) fn layer_has_plf<L, S>(_: &L) -> bool + where + L: crate::Layer<S>, + S: tracing_core::Subscriber, + { + false + } +} diff --git a/vendor/tracing-subscriber/src/filter/targets.rs b/vendor/tracing-subscriber/src/filter/targets.rs new file mode 100644 index 000000000..e1407114b --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/targets.rs @@ -0,0 +1,781 @@ +//! 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, Level, 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<dyn std::error::Error>> { +/// use tracing_subscriber::filter; +/// use tracing_core::Level; +/// +/// let filter = "my_crate=info,my_crate::interesting_module=trace,other_crate=debug" +/// .parse::<filter::Targets>()?; +/// +/// // 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][global]: +/// +/// ```rust +/// use tracing_subscriber::{ +/// fmt, +/// filter::{Targets, LevelFilter}, +/// prelude::*, +/// }; +/// use tracing_core::Level; +/// use std::{sync::Arc, fs::File}; +/// # fn docs() -> Result<(), Box<dyn std::error::Error>> { +/// +/// // 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<StaticDirective>); + +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<String>, level: impl Into<LevelFilter>) -> 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<T, L>(mut self, targets: impl IntoIterator<Item = (T, L)>) -> Self + where + String: From<T>, + LevelFilter: From<L>, + { + 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<LevelFilter>) -> Self { + self.0 + .add(StaticDirective::new(None, Default::default(), level.into())); + 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<LevelFilter> { + 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. + /// + /// # 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() + } + } + + /// Returns whether a [target]-[`Level`] pair would be enabled + /// by this `Targets`. + /// + /// This method can be used with [`module_path!`] from `std` as the target + /// in order to emulate the behavior of the [`tracing::event!`] and [`tracing::span!`] + /// macros. + /// + /// # 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); + /// + /// assert!(filter.would_enable("my_crate", &Level::INFO)); + /// assert!(!filter.would_enable("my_crate::interesting_module", &Level::TRACE)); + /// ``` + /// + /// [target]: tracing_core::Metadata::target + /// [`module_path!`]: std::module_path! + pub fn would_enable(&self, target: &str, level: &Level) -> bool { + // "Correct" to call because `Targets` only produces `StaticDirective`'s with NO + // fields + self.0.target_enabled(target, level) + } +} + +impl<T, L> Extend<(T, L)> for Targets +where + T: Into<String>, + L: Into<LevelFilter>, +{ + fn extend<I: IntoIterator<Item = (T, L)>>(&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<T, L> FromIterator<(T, L)> for Targets +where + T: Into<String>, + L: Into<LevelFilter>, +{ + fn from_iter<I: IntoIterator<Item = (T, L)>>(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<Self, Self::Err> { + s.split(',') + .map(StaticDirective::from_str) + .collect::<Result<_, _>>() + .map(Self) + } +} + +impl<S> layer::Layer<S> 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<LevelFilter> { + Some(self.0.max_level) + } +} + +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl<S> layer::Filter<S> 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<LevelFilter> { + 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< + <DirectiveSet<StaticDirective> 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::Item> { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option<usize>) { + 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::Item> { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option<usize>) { + 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::<Targets>() { + 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::<String>::new()); + + assert_eq!(dirs[1].target, Some("common".to_string())); + assert_eq!(dirs[1].level, LevelFilter::INFO); + assert_eq!(dirs[1].field_names, Vec::<String>::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::<String>::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::<String>::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::<String>::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::<String>::new()); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].field_names, Vec::<String>::new()); + + assert_eq!(dirs[5].target, Some("crate2".to_string())); + assert_eq!(dirs[5].level, LevelFilter::DEBUG); + assert_eq!(dirs[5].field_names, Vec::<String>::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::<String>::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::<String>::new()); + + assert_eq!(dirs[2].target, Some("crate3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::OFF); + assert_eq!(dirs[2].field_names, Vec::<String>::new()); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].field_names, Vec::<String>::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] + 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")] + fn size_of_filters() { + fn print_sz(s: &str) { + let filter = s.parse::<Targets>().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", + ); + } +} |