diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/tracing-subscriber/src/filter/directive.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/directive.rs) | 34 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/builder.rs | 325 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/directive.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs) | 160 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/field.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs) | 250 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs) | 677 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/filter_fn.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs) | 0 | ||||
-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 (renamed from vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs) | 454 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/level.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/level.rs) | 0 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/mod.rs) | 0 | ||||
-rw-r--r-- | vendor/tracing-subscriber/src/filter/targets.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/targets.rs) | 104 |
11 files changed, 2206 insertions, 340 deletions
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/directive.rs b/vendor/tracing-subscriber/src/filter/directive.rs index dd6b063c4..2ae3f0f24 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/directive.rs +++ b/vendor/tracing-subscriber/src/filter/directive.rs @@ -5,7 +5,7 @@ use alloc::vec; use alloc::{string::String, vec::Vec}; use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr}; -use tracing_core::Metadata; +use tracing_core::{Level, Metadata}; /// Indicates that a string could not be parsed as a filtering directive. #[derive(Debug)] pub struct ParseError { @@ -142,6 +142,22 @@ impl DirectiveSet<StaticDirective> { 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 === @@ -158,6 +174,22 @@ impl StaticDirective { 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 { 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-0.3.3/src/filter/env/directive.rs b/vendor/tracing-subscriber/src/filter/env/directive.rs index 66ca23dc4..f062e6ef9 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs +++ b/vendor/tracing-subscriber/src/filter/env/directive.rs @@ -4,14 +4,14 @@ use crate::filter::{ env::{field, FieldMap}, level::LevelFilter, }; -use lazy_static::lazy_static; +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(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] pub struct Directive { in_span: Option<String>, @@ -107,80 +107,52 @@ impl Directive { .collect(); (Dynamics::from_iter(dyns), statics) } -} - -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 fields = meta.fields(); - for field in &self.fields { - if fields.field(&field.name).is_none() { - return false; + 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, } } - - true - } - - fn level(&self) -> &LevelFilter { - &self.level } -} -impl FromStr for Directive { - type Err = ParseError; - fn from_str(from: &str) -> Result<Self, Self::Err> { - lazy_static! { - static ref DIRECTIVE_RE: Regex = 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 ref SPAN_PART_RE: Regex = - Regex::new(r#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap(); - static ref FIELD_FILTER_RE: Regex = - // TODO(eliza): this doesn't _currently_ handle value matchers that include comma - // characters. We should fix that. - Regex::new(r#"(?x) - ( - # field name - [[:word:]][[[:word:]]\.]* - # value part (optional) - (?:=[^,]+)? - ) - # trailing comma or EOS - (?:,\s?|$) - "#).unwrap(); - } + 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)?; @@ -214,7 +186,7 @@ impl FromStr for Directive { .map(|c| { FIELD_FILTER_RE .find_iter(c.as_str()) - .map(|c| c.as_str().parse()) + .map(|c| field::Match::parse(c.as_str(), regex)) .collect::<Result<Vec<_>, _>>() }) .unwrap_or_else(|| Ok(Vec::new())); @@ -228,7 +200,7 @@ impl FromStr for Directive { // Setting the target without the level enables every level for that target .unwrap_or(LevelFilter::TRACE); - Ok(Directive { + Ok(Self { level, target, in_span, @@ -237,6 +209,48 @@ impl FromStr for Directive { } } +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 { diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs b/vendor/tracing-subscriber/src/filter/env/field.rs index 970850f92..1394fd04a 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs +++ b/vendor/tracing-subscriber/src/filter/env/field.rs @@ -2,7 +2,7 @@ use matchers::Pattern; use std::{ cmp::Ordering, error::Error, - fmt, + fmt::{self, Write}, str::FromStr, sync::{ atomic::{AtomicBool, Ordering::*}, @@ -13,7 +13,7 @@ use std::{ use super::{FieldMap, LevelFilter}; use tracing_core::field::{Field, Visit}; -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct Match { pub(crate) name: String, // TODO: allow match patterns for names? pub(crate) value: Option<ValueMatch>, @@ -38,11 +38,20 @@ pub(crate) struct MatchVisitor<'a> { #[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>), } @@ -97,6 +106,9 @@ impl Ord for ValueMatch { (Pat(this), Pat(that)) => this.cmp(that), (Pat(_), _) => Ordering::Greater, + + (Debug(this), Debug(that)) => this.cmp(that), + (Debug(_), _) => Ordering::Greater, } } } @@ -107,12 +119,25 @@ impl PartialOrd for ValueMatch { } } +/// 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")))] @@ -122,9 +147,17 @@ pub struct BadName { // === impl Match === -impl FromStr for Match { - type Err = Box<dyn Error + Send + Sync>; - fn from_str(s: &str) -> Result<Self, Self::Err> { +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() @@ -133,22 +166,17 @@ impl FromStr for Match { })? // TODO: validate field name .to_string(); - let value = parts.next().map(ValueMatch::from_str).transpose()?; + 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 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() - } -} - impl fmt::Display for Match { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.name, f)?; @@ -199,9 +227,14 @@ fn value_match_f64(v: f64) -> ValueMatch { } } -impl FromStr for ValueMatch { - type Err = matchers::Error; - fn from_str(s: &str) -> Result<Self, Self::Err> { +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)) @@ -212,6 +245,21 @@ impl FromStr for ValueMatch { .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 { @@ -222,6 +270,7 @@ impl fmt::Display for ValueMatch { 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), } } @@ -264,6 +313,12 @@ impl MatchPattern { 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 { @@ -289,6 +344,102 @@ impl Ord for MatchPattern { } } +// === 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 {} @@ -401,6 +552,9 @@ impl<'a> Visit for MatchVisitor<'a> { 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) + } _ => {} } } @@ -410,7 +564,63 @@ impl<'a> Visit for MatchVisitor<'a> { 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-0.3.3/src/filter/env/mod.rs b/vendor/tracing-subscriber/src/filter/env/mod.rs index 81fe0e62d..81a9ae2bd 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs +++ b/vendor/tracing-subscriber/src/filter/env/mod.rs @@ -4,7 +4,8 @@ // these are publicly re-exported, but the compiler doesn't realize // that for some reason. #[allow(unreachable_pub)] -pub use self::{directive::Directive, field::BadName as BadFieldName}; +pub use self::{builder::Builder, directive::Directive, field::BadName as BadFieldName}; +mod builder; mod directive; mod field; @@ -15,6 +16,7 @@ use crate::{ }; 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, @@ -26,6 +28,16 @@ use tracing_core::{ /// 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. @@ -52,10 +64,27 @@ use tracing_core::{ /// 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 acts as a regex on -/// the `std::fmt::Debug` output from the value. +/// 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. @@ -72,7 +101,7 @@ use tracing_core::{ /// - A dash in a target will only appear when being specified explicitly: /// `tracing::info!(target: "target-name", ...);` /// -/// ## Examples +/// ## Example Syntax /// /// - `tokio::net=info` will enable all spans or events that: /// - have the `tokio::net` target, @@ -89,10 +118,68 @@ use tracing_core::{ /// - which has a field named `name` with value `bob`, /// - at _any_ level. /// -/// 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`]. +/// # 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 @@ -100,6 +187,11 @@ use tracing_core::{ /// [`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 { @@ -108,10 +200,8 @@ pub struct EnvFilter { has_dynamics: bool, by_id: RwLock<HashMap<span::Id, directive::SpanMatcher>>, by_cs: RwLock<HashMap<callsite::Identifier, directive::CallsiteMatcher>>, -} - -thread_local! { - static SCOPE: RefCell<Vec<LevelFilter>> = RefCell::new(Vec::new()); + scope: ThreadLocal<RefCell<Vec<LevelFilter>>>, + regex: bool, } type FieldMap<T> = HashMap<Field, T>; @@ -134,58 +224,181 @@ 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`]: #method.from_default_env - /// [`EnvFilter::try_from_default_env`]: #method.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::from_env(Self::DEFAULT_ENV) + 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 { - env::var(env.as_ref()).map(Self::new).unwrap_or_default() + 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. - pub fn new<S: AsRef<str>>(dirs: S) -> Self { - let directives = dirs.as_ref().split(',').filter_map(|s| match s.parse() { - Ok(d) => Some(d), - Err(err) => { - eprintln!("ignoring `{}`: {}", s, err); - None - } - }); - Self::from_directives(directives) + /// + /// 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> { - let directives = dirs - .as_ref() - .split(',') - .map(|s| s.parse()) - .collect::<Result<Vec<_>, _>>()?; - Ok(Self::from_directives(directives)) + Self::builder().parse(dirs) } /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment - /// variable, or an error if the environment variable contains any invalid - /// filter directives. + /// 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::try_from_env(Self::DEFAULT_ENV) + 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> { - env::var(env.as_ref())?.parse().map_err(Into::into) + Self::builder().with_env_var(env.as_ref()).try_from_env() } /// Add a filtering directive to this `EnvFilter`. @@ -202,13 +415,13 @@ impl EnvFilter { /// and events as a previous filter, but sets a different level for those /// spans and events, the previous directive is overwritten. /// - /// [`LevelFilter`]: ../filter/struct.LevelFilter.html - /// [`Level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html + /// [`LevelFilter`]: super::LevelFilter + /// [`Level`]: tracing_core::Level /// /// # Examples /// /// From [`LevelFilter`]: - //// + /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// let mut filter = EnvFilter::from_default_env() @@ -223,9 +436,9 @@ impl EnvFilter { /// let mut filter = EnvFilter::from_default_env() /// .add_directive(Level::INFO.into()); /// ``` - //// + /// /// Parsed from a string: - //// + /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, Directive}; /// @@ -236,7 +449,15 @@ impl EnvFilter { /// # Ok(()) /// # } /// ``` - pub fn add_directive(mut self, directive: Directive) -> Self { + /// 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 { @@ -246,165 +467,19 @@ impl EnvFilter { self } - fn from_directives(directives: impl IntoIterator<Item = Directive>) -> Self { - use tracing::level_filters::STATIC_MAX_LEVEL; - use tracing::Level; - - let directives: Vec<_> = directives.into_iter().collect(); - - let disabled: Vec<_> = directives - .iter() - .filter(|directive| directive.level > STATIC_MAX_LEVEL) - .collect(); - - if !disabled.is_empty() { - #[cfg(feature = "ansi_term")] - use ansi_term::{Color, Style}; - // NOTE: We can't use a configured `MakeWriter` because the EnvFilter - // has no knowledge of any underlying subscriber or subscriber, which - // may or may not use a `MakeWriter`. - let warn = |msg: &str| { - #[cfg(not(feature = "ansi_term"))] - let msg = format!("warning: {}", msg); - #[cfg(feature = "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 = "ansi_term"))] - let msg = format!("note: {}", msg); - #[cfg(feature = "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 = "ansi_term"))] - let msg = format!("note: {}", msg); - #[cfg(feature = "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", - earlier_level, feature - )); - } - - let (dynamics, mut statics) = Directive::make_tables(directives); - let has_dynamics = !dynamics.is_empty(); - - if statics.is_empty() && !has_dynamics { - statics.add(directive::StaticDirective::default()); - } - - Self { - statics, - dynamics, - has_dynamics, - by_id: RwLock::new(HashMap::new()), - by_cs: RwLock::new(HashMap::new()), - } - } - - 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() - } - } -} - -impl<S: Subscriber> Layer<S> for EnvFilter { - 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() - } - } - - 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(), - ) - } + // === filtering methods === - fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { + /// 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 local access + iterating over the + // 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() { @@ -420,14 +495,15 @@ impl<S: Subscriber> Layer<S> for EnvFilter { } } - let enabled_by_scope = SCOPE.with(|scope| { - for filter in scope.borrow().iter() { + 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; } @@ -443,7 +519,33 @@ impl<S: Subscriber> Layer<S> for EnvFilter { false } - fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) { + /// 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); @@ -451,28 +553,37 @@ impl<S: Subscriber> Layer<S> for EnvFilter { } } - fn on_record(&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 on_enter(&self, id: &span::Id, _: Context<'_, S>) { + /// 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) { - SCOPE.with(|scope| scope.borrow_mut().push(span.level())); + self.scope.get_or_default().borrow_mut().push(span.level()); } } - fn on_exit(&self, id: &span::Id, _: Context<'_, S>) { + /// 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) { - SCOPE.with(|scope| scope.borrow_mut().pop()); + self.scope.get_or_default().borrow_mut().pop(); } } - fn on_close(&self, id: span::Id, _: Context<'_, S>) { + /// 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; @@ -481,6 +592,140 @@ impl<S: Subscriber> Layer<S> for EnvFilter { 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 { @@ -502,7 +747,7 @@ where impl Default for EnvFilter { fn default() -> Self { - Self::from_directives(std::iter::empty()) + Builder::default().from_directives(std::iter::empty()) } } @@ -735,4 +980,12 @@ mod tests { [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-0.3.3/src/filter/filter_fn.rs b/vendor/tracing-subscriber/src/filter/filter_fn.rs index 332bf860a..332bf860a 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs +++ b/vendor/tracing-subscriber/src/filter/filter_fn.rs 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-0.3.3/src/filter/layer_filters.rs b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs index e77fd3751..e50ee6f00 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs +++ b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -37,14 +37,16 @@ use std::{ cell::{Cell, RefCell}, fmt, marker::PhantomData, + ops::Deref, sync::Arc, thread_local, }; use tracing_core::{ span, subscriber::{Interest, Subscriber}, - Event, Metadata, + 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. @@ -158,7 +160,288 @@ 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 { @@ -179,38 +462,35 @@ impl<S> layer::Filter<S> for LevelFilter { } } -impl<S> layer::Filter<S> for Arc<dyn layer::Filter<S> + Send + Sync + 'static> { - #[inline] - fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { - (**self).enabled(meta, cx) - } +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).callsite_enabled(meta) - } + #[inline] + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + self.deref().callsite_enabled(meta) + } - #[inline] - fn max_level_hint(&self) -> Option<LevelFilter> { - (**self).max_level_hint() - } + #[inline] + fn max_level_hint(&self) -> Option<LevelFilter> { + self.deref().max_level_hint() + } + }; } -impl<S> layer::Filter<S> for Box<dyn layer::Filter<S> + Send + Sync + 'static> { - #[inline] - fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { - (**self).enabled(meta, cx) - } - - #[inline] - fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { - (**self).callsite_enabled(meta) - } +#[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!(); +} - #[inline] - fn max_level_hint(&self) -> Option<LevelFilter> { - (**self).max_level_hint() - } +#[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 === @@ -247,6 +527,78 @@ impl<L, F, S> Filtered<L, F, S> { 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> @@ -255,6 +607,10 @@ where 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); @@ -322,7 +678,9 @@ where fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, cx: Context<'_, S>) { self.did_enable(|| { - self.layer.on_new_span(attrs, id, cx.with_filter(self.id())); + let cx = cx.with_filter(self.id()); + self.filter.on_new_span(attrs, id, cx.clone()); + self.layer.on_new_span(attrs, id, cx); }) } @@ -333,6 +691,7 @@ where 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) } } @@ -345,6 +704,22 @@ where } } + 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())); @@ -353,19 +728,22 @@ where fn on_enter(&self, id: &span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(id, self.id()) { - self.layer.on_enter(id, cx) + 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.layer.on_exit(id, cx) + 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.layer.on_close(id, cx) + self.filter.on_close(id.clone(), cx.clone()); + self.layer.on_close(id, cx); } } @@ -544,6 +922,10 @@ impl fmt::Binary for FilterId { } } +// === impl FilterExt === + +impl<F, S> FilterExt<S> for F where F: layer::Filter<S> {} + // === impl FilterMap === impl FilterMap { @@ -709,6 +1091,14 @@ impl FilterState { } } + /// 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 diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/level.rs b/vendor/tracing-subscriber/src/filter/level.rs index 0fa601260..0fa601260 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/level.rs +++ b/vendor/tracing-subscriber/src/filter/level.rs diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/mod.rs b/vendor/tracing-subscriber/src/filter/mod.rs index 000a27195..000a27195 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/mod.rs +++ b/vendor/tracing-subscriber/src/filter/mod.rs diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/targets.rs b/vendor/tracing-subscriber/src/filter/targets.rs index e0c7fcf82..e1407114b 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/targets.rs +++ b/vendor/tracing-subscriber/src/filter/targets.rs @@ -20,7 +20,7 @@ use core::{ slice, str::FromStr, }; -use tracing_core::{Interest, Metadata, Subscriber}; +use tracing_core::{Interest, Level, Metadata, Subscriber}; /// A filter that enables or disables spans and events based on their [target] /// and [level]. @@ -111,7 +111,7 @@ use tracing_core::{Interest, Metadata, Subscriber}; /// by the user at runtime. /// /// The `Targets` filter can be used as a [per-layer filter][plf] *and* as a -/// [global filter]: +/// [global filter][global]: /// /// ```rust /// use tracing_subscriber::{ @@ -277,6 +277,62 @@ impl Targets { self } + /// Returns the default level for this filter, if one is set. + /// + /// The default level is used to filter any spans or events with targets + /// that do not match any of the configured set of prefixes. + /// + /// The default level can be set for a filter either by using + /// [`with_default`](Self::with_default) or when parsing from a filter string that includes a + /// level without a target (e.g. `"trace"`). + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::filter::{LevelFilter, Targets}; + /// + /// let filter = Targets::new().with_default(LevelFilter::INFO); + /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + /// + /// let filter: Targets = "info".parse().unwrap(); + /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + /// ``` + /// + /// The default level is `None` if no default is set: + /// + /// ``` + /// use tracing_subscriber::filter::Targets; + /// + /// let filter = Targets::new(); + /// assert_eq!(filter.default_level(), None); + /// + /// let filter: Targets = "my_crate=info".parse().unwrap(); + /// assert_eq!(filter.default_level(), None); + /// ``` + /// + /// Note that an unset default level (`None`) behaves like [`LevelFilter::OFF`] when the filter is + /// used, but it could also be set explicitly which may be useful to distinguish (such as when + /// merging multiple `Targets`). + /// + /// ``` + /// use tracing_subscriber::filter::{LevelFilter, Targets}; + /// + /// let filter = Targets::new().with_default(LevelFilter::OFF); + /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + /// + /// let filter: Targets = "off".parse().unwrap(); + /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + /// ``` + pub fn default_level(&self) -> Option<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. @@ -313,6 +369,35 @@ impl Targets { 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 @@ -657,6 +742,21 @@ mod tests { } #[test] + fn targets_default_level() { + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); + assert_eq!(filter.default_level(), None); + + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::OFF); + assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::OFF) + .with_default(LevelFilter::INFO); + assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + } + + #[test] // `println!` is only available with `libstd`. #[cfg(feature = "std")] fn size_of_filters() { |