From 94a0819fe3a0d679c3042a77bfe6a2afc505daea Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:28 +0200 Subject: Adding upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- .../src/filter/directive.rs | 424 +++++++++++ .../src/filter/env/directive.rs | 846 +++++++++++++++++++++ .../src/filter/env/field.rs | 416 ++++++++++ .../tracing-subscriber-0.3.3/src/filter/env/mod.rs | 738 ++++++++++++++++++ .../src/filter/filter_fn.rs | 749 ++++++++++++++++++ .../src/filter/layer_filters.rs | 830 ++++++++++++++++++++ .../tracing-subscriber-0.3.3/src/filter/level.rs | 27 + vendor/tracing-subscriber-0.3.3/src/filter/mod.rs | 66 ++ .../tracing-subscriber-0.3.3/src/filter/targets.rs | 681 +++++++++++++++++ 9 files changed, 4777 insertions(+) create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/directive.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/level.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/mod.rs create mode 100644 vendor/tracing-subscriber-0.3.3/src/filter/targets.rs (limited to 'vendor/tracing-subscriber-0.3.3/src/filter') diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/directive.rs b/vendor/tracing-subscriber-0.3.3/src/filter/directive.rs new file mode 100644 index 000000000..dd6b063c4 --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/directive.rs @@ -0,0 +1,424 @@ +use crate::filter::level::{self, LevelFilter}; +#[cfg(not(feature = "smallvec"))] +use alloc::vec; +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr}; +use tracing_core::Metadata; +/// Indicates that a string could not be parsed as a filtering directive. +#[derive(Debug)] +pub struct ParseError { + kind: ParseErrorKind, +} + +/// A directive which will statically enable or disable a given callsite. +/// +/// Unlike a dynamic directive, this can be cached by the callsite. +#[derive(Debug, PartialEq, Eq, Clone)] +pub(crate) struct StaticDirective { + pub(in crate::filter) target: Option, + pub(in crate::filter) field_names: Vec, + pub(in crate::filter) level: LevelFilter, +} + +#[cfg(feature = "smallvec")] +pub(crate) type FilterVec = smallvec::SmallVec<[T; 8]>; +#[cfg(not(feature = "smallvec"))] +pub(crate) type FilterVec = Vec; + +#[derive(Debug, PartialEq, Clone)] +pub(in crate::filter) struct DirectiveSet { + directives: FilterVec, + pub(in crate::filter) max_level: LevelFilter, +} + +pub(in crate::filter) trait Match { + fn cares_about(&self, meta: &Metadata<'_>) -> bool; + fn level(&self) -> &LevelFilter; +} + +#[derive(Debug)] +enum ParseErrorKind { + #[cfg(feature = "std")] + Field(Box), + Level(level::ParseError), + Other(Option<&'static str>), +} + +// === impl DirectiveSet === + +impl DirectiveSet { + #[cfg(feature = "env-filter")] + pub(crate) fn is_empty(&self) -> bool { + self.directives.is_empty() + } + + pub(crate) fn iter(&self) -> slice::Iter<'_, T> { + self.directives.iter() + } +} + +impl Default for DirectiveSet { + fn default() -> Self { + Self { + directives: FilterVec::new(), + max_level: LevelFilter::OFF, + } + } +} + +impl DirectiveSet { + pub(crate) fn directives(&self) -> impl Iterator { + self.directives.iter() + } + + pub(crate) fn directives_for<'a>( + &'a self, + metadata: &'a Metadata<'a>, + ) -> impl Iterator + 'a { + self.directives().filter(move |d| d.cares_about(metadata)) + } + + pub(crate) fn add(&mut self, directive: T) { + // does this directive enable a more verbose level than the current + // max? if so, update the max level. + let level = *directive.level(); + if level > self.max_level { + self.max_level = level; + } + // insert the directive into the vec of directives, ordered by + // specificity (length of target + number of field filters). this + // ensures that, when finding a directive to match a span or event, we + // search the directive set in most specific first order. + match self.directives.binary_search(&directive) { + Ok(i) => self.directives[i] = directive, + Err(i) => self.directives.insert(i, directive), + } + } + + #[cfg(test)] + pub(in crate::filter) fn into_vec(self) -> FilterVec { + self.directives + } +} + +impl FromIterator for DirectiveSet { + fn from_iter>(iter: I) -> Self { + let mut this = Self::default(); + this.extend(iter); + this + } +} + +impl Extend for DirectiveSet { + fn extend>(&mut self, iter: I) { + for directive in iter.into_iter() { + self.add(directive); + } + } +} + +impl IntoIterator for DirectiveSet { + type Item = T; + + #[cfg(feature = "smallvec")] + type IntoIter = smallvec::IntoIter<[T; 8]>; + #[cfg(not(feature = "smallvec"))] + type IntoIter = vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.directives.into_iter() + } +} + +// === impl Statics === + +impl DirectiveSet { + pub(crate) fn enabled(&self, meta: &Metadata<'_>) -> bool { + let level = meta.level(); + match self.directives_for(meta).next() { + Some(d) => d.level >= *level, + None => false, + } + } +} + +// === impl StaticDirective === + +impl StaticDirective { + pub(in crate::filter) fn new( + target: Option, + field_names: Vec, + level: LevelFilter, + ) -> Self { + Self { + target, + field_names, + level, + } + } +} + +impl Ord for StaticDirective { + fn cmp(&self, other: &StaticDirective) -> Ordering { + // We attempt to order directives by how "specific" they are. This + // ensures that we try the most specific directives first when + // attempting to match a piece of metadata. + + // First, we compare based on whether a target is specified, and the + // lengths of those targets if both have targets. + let ordering = self + .target + .as_ref() + .map(String::len) + .cmp(&other.target.as_ref().map(String::len)) + // Then we compare how many field names are matched by each directive. + .then_with(|| self.field_names.len().cmp(&other.field_names.len())) + // Finally, we fall back to lexicographical ordering if the directives are + // equally specific. Although this is no longer semantically important, + // we need to define a total ordering to determine the directive's place + // in the BTreeMap. + .then_with(|| { + self.target + .cmp(&other.target) + .then_with(|| self.field_names[..].cmp(&other.field_names[..])) + }) + .reverse(); + + #[cfg(debug_assertions)] + { + if ordering == Ordering::Equal { + debug_assert_eq!( + self.target, other.target, + "invariant violated: Ordering::Equal must imply a.target == b.target" + ); + debug_assert_eq!( + self.field_names, other.field_names, + "invariant violated: Ordering::Equal must imply a.field_names == b.field_names" + ); + } + } + + ordering + } +} + +impl PartialOrd for StaticDirective { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Match for StaticDirective { + fn cares_about(&self, meta: &Metadata<'_>) -> bool { + // Does this directive have a target filter, and does it match the + // metadata's target? + if let Some(ref target) = self.target { + if !meta.target().starts_with(&target[..]) { + return false; + } + } + + if meta.is_event() && !self.field_names.is_empty() { + let fields = meta.fields(); + for name in &self.field_names { + if fields.field(name).is_none() { + return false; + } + } + } + + true + } + + fn level(&self) -> &LevelFilter { + &self.level + } +} + +impl Default for StaticDirective { + fn default() -> Self { + StaticDirective { + target: None, + field_names: Vec::new(), + level: LevelFilter::ERROR, + } + } +} + +impl fmt::Display for StaticDirective { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut wrote_any = false; + if let Some(ref target) = self.target { + fmt::Display::fmt(target, f)?; + wrote_any = true; + } + + if !self.field_names.is_empty() { + f.write_str("[")?; + + let mut fields = self.field_names.iter(); + if let Some(field) = fields.next() { + write!(f, "{{{}", field)?; + for field in fields { + write!(f, ",{}", field)?; + } + f.write_str("}")?; + } + + f.write_str("]")?; + wrote_any = true; + } + + if wrote_any { + f.write_str("=")?; + } + + fmt::Display::fmt(&self.level, f) + } +} + +impl FromStr for StaticDirective { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + // This method parses a filtering directive in one of the following + // forms: + // + // * `foo=trace` (TARGET=LEVEL) + // * `foo[{bar,baz}]=info` (TARGET[{FIELD,+}]=LEVEL) + // * `trace` (bare LEVEL) + // * `foo` (bare TARGET) + let mut split = s.split('='); + let part0 = split + .next() + .ok_or_else(|| ParseError::msg("string must not be empty"))?; + + // Directive includes an `=`: + // * `foo=trace` + // * `foo[{bar}]=trace` + // * `foo[{bar,baz}]=trace` + if let Some(part1) = split.next() { + if split.next().is_some() { + return Err(ParseError::msg( + "too many '=' in filter directive, expected 0 or 1", + )); + } + + let mut split = part0.split("[{"); + let target = split.next().map(String::from); + let mut field_names = Vec::new(); + // Directive includes fields: + // * `foo[{bar}]=trace` + // * `foo[{bar,baz}]=trace` + if let Some(maybe_fields) = split.next() { + if split.next().is_some() { + return Err(ParseError::msg( + "too many '[{' in filter directive, expected 0 or 1", + )); + } + + if !maybe_fields.ends_with("}]") { + return Err(ParseError::msg("expected fields list to end with '}]'")); + } + + let fields = maybe_fields + .trim_end_matches("}]") + .split(',') + .filter_map(|s| { + if s.is_empty() { + None + } else { + Some(String::from(s)) + } + }); + field_names.extend(fields); + }; + let level = part1.parse()?; + return Ok(Self { + level, + field_names, + target, + }); + } + + // Okay, the part after the `=` was empty, the directive is either a + // bare level or a bare target. + // * `foo` + // * `info` + Ok(match part0.parse::() { + Ok(level) => Self { + level, + target: None, + field_names: Vec::new(), + }, + Err(_) => Self { + target: Some(String::from(part0)), + level: LevelFilter::TRACE, + field_names: Vec::new(), + }, + }) + } +} + +// === impl ParseError === + +impl ParseError { + #[cfg(feature = "env-filter")] + pub(crate) fn new() -> Self { + ParseError { + kind: ParseErrorKind::Other(None), + } + } + + pub(crate) fn msg(s: &'static str) -> Self { + ParseError { + kind: ParseErrorKind::Other(Some(s)), + } + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + ParseErrorKind::Other(None) => f.pad("invalid filter directive"), + ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg), + ParseErrorKind::Level(ref l) => l.fmt(f), + #[cfg(feature = "std")] + ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseError { + fn description(&self) -> &str { + "invalid filter directive" + } + + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self.kind { + ParseErrorKind::Other(_) => None, + ParseErrorKind::Level(ref l) => Some(l), + ParseErrorKind::Field(ref n) => Some(n.as_ref()), + } + } +} + +#[cfg(feature = "std")] +impl From> for ParseError { + fn from(e: Box) -> Self { + Self { + kind: ParseErrorKind::Field(e), + } + } +} + +impl From for ParseError { + fn from(l: level::ParseError) -> Self { + Self { + kind: ParseErrorKind::Level(l), + } + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs b/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs new file mode 100644 index 000000000..66ca23dc4 --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs @@ -0,0 +1,846 @@ +pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective}; +use crate::filter::{ + directive::{DirectiveSet, Match}, + env::{field, FieldMap}, + level::LevelFilter, +}; +use lazy_static::lazy_static; +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)] +#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] +pub struct Directive { + in_span: Option, + fields: Vec, + pub(crate) target: Option, + pub(crate) level: LevelFilter, +} + +/// A set of dynamic filtering directives. +pub(super) type Dynamics = DirectiveSet; + +/// A set of static filtering directives. +pub(super) type Statics = DirectiveSet; + +pub(crate) type CallsiteMatcher = MatchSet; +pub(crate) type SpanMatcher = MatchSet; + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct MatchSet { + field_matches: FilterVec, + base_level: LevelFilter, +} + +impl Directive { + pub(super) fn has_name(&self) -> bool { + self.in_span.is_some() + } + + pub(super) fn has_fields(&self) -> bool { + !self.fields.is_empty() + } + + pub(super) fn to_static(&self) -> Option { + if !self.is_static() { + return None; + } + + // TODO(eliza): these strings are all immutable; we should consider + // `Arc`ing them to make this more efficient... + let field_names = self.fields.iter().map(field::Match::name).collect(); + + Some(StaticDirective::new( + self.target.clone(), + field_names, + self.level, + )) + } + + fn is_static(&self) -> bool { + !self.has_name() && !self.fields.iter().any(field::Match::has_value) + } + + pub(super) fn is_dynamic(&self) -> bool { + self.has_name() || self.has_fields() + } + + pub(crate) fn field_matcher(&self, meta: &Metadata<'_>) -> Option { + let fieldset = meta.fields(); + let fields = self + .fields + .iter() + .filter_map( + |field::Match { + ref name, + ref value, + }| { + if let Some(field) = fieldset.field(name) { + let value = value.as_ref().cloned()?; + Some(Ok((field, value))) + } else { + Some(Err(())) + } + }, + ) + .collect::, ()>>() + .ok()?; + Some(field::CallsiteMatch { + fields, + level: self.level, + }) + } + + pub(super) fn make_tables( + directives: impl IntoIterator, + ) -> (Dynamics, Statics) { + // TODO(eliza): this could be made more efficient... + let (dyns, stats): (Vec, Vec) = + directives.into_iter().partition(Directive::is_dynamic); + let statics = stats + .into_iter() + .filter_map(|d| d.to_static()) + .chain(dyns.iter().filter_map(Directive::to_static)) + .collect(); + (Dynamics::from_iter(dyns), statics) + } +} + +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; + } + } + + true + } + + fn level(&self) -> &LevelFilter { + &self.level + } +} + +impl FromStr for Directive { + type Err = ParseError; + fn from_str(from: &str) -> Result { + lazy_static! { + static ref DIRECTIVE_RE: Regex = Regex::new( + r"(?x) + ^(?P(?i:trace|debug|info|warn|error|off|[0-5]))$ | + # ^^^. + # `note: we match log level names case-insensitively + ^ + (?: # target name or span name + (?P[\w:-]+)|(?P\[[^\]]*\]) + ){1,2} + (?: # level or nothing + =(?P(?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[^\]\{]+)?(?:\{(?P[^\}]*)\})?"#).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(); + } + + let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?; + + if let Some(level) = caps + .name("global_level") + .and_then(|s| s.as_str().parse().ok()) + { + return Ok(Directive { + level, + ..Default::default() + }); + } + + let target = caps.name("target").and_then(|c| { + let s = c.as_str(); + if s.parse::().is_ok() { + None + } else { + Some(s.to_owned()) + } + }); + + let (in_span, fields) = caps + .name("span") + .and_then(|cap| { + let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']'); + let caps = SPAN_PART_RE.captures(cap)?; + let span = caps.name("name").map(|c| c.as_str().to_owned()); + let fields = caps + .name("fields") + .map(|c| { + FIELD_FILTER_RE + .find_iter(c.as_str()) + .map(|c| c.as_str().parse()) + .collect::, _>>() + }) + .unwrap_or_else(|| Ok(Vec::new())); + Some((span, fields)) + }) + .unwrap_or_else(|| (None, Ok(Vec::new()))); + + let level = caps + .name("level") + .and_then(|l| l.as_str().parse().ok()) + // Setting the target without the level enables every level for that target + .unwrap_or(LevelFilter::TRACE); + + Ok(Directive { + level, + target, + in_span, + fields: fields?, + }) + } +} + +impl Default for Directive { + fn default() -> Self { + Directive { + level: LevelFilter::OFF, + target: None, + in_span: None, + fields: Vec::new(), + } + } +} + +impl PartialOrd for Directive { + fn partial_cmp(&self, other: &Directive) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Directive { + fn cmp(&self, other: &Directive) -> Ordering { + // We attempt to order directives by how "specific" they are. This + // ensures that we try the most specific directives first when + // attempting to match a piece of metadata. + + // First, we compare based on whether a target is specified, and the + // lengths of those targets if both have targets. + let ordering = self + .target + .as_ref() + .map(String::len) + .cmp(&other.target.as_ref().map(String::len)) + // Next compare based on the presence of span names. + .then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some())) + // Then we compare how many fields are defined by each + // directive. + .then_with(|| self.fields.len().cmp(&other.fields.len())) + // Finally, we fall back to lexicographical ordering if the directives are + // equally specific. Although this is no longer semantically important, + // we need to define a total ordering to determine the directive's place + // in the BTreeMap. + .then_with(|| { + self.target + .cmp(&other.target) + .then_with(|| self.in_span.cmp(&other.in_span)) + .then_with(|| self.fields[..].cmp(&other.fields[..])) + }) + .reverse(); + + #[cfg(debug_assertions)] + { + if ordering == Ordering::Equal { + debug_assert_eq!( + self.target, other.target, + "invariant violated: Ordering::Equal must imply a.target == b.target" + ); + debug_assert_eq!( + self.in_span, other.in_span, + "invariant violated: Ordering::Equal must imply a.in_span == b.in_span" + ); + debug_assert_eq!( + self.fields, other.fields, + "invariant violated: Ordering::Equal must imply a.fields == b.fields" + ); + } + } + + ordering + } +} + +impl fmt::Display for Directive { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut wrote_any = false; + if let Some(ref target) = self.target { + fmt::Display::fmt(target, f)?; + wrote_any = true; + } + + if self.in_span.is_some() || !self.fields.is_empty() { + f.write_str("[")?; + + if let Some(ref span) = self.in_span { + fmt::Display::fmt(span, f)?; + } + + let mut fields = self.fields.iter(); + if let Some(field) = fields.next() { + write!(f, "{{{}", field)?; + for field in fields { + write!(f, ",{}", field)?; + } + f.write_str("}")?; + } + + f.write_str("]")?; + wrote_any = true; + } + + if wrote_any { + f.write_str("=")?; + } + + fmt::Display::fmt(&self.level, f) + } +} + +impl From for Directive { + fn from(level: LevelFilter) -> Self { + Self { + level, + ..Self::default() + } + } +} + +impl From for Directive { + fn from(level: Level) -> Self { + LevelFilter::from_level(level).into() + } +} + +// === impl Dynamics === + +impl Dynamics { + pub(crate) fn matcher(&self, metadata: &Metadata<'_>) -> Option { + let mut base_level = None; + let field_matches = self + .directives_for(metadata) + .filter_map(|d| { + if let Some(f) = d.field_matcher(metadata) { + return Some(f); + } + match base_level { + Some(ref b) if d.level > *b => base_level = Some(d.level), + None => base_level = Some(d.level), + _ => {} + } + None + }) + .collect(); + + if let Some(base_level) = base_level { + Some(CallsiteMatcher { + field_matches, + base_level, + }) + } else if !field_matches.is_empty() { + Some(CallsiteMatcher { + field_matches, + base_level: base_level.unwrap_or(LevelFilter::OFF), + }) + } else { + None + } + } + + pub(crate) fn has_value_filters(&self) -> bool { + self.directives() + .any(|d| d.fields.iter().any(|f| f.value.is_some())) + } +} + +// ===== impl DynamicMatch ===== + +impl CallsiteMatcher { + /// Create a new `SpanMatch` for a given instance of the matched callsite. + pub(crate) fn to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher { + let field_matches = self + .field_matches + .iter() + .map(|m| { + let m = m.to_span_match(); + attrs.record(&mut m.visitor()); + m + }) + .collect(); + SpanMatcher { + field_matches, + base_level: self.base_level, + } + } +} + +impl SpanMatcher { + /// Returns the level currently enabled for this callsite. + pub(crate) fn level(&self) -> LevelFilter { + self.field_matches + .iter() + .filter_map(field::SpanMatch::filter) + .max() + .unwrap_or(self.base_level) + } + + pub(crate) fn record_update(&self, record: &span::Record<'_>) { + for m in &self.field_matches { + record.record(&mut m.visitor()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn parse_directives(dirs: impl AsRef) -> Vec { + dirs.as_ref() + .split(',') + .filter_map(|s| s.parse().ok()) + .collect() + } + + fn expect_parse(dirs: impl AsRef) -> Vec { + dirs.as_ref() + .split(',') + .map(|s| { + s.parse() + .unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err)) + }) + .collect() + } + + #[test] + fn directive_ordering_by_target_len() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse( + "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn", + ); + dirs.sort_unstable(); + + let expected = vec![ + "a_really_long_name_with_no_colons", + "foo::bar::baz", + "foo::bar", + "foo", + ]; + let sorted = dirs + .iter() + .map(|d| d.target.as_ref().unwrap()) + .collect::>(); + + assert_eq!(expected, sorted); + } + #[test] + fn directive_ordering_by_span() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn"); + dirs.sort_unstable(); + + let expected = vec!["baz::quux", "bar", "foo", "a"]; + let sorted = dirs + .iter() + .map(|d| d.target.as_ref().unwrap()) + .collect::>(); + + assert_eq!(expected, sorted); + } + + #[test] + fn directive_ordering_uses_lexicographic_when_equal() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info"); + dirs.sort_unstable(); + + let expected = vec![ + ("span", Some("b")), + ("span", Some("a")), + ("c", None), + ("b", None), + ("a", None), + ]; + let sorted = dirs + .iter() + .map(|d| { + ( + d.target.as_ref().unwrap().as_ref(), + d.in_span.as_ref().map(String::as_ref), + ) + }) + .collect::>(); + + assert_eq!(expected, sorted); + } + + // TODO: this test requires the parser to support directives with multiple + // fields, which it currently can't handle. We should enable this test when + // that's implemented. + #[test] + #[ignore] + fn directive_ordering_by_field_num() { + // TODO(eliza): it would be nice to have a property-based test for this + // instead. + let mut dirs = expect_parse( + "b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info" + ); + dirs.sort_unstable(); + + let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"]; + let sorted = dirs + .iter() + .map(|d| d.target.as_ref().unwrap()) + .collect::>(); + + assert_eq!(expected, sorted); + } + + #[test] + fn parse_directives_ralith() { + let dirs = parse_directives("common=trace,server=trace"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("common".to_string())); + assert_eq!(dirs[0].level, LevelFilter::TRACE); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("server".to_string())); + assert_eq!(dirs[1].level, LevelFilter::TRACE); + assert_eq!(dirs[1].in_span, None); + } + + #[test] + fn parse_directives_ralith_uc() { + let dirs = parse_directives("common=INFO,server=DEBUG"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("common".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("server".to_string())); + assert_eq!(dirs[1].level, LevelFilter::DEBUG); + assert_eq!(dirs[1].in_span, None); + } + + #[test] + fn parse_directives_ralith_mixed() { + let dirs = parse_directives("common=iNfo,server=dEbUg"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("common".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("server".to_string())); + assert_eq!(dirs[1].level, LevelFilter::DEBUG); + assert_eq!(dirs[1].in_span, None); + } + + #[test] + fn parse_directives_valid() { + let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); + assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::TRACE); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate2".to_string())); + assert_eq!(dirs[2].level, LevelFilter::DEBUG); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate3".to_string())); + assert_eq!(dirs[3].level, LevelFilter::OFF); + assert_eq!(dirs[3].in_span, None); + } + + #[test] + + fn parse_level_directives() { + let dirs = parse_directives( + "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ + crate2=debug,crate3=trace,crate3::mod2::mod1=off", + ); + assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::INFO); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].in_span, None); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].in_span, None); + + assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); + assert_eq!(dirs[5].level, LevelFilter::OFF); + assert_eq!(dirs[5].in_span, None); + } + + #[test] + fn parse_uppercase_level_directives() { + let dirs = parse_directives( + "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ + crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", + ); + assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::INFO); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].in_span, None); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].in_span, None); + + assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); + assert_eq!(dirs[5].level, LevelFilter::OFF); + assert_eq!(dirs[5].in_span, None); + } + + #[test] + fn parse_numeric_level_directives() { + let dirs = parse_directives( + "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\ + crate3=5,crate3::mod2::mod1=0", + ); + assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, None); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::INFO); + assert_eq!(dirs[2].in_span, None); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].in_span, None); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].in_span, None); + + assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); + assert_eq!(dirs[5].level, LevelFilter::OFF); + assert_eq!(dirs[5].in_span, None); + } + + #[test] + fn parse_directives_invalid_crate() { + // test parse_directives with multiple = in specification + let dirs = parse_directives("crate1::mod1=warn=info,crate2=debug"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::DEBUG); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_invalid_level() { + // test parse_directives with 'noNumber' as log level + let dirs = parse_directives("crate1::mod1=noNumber,crate2=debug"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::DEBUG); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_string_level() { + // test parse_directives with 'warn' as log level + let dirs = parse_directives("crate1::mod1=wrong,crate2=warn"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::WARN); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_empty_level() { + // test parse_directives with '' as log level + let dirs = parse_directives("crate1::mod1=wrong,crate2="); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::TRACE); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_global() { + // test parse_directives with no crate + let dirs = parse_directives("warn,crate2=debug"); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, None); + assert_eq!(dirs[0].level, LevelFilter::WARN); + assert_eq!(dirs[1].in_span, None); + + assert_eq!(dirs[1].target, Some("crate2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::DEBUG); + assert_eq!(dirs[1].in_span, None); + } + + // helper function for tests below + fn test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter) { + let dirs = parse_directives(directive_to_test); + assert_eq!( + dirs.len(), + 1, + "\ninput: \"{}\"; parsed: {:#?}", + directive_to_test, + dirs + ); + assert_eq!(dirs[0].target, None); + assert_eq!(dirs[0].level, level_expected); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_global_bare_warn_lc() { + // test parse_directives with no crate, in isolation, all lowercase + test_parse_bare_level("warn", LevelFilter::WARN); + } + + #[test] + fn parse_directives_global_bare_warn_uc() { + // test parse_directives with no crate, in isolation, all uppercase + test_parse_bare_level("WARN", LevelFilter::WARN); + } + + #[test] + fn parse_directives_global_bare_warn_mixed() { + // test parse_directives with no crate, in isolation, mixed case + test_parse_bare_level("wArN", LevelFilter::WARN); + } + + #[test] + fn parse_directives_valid_with_spans() { + let dirs = parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug"); + assert_eq!(dirs.len(), 3, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::ERROR); + assert_eq!(dirs[0].in_span, Some("foo".to_string())); + + assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, LevelFilter::TRACE); + assert_eq!(dirs[1].in_span, Some("bar".to_string())); + + assert_eq!(dirs[2].target, Some("crate2".to_string())); + assert_eq!(dirs[2].level, LevelFilter::DEBUG); + assert_eq!(dirs[2].in_span, Some("baz".to_string())); + } + + #[test] + fn parse_directives_with_dash_in_target_name() { + let dirs = parse_directives("target-name=info"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("target-name".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, None); + } + + #[test] + fn parse_directives_with_dash_in_span_name() { + // Reproduces https://github.com/tokio-rs/tracing/issues/1367 + + let dirs = parse_directives("target[span-name]=info"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("target".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, Some("span-name".to_string())); + } + + #[test] + fn parse_directives_with_special_characters_in_span_name() { + let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}"; + + let dirs = parse_directives(format!("target[{}]=info", span_name)); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("target".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, Some(span_name.to_string())); + } + + #[test] + fn parse_directives_with_invalid_span_chars() { + let invalid_span_name = "]{"; + + let dirs = parse_directives(format!("target[{}]=info", invalid_span_name)); + assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs); + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs b/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs new file mode 100644 index 000000000..970850f92 --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs @@ -0,0 +1,416 @@ +use matchers::Pattern; +use std::{ + cmp::Ordering, + error::Error, + fmt, + str::FromStr, + sync::{ + atomic::{AtomicBool, Ordering::*}, + Arc, + }, +}; + +use super::{FieldMap, LevelFilter}; +use tracing_core::field::{Field, Visit}; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct Match { + pub(crate) name: String, // TODO: allow match patterns for names? + pub(crate) value: Option, +} + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct CallsiteMatch { + pub(crate) fields: FieldMap, + pub(crate) level: LevelFilter, +} + +#[derive(Debug)] +pub(crate) struct SpanMatch { + fields: FieldMap<(ValueMatch, AtomicBool)>, + level: LevelFilter, + has_matched: AtomicBool, +} + +pub(crate) struct MatchVisitor<'a> { + inner: &'a SpanMatch, +} + +#[derive(Debug, Clone)] +pub(crate) enum ValueMatch { + Bool(bool), + F64(f64), + U64(u64), + I64(i64), + NaN, + Pat(Box), +} + +impl Eq for ValueMatch {} + +impl PartialEq for ValueMatch { + fn eq(&self, other: &Self) -> bool { + use ValueMatch::*; + match (self, other) { + (Bool(a), Bool(b)) => a.eq(b), + (F64(a), F64(b)) => { + debug_assert!(!a.is_nan()); + debug_assert!(!b.is_nan()); + + a.eq(b) + } + (U64(a), U64(b)) => a.eq(b), + (I64(a), I64(b)) => a.eq(b), + (NaN, NaN) => true, + (Pat(a), Pat(b)) => a.eq(b), + _ => false, + } + } +} + +impl Ord for ValueMatch { + fn cmp(&self, other: &Self) -> Ordering { + use ValueMatch::*; + match (self, other) { + (Bool(this), Bool(that)) => this.cmp(that), + (Bool(_), _) => Ordering::Less, + + (F64(this), F64(that)) => this + .partial_cmp(that) + .expect("`ValueMatch::F64` may not contain `NaN` values"), + (F64(_), Bool(_)) => Ordering::Greater, + (F64(_), _) => Ordering::Less, + + (NaN, NaN) => Ordering::Equal, + (NaN, Bool(_)) | (NaN, F64(_)) => Ordering::Greater, + (NaN, _) => Ordering::Less, + + (U64(this), U64(that)) => this.cmp(that), + (U64(_), Bool(_)) | (U64(_), F64(_)) | (U64(_), NaN) => Ordering::Greater, + (U64(_), _) => Ordering::Less, + + (I64(this), I64(that)) => this.cmp(that), + (I64(_), Bool(_)) | (I64(_), F64(_)) | (I64(_), NaN) | (I64(_), U64(_)) => { + Ordering::Greater + } + (I64(_), _) => Ordering::Less, + + (Pat(this), Pat(that)) => this.cmp(that), + (Pat(_), _) => Ordering::Greater, + } + } +} + +impl PartialOrd for ValueMatch { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct MatchPattern { + pub(crate) matcher: Pattern, + pattern: Arc, +} + +/// Indicates that a field name specified in a filter directive was invalid. +#[derive(Clone, Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] +pub struct BadName { + name: String, +} + +// === impl Match === + +impl FromStr for Match { + type Err = Box; + fn from_str(s: &str) -> Result { + let mut parts = s.split('='); + let name = parts + .next() + .ok_or_else(|| BadName { + name: "".to_string(), + })? + // TODO: validate field name + .to_string(); + let value = parts.next().map(ValueMatch::from_str).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)?; + if let Some(ref value) = self.value { + write!(f, "={}", value)?; + } + Ok(()) + } +} + +impl Ord for Match { + fn cmp(&self, other: &Self) -> Ordering { + // Ordering for `Match` directives is based first on _whether_ a value + // is matched or not. This is semantically meaningful --- we would + // prefer to check directives that match values first as they are more + // specific. + let has_value = match (self.value.as_ref(), other.value.as_ref()) { + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + _ => Ordering::Equal, + }; + // If both directives match a value, we fall back to the field names in + // length + lexicographic ordering, and if these are equal as well, we + // compare the match directives. + // + // This ordering is no longer semantically meaningful but is necessary + // so that the directives can be stored in the `BTreeMap` in a defined + // order. + has_value + .then_with(|| self.name.cmp(&other.name)) + .then_with(|| self.value.cmp(&other.value)) + } +} + +impl PartialOrd for Match { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// === impl ValueMatch === + +fn value_match_f64(v: f64) -> ValueMatch { + if v.is_nan() { + ValueMatch::NaN + } else { + ValueMatch::F64(v) + } +} + +impl FromStr for ValueMatch { + type Err = matchers::Error; + fn from_str(s: &str) -> Result { + s.parse::() + .map(ValueMatch::Bool) + .or_else(|_| s.parse::().map(ValueMatch::U64)) + .or_else(|_| s.parse::().map(ValueMatch::I64)) + .or_else(|_| s.parse::().map(value_match_f64)) + .or_else(|_| { + s.parse::() + .map(|p| ValueMatch::Pat(Box::new(p))) + }) + } +} + +impl fmt::Display for ValueMatch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f), + ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f), + } + } +} + +// === impl MatchPattern === + +impl FromStr for MatchPattern { + type Err = matchers::Error; + fn from_str(s: &str) -> Result { + let matcher = s.parse::()?; + Ok(Self { + matcher, + pattern: s.to_owned().into(), + }) + } +} + +impl fmt::Display for MatchPattern { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self.pattern, f) + } +} + +impl AsRef for MatchPattern { + #[inline] + fn as_ref(&self) -> &str { + self.pattern.as_ref() + } +} + +impl MatchPattern { + #[inline] + fn str_matches(&self, s: &impl AsRef) -> bool { + self.matcher.matches(s) + } + + #[inline] + fn debug_matches(&self, d: &impl fmt::Debug) -> bool { + self.matcher.debug_matches(d) + } +} + +impl PartialEq for MatchPattern { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.pattern == other.pattern + } +} + +impl Eq for MatchPattern {} + +impl PartialOrd for MatchPattern { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.pattern.cmp(&other.pattern)) + } +} + +impl Ord for MatchPattern { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.pattern.cmp(&other.pattern) + } +} + +// === impl BadName === + +impl Error for BadName {} + +impl fmt::Display for BadName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid field name `{}`", self.name) + } +} + +impl CallsiteMatch { + pub(crate) fn to_span_match(&self) -> SpanMatch { + let fields = self + .fields + .iter() + .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false)))) + .collect(); + SpanMatch { + fields, + level: self.level, + has_matched: AtomicBool::new(false), + } + } +} + +impl SpanMatch { + pub(crate) fn visitor(&self) -> MatchVisitor<'_> { + MatchVisitor { inner: self } + } + + #[inline] + pub(crate) fn is_matched(&self) -> bool { + if self.has_matched.load(Acquire) { + return true; + } + self.is_matched_slow() + } + + #[inline(never)] + fn is_matched_slow(&self) -> bool { + let matched = self + .fields + .values() + .all(|(_, matched)| matched.load(Acquire)); + if matched { + self.has_matched.store(true, Release); + } + matched + } + + #[inline] + pub(crate) fn filter(&self) -> Option { + if self.is_matched() { + Some(self.level) + } else { + None + } + } +} + +impl<'a> Visit for MatchVisitor<'a> { + fn record_f64(&mut self, field: &Field, value: f64) { + match self.inner.fields.get(field) { + Some((ValueMatch::NaN, ref matched)) if value.is_nan() => { + matched.store(true, Release); + } + Some((ValueMatch::F64(ref e), ref matched)) + if (value - *e).abs() < std::f64::EPSILON => + { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_i64(&mut self, field: &Field, value: i64) { + use std::convert::TryInto; + + match self.inner.fields.get(field) { + Some((ValueMatch::I64(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_u64(&mut self, field: &Field, value: u64) { + match self.inner.fields.get(field) { + Some((ValueMatch::U64(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_bool(&mut self, field: &Field, value: bool) { + match self.inner.fields.get(field) { + Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_str(&mut self, field: &Field, value: &str) { + match self.inner.fields.get(field) { + Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + match self.inner.fields.get(field) { + Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release); + } + _ => {} + } + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs b/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs new file mode 100644 index 000000000..81fe0e62d --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs @@ -0,0 +1,738 @@ +//! A `Layer` that enables or disables spans and events based on a set of +//! filtering directives. + +// these are publicly re-exported, but the compiler doesn't realize +// that for some reason. +#[allow(unreachable_pub)] +pub use self::{directive::Directive, field::BadName as BadFieldName}; +mod directive; +mod field; + +use crate::{ + filter::LevelFilter, + layer::{Context, Layer}, + sync::RwLock, +}; +use directive::ParseError; +use std::{cell::RefCell, collections::HashMap, env, error::Error, fmt, str::FromStr}; +use tracing_core::{ + callsite, + field::Field, + span, + subscriber::{Interest, Subscriber}, + Metadata, +}; + +/// A [`Layer`] which filters spans and events based on a set of filter +/// directives. +/// +/// # Directives +/// +/// A filter consists of one or more comma-separated directives which match on [`Span`]s and [`Event`]s. +/// Each directive may have a corresponding maximum verbosity [`level`] which +/// enables (e.g., _selects for_) spans and events that match. Like `log`, +/// `tracing` considers less exclusive levels (like `trace` or `info`) to be more +/// verbose than more exclusive levels (like `error` or `warn`). +/// +/// The directive syntax is similar to that of [`env_logger`]'s. At a high level, the syntax for directives +/// consists of several parts: +/// +/// ```text +/// target[span{field=value}]=level +/// ``` +/// +/// Each component (`target`, `span`, `field`, `value`, and `level`) will be covered in turn. +/// +/// - `target` matches the event or span's target. In general, this is the module path and/or crate name. +/// Examples of targets `h2`, `tokio::net`, or `tide::server`. For more information on targets, +/// please refer to [`Metadata`]'s documentation. +/// - `span` matches on the span's name. If a `span` directive is provided alongside a `target`, +/// the `span` directive will match on spans _within_ the `target`. +/// - `field` matches on [fields] within spans. Field names can also be supplied without a `value` +/// and will match on any [`Span`] or [`Event`] that has a field with that name. +/// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`. +/// - `value` matches on the value of a span's field. If a value is a numeric literal or a bool, +/// it will match _only_ on that value. Otherwise, this filter acts as a regex on +/// the `std::fmt::Debug` output from the value. +/// - `level` sets a maximum verbosity level accepted by this directive. +/// +/// ## Usage Notes +/// +/// - The portion of the directive which is included within the square brackets is `tracing`-specific. +/// - Any portion of the directive can be omitted. +/// - The sole exception are the `field` and `value` directives. If a `value` is provided, +/// a `field` must _also_ be provided. However, the converse does not hold, as fields can +/// be matched without a value. +/// - If only a level is provided, it will set the maximum level for all `Span`s and `Event`s +/// that are not enabled by other filters. +/// - A directive without a level will enable anything that it matches. This is equivalent to `=trace`. +/// - When a crate has a dash in its name, the default target for events will be the +/// crate's module path as it appears in Rust. This means every dash will be replaced +/// with an underscore. +/// - A dash in a target will only appear when being specified explicitly: +/// `tracing::info!(target: "target-name", ...);` +/// +/// ## Examples +/// +/// - `tokio::net=info` will enable all spans or events that: +/// - have the `tokio::net` target, +/// - at the level `info` or above. +/// - `warn,tokio::net=info` will enable all spans and events that: +/// - are at the level `warn` or above, *or* +/// - have the `tokio::net` target at the level `info` or above. +/// - `my_crate[span_a]=trace` will enable all spans and events that: +/// - are within the `span_a` span or named `span_a` _if_ `span_a` has the target `my_crate`, +/// - at the level `trace` or above. +/// - `[span_b{name=\"bob\"}]` will enable all spans or event that: +/// - have _any_ target, +/// - are inside a span named `span_b`, +/// - which has a field named `name` with value `bob`, +/// - at _any_ level. +/// +/// 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`]. +/// +/// [`Span`]: tracing_core::span +/// [fields]: tracing_core::Field +/// [`Event`]: tracing_core::Event +/// [`level`]: tracing_core::Level +/// [`Metadata`]: tracing_core::Metadata +/// [`Targets`]: crate::filter::Targets +#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] +#[derive(Debug)] +pub struct EnvFilter { + statics: directive::Statics, + dynamics: directive::Dynamics, + has_dynamics: bool, + by_id: RwLock>, + by_cs: RwLock>, +} + +thread_local! { + static SCOPE: RefCell> = RefCell::new(Vec::new()); +} + +type FieldMap = HashMap; + +/// Indicates that an error occurred while parsing a `EnvFilter` from an +/// environment variable. +#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] +#[derive(Debug)] +pub struct FromEnvError { + kind: ErrorKind, +} + +#[derive(Debug)] +enum ErrorKind { + Parse(ParseError), + Env(env::VarError), +} + +impl EnvFilter { + /// `RUST_LOG` is the default environment variable used by + /// [`EnvFilter::from_default_env`] and [`EnvFilter::try_from_default_env`]. + /// + /// [`EnvFilter::from_default_env`]: #method.from_default_env + /// [`EnvFilter::try_from_default_env`]: #method.try_from_default_env + pub const DEFAULT_ENV: &'static str = "RUST_LOG"; + + /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment + /// variable, ignoring any invalid filter directives. + pub fn from_default_env() -> Self { + Self::from_env(Self::DEFAULT_ENV) + } + + /// Returns a new `EnvFilter` from the value of the given environment + /// variable, ignoring any invalid filter directives. + pub fn from_env>(env: A) -> Self { + env::var(env.as_ref()).map(Self::new).unwrap_or_default() + } + + /// Returns a new `EnvFilter` from the directives in the given string, + /// ignoring any that are invalid. + pub fn new>(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) + } + + /// Returns a new `EnvFilter` from the directives in the given string, + /// or an error if any are invalid. + pub fn try_new>(dirs: S) -> Result { + let directives = dirs + .as_ref() + .split(',') + .map(|s| s.parse()) + .collect::, _>>()?; + Ok(Self::from_directives(directives)) + } + + /// 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. + pub fn try_from_default_env() -> Result { + Self::try_from_env(Self::DEFAULT_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. + pub fn try_from_env>(env: A) -> Result { + env::var(env.as_ref())?.parse().map_err(Into::into) + } + + /// Add a filtering directive to this `EnvFilter`. + /// + /// The added directive will be used in addition to any previously set + /// directives, either added using this method or provided when the filter + /// is constructed. + /// + /// Filters may be created from [`LevelFilter`] or [`Level`], which will + /// enable all traces at or below a certain verbosity level, or + /// parsed from a string specifying a directive. + /// + /// If a filter directive is inserted that matches exactly the same spans + /// and events as a previous filter, but sets a different level for those + /// spans and events, the previous directive is overwritten. + /// + /// [`LevelFilter`]: ../filter/struct.LevelFilter.html + /// [`Level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html + /// + /// # Examples + /// + /// From [`LevelFilter`]: + //// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// let mut filter = EnvFilter::from_default_env() + /// .add_directive(LevelFilter::INFO.into()); + /// ``` + /// + /// Or from [`Level`]: + /// + /// ```rust + /// # use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// # use tracing::Level; + /// let mut filter = EnvFilter::from_default_env() + /// .add_directive(Level::INFO.into()); + /// ``` + //// + /// Parsed from a string: + //// + /// ```rust + /// use tracing_subscriber::filter::{EnvFilter, Directive}; + /// + /// # fn try_mk_filter() -> Result<(), Box> { + /// let mut filter = EnvFilter::try_from_default_env()? + /// .add_directive("my_crate::module=trace".parse()?) + /// .add_directive("my_crate::my_other_module::something=info".parse()?); + /// # Ok(()) + /// # } + /// ``` + pub fn add_directive(mut self, directive: Directive) -> Self { + if let Some(stat) = directive.to_static() { + self.statics.add(stat) + } else { + self.has_dynamics = true; + self.dynamics.add(directive); + } + self + } + + fn from_directives(directives: impl IntoIterator) -> 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 Layer 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 { + 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(), + ) + } + + fn enabled(&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 + // spans in the current scope. + if self.has_dynamics && self.dynamics.max_level >= *level { + if metadata.is_span() { + // If the metadata is a span, see if we care about its callsite. + let enabled_by_cs = self + .by_cs + .read() + .ok() + .map(|by_cs| by_cs.contains_key(&metadata.callsite())) + .unwrap_or(false); + if enabled_by_cs { + return true; + } + } + + let enabled_by_scope = SCOPE.with(|scope| { + for filter in scope.borrow().iter() { + if filter >= level { + return true; + } + } + false + }); + if enabled_by_scope { + return true; + } + } + + // is it possible for a static filter directive to enable this event? + if self.statics.max_level >= *level { + // Otherwise, fall back to checking if the callsite is + // statically enabled. + return self.statics.enabled(metadata); + } + + false + } + + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) { + let by_cs = try_lock!(self.by_cs.read()); + if let Some(cs) = by_cs.get(&attrs.metadata().callsite()) { + let span = cs.to_span_match(attrs); + try_lock!(self.by_id.write()).insert(id.clone(), span); + } + } + + 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>) { + // 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())); + } + } + + fn on_exit(&self, id: &span::Id, _: Context<'_, S>) { + if self.cares_about_span(id) { + SCOPE.with(|scope| scope.borrow_mut().pop()); + } + } + + fn on_close(&self, id: span::Id, _: Context<'_, S>) { + // If we don't need to acquire a write lock, avoid doing so. + if !self.cares_about_span(&id) { + return; + } + + let mut spans = try_lock!(self.by_id.write()); + spans.remove(&id); + } +} + +impl FromStr for EnvFilter { + type Err = directive::ParseError; + + fn from_str(spec: &str) -> Result { + Self::try_new(spec) + } +} + +impl From for EnvFilter +where + S: AsRef, +{ + fn from(s: S) -> Self { + Self::new(s) + } +} + +impl Default for EnvFilter { + fn default() -> Self { + Self::from_directives(std::iter::empty()) + } +} + +impl fmt::Display for EnvFilter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut statics = self.statics.iter(); + let wrote_statics = if let Some(next) = statics.next() { + fmt::Display::fmt(next, f)?; + for directive in statics { + write!(f, ",{}", directive)?; + } + true + } else { + false + }; + + let mut dynamics = self.dynamics.iter(); + if let Some(next) = dynamics.next() { + if wrote_statics { + f.write_str(",")?; + } + fmt::Display::fmt(next, f)?; + for directive in dynamics { + write!(f, ",{}", directive)?; + } + } + Ok(()) + } +} + +// ===== impl FromEnvError ===== + +impl From for FromEnvError { + fn from(p: directive::ParseError) -> Self { + Self { + kind: ErrorKind::Parse(p), + } + } +} + +impl From for FromEnvError { + fn from(v: env::VarError) -> Self { + Self { + kind: ErrorKind::Env(v), + } + } +} + +impl fmt::Display for FromEnvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + ErrorKind::Parse(ref p) => p.fmt(f), + ErrorKind::Env(ref e) => e.fmt(f), + } + } +} + +impl Error for FromEnvError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self.kind { + ErrorKind::Parse(ref p) => Some(p), + ErrorKind::Env(ref e) => Some(e), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tracing_core::field::FieldSet; + use tracing_core::*; + + struct NoSubscriber; + impl Subscriber for NoSubscriber { + #[inline] + fn register_callsite(&self, _: &'static Metadata<'static>) -> subscriber::Interest { + subscriber::Interest::always() + } + fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { + span::Id::from_u64(0xDEAD) + } + fn event(&self, _event: &Event<'_>) {} + fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {} + fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {} + + #[inline] + fn enabled(&self, _metadata: &Metadata<'_>) -> bool { + true + } + fn enter(&self, _span: &span::Id) {} + fn exit(&self, _span: &span::Id) {} + } + + struct Cs; + impl Callsite for Cs { + fn set_interest(&self, _interest: Interest) {} + fn metadata(&self) -> &Metadata<'_> { + unimplemented!() + } + } + + #[test] + fn callsite_enabled_no_span_directive() { + let filter = EnvFilter::new("app=debug").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&[], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_never()); + } + + #[test] + fn callsite_off() { + let filter = EnvFilter::new("app=off").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::ERROR, + None, + None, + None, + FieldSet::new(&[], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_never()); + } + + #[test] + fn callsite_enabled_includes_span_directive() { + let filter = EnvFilter::new("app[mySpan]=debug").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&[], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_always()); + } + + #[test] + fn callsite_enabled_includes_span_directive_field() { + let filter = + EnvFilter::new("app[mySpan{field=\"value\"}]=debug").with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&["field"], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_always()); + } + + #[test] + fn callsite_enabled_includes_span_directive_multiple_fields() { + let filter = EnvFilter::new("app[mySpan{field=\"value\",field2=2}]=debug") + .with_subscriber(NoSubscriber); + static META: &Metadata<'static> = &Metadata::new( + "mySpan", + "app", + Level::TRACE, + None, + None, + None, + FieldSet::new(&["field"], identify_callsite!(&Cs)), + Kind::SPAN, + ); + + let interest = filter.register_callsite(META); + assert!(interest.is_never()); + } + + #[test] + fn roundtrip() { + let f1: EnvFilter = + "[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug" + .parse() + .unwrap(); + let f2: EnvFilter = format!("{}", f1).parse().unwrap(); + assert_eq!(f1.statics, f2.statics); + assert_eq!(f1.dynamics, f2.dynamics); + } + + #[test] + fn size_of_filters() { + fn print_sz(s: &str) { + let filter = s.parse::().expect("filter should parse"); + println!( + "size_of_val({:?})\n -> {}B", + s, + std::mem::size_of_val(&filter) + ); + } + + print_sz("info"); + + print_sz("foo=debug"); + + print_sz( + "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ + crate2=debug,crate3=trace,crate3::mod2::mod1=off", + ); + + print_sz("[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug"); + + print_sz( + "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ + crate2=debug,crate3=trace,crate3::mod2::mod1=off,[span1{foo=1}]=error,\ + [span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug", + ); + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs b/vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs new file mode 100644 index 000000000..332bf860a --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs @@ -0,0 +1,749 @@ +use crate::{ + filter::LevelFilter, + layer::{Context, Layer}, +}; +use core::{any::type_name, fmt, marker::PhantomData}; +use tracing_core::{Interest, Metadata, Subscriber}; + +/// A filter implemented by a closure or function pointer that +/// determines whether a given span or event is enabled, based on its +/// [`Metadata`]. +/// +/// This type can be used for both [per-layer filtering][plf] (using its +/// [`Filter`] implementation) and [global filtering][global] (using its +/// [`Layer`] implementation). +/// +/// See the [documentation on filtering with layers][filtering] for details. +/// +/// [`Metadata`]: tracing_core::Metadata +/// [`Filter`]: crate::layer::Filter +/// [`Layer`]: crate::layer::Layer +/// [plf]: crate::layer#per-layer-filtering +/// [global]: crate::layer#global-filtering +/// [filtering]: crate::layer#filtering-with-layers +#[derive(Clone)] +pub struct FilterFn) -> bool> { + enabled: F, + max_level_hint: Option, +} + +/// A filter implemented by a closure or function pointer that +/// determines whether a given span or event is enabled _dynamically_, +/// potentially based on the current [span context]. +/// +/// This type can be used for both [per-layer filtering][plf] (using its +/// [`Filter`] implementation) and [global filtering][global] (using its +/// [`Layer`] implementation). +/// +/// See the [documentation on filtering with layers][filtering] for details. +/// +/// [span context]: crate::layer::Context +/// [`Filter`]: crate::layer::Filter +/// [`Layer`]: crate::layer::Layer +/// [plf]: crate::layer#per-layer-filtering +/// [global]: crate::layer#global-filtering +/// [filtering]: crate::layer#filtering-with-layers +pub struct DynFilterFn< + S, + // TODO(eliza): should these just be boxed functions? + F = fn(&Metadata<'_>, &Context<'_, S>) -> bool, + R = fn(&'static Metadata<'static>) -> Interest, +> { + enabled: F, + register_callsite: Option, + max_level_hint: Option, + _s: PhantomData, +} + +// === impl FilterFn === + +/// Constructs a [`FilterFn`], from a function or closure that returns `true` if +/// a span or event should be enabled, based on its [`Metadata`]. +/// +/// The returned [`FilterFn`] can be used for both [per-layer filtering][plf] +/// (using its [`Filter`] implementation) and [global filtering][global] (using +/// its [`Layer`] implementation). +/// +/// See the [documentation on filtering with layers][filtering] for details. +/// +/// This is equivalent to calling [`FilterFn::new`]. +/// +/// [`Metadata`]: tracing_core::Metadata +/// [`Filter`]: crate::layer::Filter +/// [`Layer`]: crate::layer::Layer +/// [plf]: crate::layer#per-layer-filtering +/// [global]: crate::layer#global-filtering +/// [filtering]: crate::layer#filtering-with-layers +/// +/// # Examples +/// +/// ``` +/// use tracing_subscriber::{ +/// layer::{Layer, SubscriberExt}, +/// filter, +/// util::SubscriberInitExt, +/// }; +/// +/// let my_filter = filter::filter_fn(|metadata| { +/// // Only enable spans or events with the target "interesting_things" +/// metadata.target() == "interesting_things" +/// }); +/// +/// let my_layer = tracing_subscriber::fmt::layer(); +/// +/// tracing_subscriber::registry() +/// .with(my_layer.with_filter(my_filter)) +/// .init(); +/// +/// // This event will not be enabled. +/// tracing::warn!("something important but uninteresting happened!"); +/// +/// // This event will be enabled. +/// tracing::debug!(target: "interesting_things", "an interesting minor detail..."); +/// ``` +pub fn filter_fn(f: F) -> FilterFn +where + F: Fn(&Metadata<'_>) -> bool, +{ + FilterFn::new(f) +} + +/// Constructs a [`DynFilterFn`] from a function or closure that returns `true` +/// if a span or event should be enabled within a particular [span context][`Context`]. +/// +/// This is equivalent to calling [`DynFilterFn::new`]. +/// +/// Unlike [`filter_fn`], this function takes a closure or function pointer +/// taking the [`Metadata`] for a span or event *and* the current [`Context`]. +/// This means that a [`DynFilterFn`] can choose whether to enable spans or +/// events based on information about the _current_ span (or its parents). +/// +/// If this is *not* necessary, use [`filter_fn`] instead. +/// +/// The returned [`DynFilterFn`] can be used for both [per-layer filtering][plf] +/// (using its [`Filter`] implementation) and [global filtering][global] (using +/// its [`Layer`] implementation). +/// +/// See the [documentation on filtering with layers][filtering] for details. +/// +/// # Examples +/// +/// ``` +/// use tracing_subscriber::{ +/// layer::{Layer, SubscriberExt}, +/// filter, +/// util::SubscriberInitExt, +/// }; +/// +/// // Only enable spans or events within a span named "interesting_span". +/// let my_filter = filter::dynamic_filter_fn(|metadata, cx| { +/// // If this *is* "interesting_span", make sure to enable it. +/// if metadata.is_span() && metadata.name() == "interesting_span" { +/// return true; +/// } +/// +/// // Otherwise, are we in an interesting span? +/// if let Some(current_span) = cx.lookup_current() { +/// return current_span.name() == "interesting_span"; +/// } +/// +/// false +/// }); +/// +/// let my_layer = tracing_subscriber::fmt::layer(); +/// +/// tracing_subscriber::registry() +/// .with(my_layer.with_filter(my_filter)) +/// .init(); +/// +/// // This event will not be enabled. +/// tracing::info!("something happened"); +/// +/// tracing::info_span!("interesting_span").in_scope(|| { +/// // This event will be enabled. +/// tracing::debug!("something else happened"); +/// }); +/// ``` +/// +/// [`Filter`]: crate::layer::Filter +/// [`Layer`]: crate::layer::Layer +/// [plf]: crate::layer#per-layer-filtering +/// [global]: crate::layer#global-filtering +/// [filtering]: crate::layer#filtering-with-layers +/// [`Context`]: crate::layer::Context +/// [`Metadata`]: tracing_core::Metadata +pub fn dynamic_filter_fn(f: F) -> DynFilterFn +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + DynFilterFn::new(f) +} + +impl FilterFn +where + F: Fn(&Metadata<'_>) -> bool, +{ + /// Constructs a [`FilterFn`] from a function or closure that returns `true` + /// if a span or event should be enabled, based on its [`Metadata`]. + /// + /// If determining whether a span or event should be enabled also requires + /// information about the current span context, use [`DynFilterFn`] instead. + /// + /// See the [documentation on per-layer filtering][plf] for details on using + /// [`Filter`]s. + /// + /// [`Filter`]: crate::layer::Filter + /// [plf]: crate::layer#per-layer-filtering + /// [`Metadata`]: tracing_core::Metadata + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::FilterFn, + /// util::SubscriberInitExt, + /// }; + /// + /// let my_filter = FilterFn::new(|metadata| { + /// // Only enable spans or events with the target "interesting_things" + /// metadata.target() == "interesting_things" + /// }); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// + /// // This event will not be enabled. + /// tracing::warn!("something important but uninteresting happened!"); + /// + /// // This event will be enabled. + /// tracing::debug!(target: "interesting_things", "an interesting minor detail..."); + /// ``` + pub fn new(enabled: F) -> Self { + Self { + enabled, + max_level_hint: None, + } + } + + /// Sets the highest verbosity [`Level`] the filter function will enable. + /// + /// The value passed to this method will be returned by this `FilterFn`'s + /// [`Filter::max_level_hint`] method. + /// + /// If the provided function will not enable all levels, it is recommended + /// to call this method to configure it with the most verbose level it will + /// enable. + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::{filter_fn, LevelFilter}, + /// util::SubscriberInitExt, + /// }; + /// use tracing_core::Level; + /// + /// let my_filter = filter_fn(|metadata| { + /// // Only enable spans or events with targets starting with `my_crate` + /// // and levels at or below `INFO`. + /// metadata.level() <= &Level::INFO && metadata.target().starts_with("my_crate") + /// }) + /// // Since the filter closure will only enable the `INFO` level and + /// // below, set the max level hint + /// .with_max_level_hint(LevelFilter::INFO); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// ``` + /// + /// [`Level`]: tracing_core::Level + /// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint + pub fn with_max_level_hint(self, max_level_hint: impl Into) -> Self { + Self { + max_level_hint: Some(max_level_hint.into()), + ..self + } + } + + #[inline] + pub(in crate::filter) fn is_enabled(&self, metadata: &Metadata<'_>) -> bool { + let enabled = (self.enabled)(metadata); + debug_assert!( + !enabled || self.is_below_max_level(metadata), + "FilterFn<{}> claimed it would only enable {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + + enabled + } + + #[inline] + pub(in crate::filter) fn is_callsite_enabled( + &self, + metadata: &'static Metadata<'static>, + ) -> Interest { + // Because `self.enabled` takes a `Metadata` only (and no `Context` + // parameter), we can reasonably assume its results are cachable, and + // just return `Interest::always`/`Interest::never`. + if (self.enabled)(metadata) { + debug_assert!( + self.is_below_max_level(metadata), + "FilterFn<{}> claimed it was only interested in {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + return Interest::always(); + } + + Interest::never() + } + + fn is_below_max_level(&self, metadata: &Metadata<'_>) -> bool { + self.max_level_hint + .as_ref() + .map(|hint| metadata.level() <= hint) + .unwrap_or(true) + } +} + +impl Layer for FilterFn +where + F: Fn(&Metadata<'_>) -> bool + 'static, + S: Subscriber, +{ + fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { + self.is_enabled(metadata) + } + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option { + self.max_level_hint + } +} + +impl From for FilterFn +where + F: Fn(&Metadata<'_>) -> bool, +{ + fn from(enabled: F) -> Self { + Self::new(enabled) + } +} + +impl fmt::Debug for FilterFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterFn") + .field("enabled", &format_args!("{}", type_name::())) + .field("max_level_hint", &self.max_level_hint) + .finish() + } +} + +// === impl DynFilterFn == + +impl DynFilterFn +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + /// Constructs a [`Filter`] from a function or closure that returns `true` + /// if a span or event should be enabled in the current [span + /// context][`Context`]. + /// + /// Unlike [`FilterFn`], a `DynFilterFn` is constructed from a closure or + /// function pointer that takes both the [`Metadata`] for a span or event + /// *and* the current [`Context`]. This means that a [`DynFilterFn`] can + /// choose whether to enable spans or events based on information about the + /// _current_ span (or its parents). + /// + /// If this is *not* necessary, use [`FilterFn`] instead. + /// + /// See the [documentation on per-layer filtering][plf] for details on using + /// [`Filter`]s. + /// + /// [`Filter`]: crate::layer::Filter + /// [plf]: crate::layer#per-layer-filtering + /// [`Context`]: crate::layer::Context + /// [`Metadata`]: tracing_core::Metadata + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::DynFilterFn, + /// util::SubscriberInitExt, + /// }; + /// + /// // Only enable spans or events within a span named "interesting_span". + /// let my_filter = DynFilterFn::new(|metadata, cx| { + /// // If this *is* "interesting_span", make sure to enable it. + /// if metadata.is_span() && metadata.name() == "interesting_span" { + /// return true; + /// } + /// + /// // Otherwise, are we in an interesting span? + /// if let Some(current_span) = cx.lookup_current() { + /// return current_span.name() == "interesting_span"; + /// } + /// + /// false + /// }); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// + /// // This event will not be enabled. + /// tracing::info!("something happened"); + /// + /// tracing::info_span!("interesting_span").in_scope(|| { + /// // This event will be enabled. + /// tracing::debug!("something else happened"); + /// }); + /// ``` + pub fn new(enabled: F) -> Self { + Self { + enabled, + register_callsite: None, + max_level_hint: None, + _s: PhantomData, + } + } +} + +impl DynFilterFn +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + /// Sets the highest verbosity [`Level`] the filter function will enable. + /// + /// The value passed to this method will be returned by this `DynFilterFn`'s + /// [`Filter::max_level_hint`] method. + /// + /// If the provided function will not enable all levels, it is recommended + /// to call this method to configure it with the most verbose level it will + /// enable. + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::{DynFilterFn, LevelFilter}, + /// util::SubscriberInitExt, + /// }; + /// use tracing_core::Level; + /// + /// // Only enable spans or events with levels at or below `INFO`, if + /// // we are inside a span called "interesting_span". + /// let my_filter = DynFilterFn::new(|metadata, cx| { + /// // If the level is greater than INFO, disable it. + /// if metadata.level() > &Level::INFO { + /// return false; + /// } + /// + /// // If any span in the current scope is named "interesting_span", + /// // enable this span or event. + /// for span in cx.lookup_current().iter().flat_map(|span| span.scope()) { + /// if span.name() == "interesting_span" { + /// return true; + /// } + /// } + /// + /// // Otherwise, disable it. + /// false + /// }) + /// // Since the filter closure will only enable the `INFO` level and + /// // below, set the max level hint + /// .with_max_level_hint(LevelFilter::INFO); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// ``` + /// + /// [`Level`]: tracing_core::Level + /// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint + pub fn with_max_level_hint(self, max_level_hint: impl Into) -> Self { + Self { + max_level_hint: Some(max_level_hint.into()), + ..self + } + } + + /// Adds a function for filtering callsites to this filter. + /// + /// When this filter's [`Filter::callsite_enabled`][cse] method is called, + /// the provided function will be used rather than the default. + /// + /// By default, `DynFilterFn` assumes that, because the filter _may_ depend + /// dynamically on the current [span context], its result should never be + /// cached. However, some filtering strategies may require dynamic information + /// from the current span context in *some* cases, but are able to make + /// static filtering decisions from [`Metadata`] alone in others. + /// + /// For example, consider the filter given in the example for + /// [`DynFilterFn::new`]. That filter enables all spans named + /// "interesting_span", and any events and spans that occur inside of an + /// interesting span. Since the span's name is part of its static + /// [`Metadata`], the "interesting_span" can be enabled in + /// [`callsite_enabled`][cse]: + /// + /// ``` + /// use tracing_subscriber::{ + /// layer::{Layer, SubscriberExt}, + /// filter::DynFilterFn, + /// util::SubscriberInitExt, + /// }; + /// use tracing_core::subscriber::Interest; + /// + /// // Only enable spans or events within a span named "interesting_span". + /// let my_filter = DynFilterFn::new(|metadata, cx| { + /// // If this *is* "interesting_span", make sure to enable it. + /// if metadata.is_span() && metadata.name() == "interesting_span" { + /// return true; + /// } + /// + /// // Otherwise, are we in an interesting span? + /// if let Some(current_span) = cx.lookup_current() { + /// return current_span.name() == "interesting_span"; + /// } + /// + /// false + /// }).with_callsite_filter(|metadata| { + /// // If this is an "interesting_span", we know we will always + /// // enable it. + /// if metadata.is_span() && metadata.name() == "interesting_span" { + /// return Interest::always(); + /// } + /// + /// // Otherwise, it depends on whether or not we're in an interesting + /// // span. You'll have to ask us again for each span/event! + /// Interest::sometimes() + /// }); + /// + /// let my_layer = tracing_subscriber::fmt::layer(); + /// + /// tracing_subscriber::registry() + /// .with(my_layer.with_filter(my_filter)) + /// .init(); + /// ``` + /// + /// [cse]: crate::layer::Filter::callsite_enabled + /// [`enabled`]: crate::layer::Filter::enabled + /// [`Metadata`]: tracing_core::Metadata + /// [span context]: crate::layer::Context + pub fn with_callsite_filter(self, callsite_enabled: R2) -> DynFilterFn + where + R2: Fn(&'static Metadata<'static>) -> Interest, + { + let register_callsite = Some(callsite_enabled); + let DynFilterFn { + enabled, + max_level_hint, + _s, + .. + } = self; + DynFilterFn { + enabled, + register_callsite, + max_level_hint, + _s, + } + } + + fn default_callsite_enabled(&self, metadata: &Metadata<'_>) -> Interest { + // If it's below the configured max level, assume that `enabled` will + // never enable it... + if !is_below_max_level(&self.max_level_hint, metadata) { + debug_assert!( + !(self.enabled)(metadata, &Context::none()), + "DynFilterFn<{}> claimed it would only enable {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + return Interest::never(); + } + + // Otherwise, since this `enabled` function is dynamic and depends on + // the current context, we don't know whether this span or event will be + // enabled or not. Ask again every time it's recorded! + Interest::sometimes() + } +} + +impl DynFilterFn +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, + R: Fn(&'static Metadata<'static>) -> Interest, +{ + #[inline] + fn is_enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + let enabled = (self.enabled)(metadata, cx); + debug_assert!( + !enabled || is_below_max_level(&self.max_level_hint, metadata), + "DynFilterFn<{}> claimed it would only enable {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + + enabled + } + + #[inline] + fn is_callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + let interest = self + .register_callsite + .as_ref() + .map(|callsite_enabled| callsite_enabled(metadata)) + .unwrap_or_else(|| self.default_callsite_enabled(metadata)); + debug_assert!( + interest.is_never() || is_below_max_level(&self.max_level_hint, metadata), + "DynFilterFn<{}, {}> claimed it was only interested in {:?} and below, \ + but it enabled metadata with the {:?} level\nmetadata={:#?}", + type_name::(), + type_name::(), + self.max_level_hint.unwrap(), + metadata.level(), + metadata, + ); + + interest + } +} + +impl Layer for DynFilterFn +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool + 'static, + R: Fn(&'static Metadata<'static>) -> Interest + 'static, + S: Subscriber, +{ + fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool { + self.is_enabled(metadata, &cx) + } + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option { + self.max_level_hint + } +} + +impl fmt::Debug for DynFilterFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("DynFilterFn"); + s.field("enabled", &format_args!("{}", type_name::())); + if self.register_callsite.is_some() { + s.field( + "register_callsite", + &format_args!("Some({})", type_name::()), + ); + } else { + s.field("register_callsite", &format_args!("None")); + } + + s.field("max_level_hint", &self.max_level_hint).finish() + } +} + +impl Clone for DynFilterFn +where + F: Clone, + R: Clone, +{ + fn clone(&self) -> Self { + Self { + enabled: self.enabled.clone(), + register_callsite: self.register_callsite.clone(), + max_level_hint: self.max_level_hint, + _s: PhantomData, + } + } +} + +impl From for DynFilterFn +where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, +{ + fn from(f: F) -> Self { + Self::new(f) + } +} + +// === PLF impls === + +feature! { + #![all(feature = "registry", feature = "std")] + use crate::layer::Filter; + + impl Filter for FilterFn + where + F: Fn(&Metadata<'_>) -> bool, + { + fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, S>) -> bool { + self.is_enabled(metadata) + } + + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option { + self.max_level_hint + } + } + + impl Filter for DynFilterFn + where + F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, + R: Fn(&'static Metadata<'static>) -> Interest, + { + fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + self.is_enabled(metadata, cx) + } + + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option { + self.max_level_hint + } + } +} + +fn is_below_max_level(hint: &Option, metadata: &Metadata<'_>) -> bool { + hint.as_ref() + .map(|hint| metadata.level() <= hint) + .unwrap_or(true) +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs b/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs new file mode 100644 index 000000000..e77fd3751 --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs @@ -0,0 +1,830 @@ +//! ## Per-Layer Filtering +//! +//! Per-layer filters permit individual `Layer`s to have their own filter +//! configurations without interfering with other `Layer`s. +//! +//! This module is not public; the public APIs defined in this module are +//! re-exported in the top-level `filter` module. Therefore, this documentation +//! primarily concerns the internal implementation details. For the user-facing +//! public API documentation, see the individual public types in this module, as +//! well as the, see the `Layer` trait documentation's [per-layer filtering +//! section]][1]. +//! +//! ## How does per-layer filtering work? +//! +//! As described in the API documentation, the [`Filter`] trait defines a +//! filtering strategy for a per-layer filter. We expect there will be a variety +//! of implementations of [`Filter`], both in `tracing-subscriber` and in user +//! code. +//! +//! To actually *use* a [`Filter`] implementation, it is combined with a +//! [`Layer`] by the [`Filtered`] struct defined in this module. [`Filtered`] +//! implements [`Layer`] by calling into the wrapped [`Layer`], or not, based on +//! the filtering strategy. While there will be a variety of types that implement +//! [`Filter`], all actual *uses* of per-layer filtering will occur through the +//! [`Filtered`] struct. Therefore, most of the implementation details live +//! there. +//! +//! [1]: crate::layer#per-layer-filtering +//! [`Filter`]: crate::layer::Filter +use crate::{ + filter::LevelFilter, + layer::{self, Context, Layer}, + registry, +}; +use std::{ + any::TypeId, + cell::{Cell, RefCell}, + fmt, + marker::PhantomData, + sync::Arc, + thread_local, +}; +use tracing_core::{ + span, + subscriber::{Interest, Subscriber}, + Event, Metadata, +}; + +/// A [`Layer`] that wraps an inner [`Layer`] and adds a [`Filter`] which +/// controls what spans and events are enabled for that layer. +/// +/// This is returned by the [`Layer::with_filter`] method. See the +/// [documentation on per-layer filtering][plf] for details. +/// +/// [`Filter`]: crate::layer::Filter +/// [plf]: crate::layer#per-layer-filtering +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +#[derive(Clone)] +pub struct Filtered { + filter: F, + layer: L, + id: MagicPlfDowncastMarker, + _s: PhantomData, +} + +/// Uniquely identifies an individual [`Filter`] instance in the context of +/// a [`Subscriber`]. +/// +/// When adding a [`Filtered`] [`Layer`] to a [`Subscriber`], the [`Subscriber`] +/// generates a `FilterId` for that [`Filtered`] layer. The [`Filtered`] layer +/// will then use the generated ID to query whether a particular span was +/// previously enabled by that layer's [`Filter`]. +/// +/// **Note**: Currently, the [`Registry`] type provided by this crate is the +/// **only** [`Subscriber`] implementation capable of participating in per-layer +/// filtering. Therefore, the `FilterId` type cannot currently be constructed by +/// code outside of `tracing-subscriber`. In the future, new APIs will be added to `tracing-subscriber` to +/// allow non-Registry [`Subscriber`]s to also participate in per-layer +/// filtering. When those APIs are added, subscribers will be responsible +/// for generating and assigning `FilterId`s. +/// +/// [`Filter`]: crate::layer::Filter +/// [`Subscriber`]: tracing_core::Subscriber +/// [`Layer`]: crate::layer::Layer +/// [`Registry`]: crate::registry::Registry +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +#[derive(Copy, Clone)] +pub struct FilterId(u64); + +/// A bitmap tracking which [`FilterId`]s have enabled a given span or +/// event. +/// +/// This is currently a private type that's used exclusively by the +/// [`Registry`]. However, in the future, this may become a public API, in order +/// to allow user subscribers to host [`Filter`]s. +/// +/// [`Registry`]: crate::Registry +/// [`Filter`]: crate::layer::Filter +#[derive(Default, Copy, Clone, Eq, PartialEq)] +pub(crate) struct FilterMap { + bits: u64, +} + +/// The current state of `enabled` calls to per-layer filters on this +/// thread. +/// +/// When `Filtered::enabled` is called, the filter will set the bit +/// corresponding to its ID if the filter will disable the event/span being +/// filtered. When the event or span is recorded, the per-layer filter will +/// check its bit to determine if it disabled that event or span, and skip +/// forwarding the event or span to the inner layer if the bit is set. Once +/// a span or event has been skipped by a per-layer filter, it unsets its +/// bit, so that the `FilterMap` has been cleared for the next set of +/// `enabled` calls. +/// +/// FilterState is also read by the `Registry`, for two reasons: +/// +/// 1. When filtering a span, the Registry must store the `FilterMap` +/// generated by `Filtered::enabled` calls for that span as part of the +/// span's per-span data. This allows `Filtered` layers to determine +/// whether they had previously disabled a given span, and avoid showing it +/// to the wrapped layer if it was disabled. +/// +/// This allows `Filtered` layers to also filter out the spans they +/// disable from span traversals (such as iterating over parents, etc). +/// 2. If all the bits are set, then every per-layer filter has decided it +/// doesn't want to enable that span or event. In that case, the +/// `Registry`'s `enabled` method will return `false`, so that +/// recording a span or event can be skipped entirely. +#[derive(Debug)] +pub(crate) struct FilterState { + enabled: Cell, + // TODO(eliza): `Interest`s should _probably_ be `Copy`. The only reason + // they're not is our Obsessive Commitment to Forwards-Compatibility. If + // this changes in tracing-core`, we can make this a `Cell` rather than + // `RefCell`... + interest: RefCell>, + + #[cfg(debug_assertions)] + counters: DebugCounters, +} + +/// Extra counters added to `FilterState` used only to make debug assertions. +#[cfg(debug_assertions)] +#[derive(Debug, Default)] +struct DebugCounters { + /// How many per-layer filters have participated in the current `enabled` + /// call? + in_filter_pass: Cell, + + /// How many per-layer filters have participated in the current `register_callsite` + /// call? + in_interest_pass: Cell, +} + +thread_local! { + pub(crate) static FILTERING: FilterState = FilterState::new(); +} + +// === impl Filter === +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl layer::Filter for LevelFilter { + fn enabled(&self, meta: &Metadata<'_>, _: &Context<'_, S>) -> bool { + meta.level() <= self + } + + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + if meta.level() <= self { + Interest::always() + } else { + Interest::never() + } + } + + fn max_level_hint(&self) -> Option { + Some(*self) + } +} + +impl layer::Filter for Arc + 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) + } + + #[inline] + fn max_level_hint(&self) -> Option { + (**self).max_level_hint() + } +} + +impl layer::Filter for Box + 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) + } + + #[inline] + fn max_level_hint(&self) -> Option { + (**self).max_level_hint() + } +} + +// === impl Filtered === + +impl Filtered { + /// Wraps the provided [`Layer`] so that it is filtered by the given + /// [`Filter`]. + /// + /// This is equivalent to calling the [`Layer::with_filter`] method. + /// + /// See the [documentation on per-layer filtering][plf] for details. + /// + /// [`Filter`]: crate::layer::Filter + /// [plf]: crate::layer#per-layer-filtering + pub fn new(layer: L, filter: F) -> Self { + Self { + layer, + filter, + id: MagicPlfDowncastMarker(FilterId::disabled()), + _s: PhantomData, + } + } + + #[inline(always)] + fn id(&self) -> FilterId { + debug_assert!( + !self.id.0.is_disabled(), + "a `Filtered` layer was used, but it had no `FilterId`; \ + was it registered with the subscriber?" + ); + self.id.0 + } + + fn did_enable(&self, f: impl FnOnce()) { + FILTERING.with(|filtering| filtering.did_enable(self.id(), f)) + } +} + +impl Layer for Filtered +where + S: Subscriber + for<'span> registry::LookupSpan<'span> + 'static, + F: layer::Filter + 'static, + L: Layer, +{ + fn on_layer(&mut self, subscriber: &mut S) { + self.id = MagicPlfDowncastMarker(subscriber.register_filter()); + self.layer.on_layer(subscriber); + } + + // TODO(eliza): can we figure out a nice way to make the `Filtered` layer + // not call `is_enabled_for` in hooks that the inner layer doesn't actually + // have real implementations of? probably not... + // + // it would be cool if there was some wild rust reflection way of checking + // if a trait impl has the default impl of a trait method or not, but that's + // almsot certainly impossible...right? + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + let interest = self.filter.callsite_enabled(metadata); + + // If the filter didn't disable the callsite, allow the inner layer to + // register it — since `register_callsite` is also used for purposes + // such as reserving/caching per-callsite data, we want the inner layer + // to be able to perform any other registration steps. However, we'll + // ignore its `Interest`. + if !interest.is_never() { + self.layer.register_callsite(metadata); + } + + // Add our `Interest` to the current sum of per-layer filter `Interest`s + // for this callsite. + FILTERING.with(|filtering| filtering.add_interest(interest)); + + // don't short circuit! if the stack consists entirely of `Layer`s with + // per-layer filters, the `Registry` will return the actual `Interest` + // value that's the sum of all the `register_callsite` calls to those + // per-layer filters. if we returned an actual `never` interest here, a + // `Layered` layer would short-circuit and not allow any `Filtered` + // layers below us if _they_ are interested in the callsite. + Interest::always() + } + + fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool { + let cx = cx.with_filter(self.id()); + let enabled = self.filter.enabled(metadata, &cx); + FILTERING.with(|filtering| filtering.set(self.id(), enabled)); + + if enabled { + // If the filter enabled this metadata, ask the wrapped layer if + // _it_ wants it --- it might have a global filter. + self.layer.enabled(metadata, cx) + } else { + // Otherwise, return `true`. The _per-layer_ filter disabled this + // metadata, but returning `false` in `Layer::enabled` will + // short-circuit and globally disable the span or event. This is + // *not* what we want for per-layer filters, as other layers may + // still want this event. Returning `true` here means we'll continue + // asking the next layer in the stack. + // + // Once all per-layer filters have been evaluated, the `Registry` + // at the root of the stack will return `false` from its `enabled` + // method if *every* per-layer filter disabled this metadata. + // Otherwise, the individual per-layer filters will skip the next + // `new_span` or `on_event` call for their layer if *they* disabled + // the span or event, but it was not globally disabled. + true + } + } + + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, cx: Context<'_, S>) { + self.did_enable(|| { + self.layer.on_new_span(attrs, id, cx.with_filter(self.id())); + }) + } + + #[doc(hidden)] + fn max_level_hint(&self) -> Option { + self.filter.max_level_hint() + } + + fn on_record(&self, span: &span::Id, values: &span::Record<'_>, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(span, self.id()) { + self.layer.on_record(span, values, cx) + } + } + + fn on_follows_from(&self, span: &span::Id, follows: &span::Id, cx: Context<'_, S>) { + // only call `on_follows_from` if both spans are enabled by us + if cx.is_enabled_for(span, self.id()) && cx.is_enabled_for(follows, self.id()) { + self.layer + .on_follows_from(span, follows, cx.with_filter(self.id())) + } + } + + fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) { + self.did_enable(|| { + self.layer.on_event(event, cx.with_filter(self.id())); + }) + } + + fn on_enter(&self, id: &span::Id, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(id, self.id()) { + self.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) + } + } + + 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) + } + } + + // XXX(eliza): the existence of this method still makes me sad... + fn on_id_change(&self, old: &span::Id, new: &span::Id, cx: Context<'_, S>) { + if let Some(cx) = cx.if_enabled_for(old, self.id()) { + self.layer.on_id_change(old, new, cx) + } + } + + #[doc(hidden)] + #[inline] + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { + match id { + id if id == TypeId::of::() => Some(self as *const _ as *const ()), + id if id == TypeId::of::() => Some(&self.layer as *const _ as *const ()), + id if id == TypeId::of::() => Some(&self.filter as *const _ as *const ()), + id if id == TypeId::of::() => { + Some(&self.id as *const _ as *const ()) + } + _ => self.layer.downcast_raw(id), + } + } +} + +impl fmt::Debug for Filtered +where + F: fmt::Debug, + L: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Filtered") + .field("filter", &self.filter) + .field("layer", &self.layer) + .field("id", &self.id) + .finish() + } +} + +// === impl FilterId === + +impl FilterId { + const fn disabled() -> Self { + Self(std::u64::MAX) + } + + /// Returns a `FilterId` that will consider _all_ spans enabled. + pub(crate) const fn none() -> Self { + Self(0) + } + + pub(crate) fn new(id: u8) -> Self { + assert!(id < 64, "filter IDs may not be greater than 64"); + Self(1 << id as usize) + } + + /// Combines two `FilterId`s, returning a new `FilterId` that will match a + /// [`FilterMap`] where the span was disabled by _either_ this `FilterId` + /// *or* the combined `FilterId`. + /// + /// This method is called by [`Context`]s when adding the `FilterId` of a + /// [`Filtered`] layer to the context. + /// + /// This is necessary for cases where we have a tree of nested [`Filtered`] + /// layers, like this: + /// + /// ```text + /// Filtered { + /// filter1, + /// Layered { + /// layer1, + /// Filtered { + /// filter2, + /// layer2, + /// }, + /// } + /// ``` + /// + /// We want `layer2` to be affected by both `filter1` _and_ `filter2`. + /// Without combining `FilterId`s, this works fine when filtering + /// `on_event`/`new_span`, because the outer `Filtered` layer (`filter1`) + /// won't call the inner layer's `on_event` or `new_span` callbacks if it + /// disabled the event/span. + /// + /// However, it _doesn't_ work when filtering span lookups and traversals + /// (e.g. `scope`). This is because the [`Context`] passed to `layer2` + /// would set its filter ID to the filter ID of `filter2`, and would skip + /// spans that were disabled by `filter2`. However, what if a span was + /// disabled by `filter1`? We wouldn't see it in `new_span`, but we _would_ + /// see it in lookups and traversals...which we don't want. + /// + /// When a [`Filtered`] layer adds its ID to a [`Context`], it _combines_ it + /// with any previous filter ID that the context had, rather than replacing + /// it. That way, `layer2`'s context will check if a span was disabled by + /// `filter1` _or_ `filter2`. The way we do this, instead of representing + /// `FilterId`s as a number number that we shift a 1 over by to get a mask, + /// we just store the actual mask,so we can combine them with a bitwise-OR. + /// + /// For example, if we consider the following case (pretending that the + /// masks are 8 bits instead of 64 just so i don't have to write out a bunch + /// of extra zeroes): + /// + /// - `filter1` has the filter id 1 (`0b0000_0001`) + /// - `filter2` has the filter id 2 (`0b0000_0010`) + /// + /// A span that gets disabled by filter 1 would have the [`FilterMap`] with + /// bits `0b0000_0001`. + /// + /// If the `FilterId` was internally represented as `(bits to shift + 1), + /// when `layer2`'s [`Context`] checked if it enabled the span, it would + /// make the mask `0b0000_0010` (`1 << 1`). That bit would not be set in the + /// [`FilterMap`], so it would see that it _didn't_ disable the span. Which + /// is *true*, it just doesn't reflect the tree-like shape of the actual + /// subscriber. + /// + /// By having the IDs be masks instead of shifts, though, when the + /// [`Filtered`] with `filter2` gets the [`Context`] with `filter1`'s filter ID, + /// instead of replacing it, it ors them together: + /// + /// ```ignore + /// 0b0000_0001 | 0b0000_0010 == 0b0000_0011; + /// ``` + /// + /// We then test if the span was disabled by seeing if _any_ bits in the + /// mask are `1`: + /// + /// ```ignore + /// filtermap & mask != 0; + /// 0b0000_0001 & 0b0000_0011 != 0; + /// 0b0000_0001 != 0; + /// true; + /// ``` + /// + /// [`Context`]: crate::layer::Context + pub(crate) fn and(self, FilterId(other): Self) -> Self { + // If this mask is disabled, just return the other --- otherwise, we + // would always see that every span is disabled. + if self.0 == Self::disabled().0 { + return Self(other); + } + + Self(self.0 | other) + } + + fn is_disabled(self) -> bool { + self.0 == Self::disabled().0 + } +} + +impl fmt::Debug for FilterId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // don't print a giant set of the numbers 0..63 if the filter ID is disabled. + if self.0 == Self::disabled().0 { + return f + .debug_tuple("FilterId") + .field(&format_args!("DISABLED")) + .finish(); + } + + if f.alternate() { + f.debug_struct("FilterId") + .field("ids", &format_args!("{:?}", FmtBitset(self.0))) + .field("bits", &format_args!("{:b}", self.0)) + .finish() + } else { + f.debug_tuple("FilterId").field(&FmtBitset(self.0)).finish() + } + } +} + +impl fmt::Binary for FilterId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("FilterId") + .field(&format_args!("{:b}", self.0)) + .finish() + } +} + +// === impl FilterMap === + +impl FilterMap { + pub(crate) fn set(self, FilterId(mask): FilterId, enabled: bool) -> Self { + if mask == std::u64::MAX { + return self; + } + + if enabled { + Self { + bits: self.bits & (!mask), + } + } else { + Self { + bits: self.bits | mask, + } + } + } + + #[inline] + pub(crate) fn is_enabled(self, FilterId(mask): FilterId) -> bool { + self.bits & mask == 0 + } + + #[inline] + pub(crate) fn any_enabled(self) -> bool { + self.bits != std::u64::MAX + } +} + +impl fmt::Debug for FilterMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let alt = f.alternate(); + let mut s = f.debug_struct("FilterMap"); + s.field("disabled_by", &format_args!("{:?}", &FmtBitset(self.bits))); + + if alt { + s.field("bits", &format_args!("{:b}", self.bits)); + } + + s.finish() + } +} + +impl fmt::Binary for FilterMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterMap") + .field("bits", &format_args!("{:b}", self.bits)) + .finish() + } +} + +// === impl FilterState === + +impl FilterState { + fn new() -> Self { + Self { + enabled: Cell::new(FilterMap::default()), + interest: RefCell::new(None), + + #[cfg(debug_assertions)] + counters: DebugCounters::default(), + } + } + + fn set(&self, filter: FilterId, enabled: bool) { + #[cfg(debug_assertions)] + { + let in_current_pass = self.counters.in_filter_pass.get(); + if in_current_pass == 0 { + debug_assert_eq!(self.enabled.get(), FilterMap::default()); + } + self.counters.in_filter_pass.set(in_current_pass + 1); + debug_assert_eq!( + self.counters.in_interest_pass.get(), + 0, + "if we are in or starting a filter pass, we must not be in an interest pass." + ) + } + + self.enabled.set(self.enabled.get().set(filter, enabled)) + } + + fn add_interest(&self, interest: Interest) { + let mut curr_interest = self.interest.borrow_mut(); + + #[cfg(debug_assertions)] + { + let in_current_pass = self.counters.in_interest_pass.get(); + if in_current_pass == 0 { + debug_assert!(curr_interest.is_none()); + } + self.counters.in_interest_pass.set(in_current_pass + 1); + } + + if let Some(curr_interest) = curr_interest.as_mut() { + if (curr_interest.is_always() && !interest.is_always()) + || (curr_interest.is_never() && !interest.is_never()) + { + *curr_interest = Interest::sometimes(); + } + // If the two interests are the same, do nothing. If the current + // interest is `sometimes`, stay sometimes. + } else { + *curr_interest = Some(interest); + } + } + + pub(crate) fn event_enabled() -> bool { + FILTERING + .try_with(|this| { + let enabled = this.enabled.get().any_enabled(); + #[cfg(debug_assertions)] + { + if this.counters.in_filter_pass.get() == 0 { + debug_assert_eq!(this.enabled.get(), FilterMap::default()); + } + + // Nothing enabled this event, we won't tick back down the + // counter in `did_enable`. Reset it. + if !enabled { + this.counters.in_filter_pass.set(0); + } + } + enabled + }) + .unwrap_or(true) + } + + /// Executes a closure if the filter with the provided ID did not disable + /// the current span/event. + /// + /// This is used to implement the `on_event` and `new_span` methods for + /// `Filtered`. + fn did_enable(&self, filter: FilterId, f: impl FnOnce()) { + let map = self.enabled.get(); + if map.is_enabled(filter) { + // If the filter didn't disable the current span/event, run the + // callback. + f(); + } else { + // Otherwise, if this filter _did_ disable the span or event + // currently being processed, clear its bit from this thread's + // `FilterState`. The bit has already been "consumed" by skipping + // this callback, and we need to ensure that the `FilterMap` for + // this thread is reset when the *next* `enabled` call occurs. + self.enabled.set(map.set(filter, true)); + } + #[cfg(debug_assertions)] + { + let in_current_pass = self.counters.in_filter_pass.get(); + if in_current_pass <= 1 { + debug_assert_eq!(self.enabled.get(), FilterMap::default()); + } + self.counters + .in_filter_pass + .set(in_current_pass.saturating_sub(1)); + debug_assert_eq!( + self.counters.in_interest_pass.get(), + 0, + "if we are in a filter pass, we must not be in an interest pass." + ) + } + } + + /// Clears the current in-progress filter state. + /// + /// This resets the [`FilterMap`] and current [`Interest`] as well as + /// clearing the debug counters. + pub(crate) fn clear_enabled() { + // Drop the `Result` returned by `try_with` --- if we are in the middle + // a panic and the thread-local has been torn down, that's fine, just + // ignore it ratehr than panicking. + let _ = FILTERING.try_with(|filtering| { + filtering.enabled.set(FilterMap::default()); + + #[cfg(debug_assertions)] + filtering.counters.in_filter_pass.set(0); + }); + } + + pub(crate) fn take_interest() -> Option { + FILTERING + .try_with(|filtering| { + #[cfg(debug_assertions)] + { + if filtering.counters.in_interest_pass.get() == 0 { + debug_assert!(filtering.interest.try_borrow().ok()?.is_none()); + } + filtering.counters.in_interest_pass.set(0); + } + filtering.interest.try_borrow_mut().ok()?.take() + }) + .ok()? + } + + pub(crate) fn filter_map(&self) -> FilterMap { + let map = self.enabled.get(); + #[cfg(debug_assertions)] + { + if self.counters.in_filter_pass.get() == 0 { + debug_assert_eq!(map, FilterMap::default()); + } + } + + map + } +} +/// This is a horrible and bad abuse of the downcasting system to expose +/// *internally* whether a layer has per-layer filtering, within +/// `tracing-subscriber`, without exposing a public API for it. +/// +/// If a `Layer` has per-layer filtering, it will downcast to a +/// `MagicPlfDowncastMarker`. Since layers which contain other layers permit +/// downcasting to recurse to their children, this will do the Right Thing with +/// layers like Reload, Option, etc. +/// +/// Why is this a wrapper around the `FilterId`, you may ask? Because +/// downcasting works by returning a pointer, and we don't want to risk +/// introducing UB by constructing pointers that _don't_ point to a valid +/// instance of the type they claim to be. In this case, we don't _intend_ for +/// this pointer to be dereferenced, so it would actually be fine to return one +/// that isn't a valid pointer...but we can't guarantee that the caller won't +/// (accidentally) dereference it, so it's better to be safe than sorry. We +/// could, alternatively, add an additional field to the type that's used only +/// for returning pointers to as as part of the evil downcasting hack, but I +/// thought it was nicer to just add a `repr(transparent)` wrapper to the +/// existing `FilterId` field, since it won't make the struct any bigger. +/// +/// Don't worry, this isn't on the test. :) +#[derive(Clone, Copy)] +#[repr(transparent)] +struct MagicPlfDowncastMarker(FilterId); +impl fmt::Debug for MagicPlfDowncastMarker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Just pretend that `MagicPlfDowncastMarker` doesn't exist for + // `fmt::Debug` purposes...if no one *sees* it in their `Debug` output, + // they don't have to know I thought this code would be a good idea. + fmt::Debug::fmt(&self.0, f) + } +} + +pub(crate) fn is_plf_downcast_marker(type_id: TypeId) -> bool { + type_id == TypeId::of::() +} + +/// Does a type implementing `Subscriber` contain any per-layer filters? +pub(crate) fn subscriber_has_plf(subscriber: &S) -> bool +where + S: Subscriber, +{ + (subscriber as &dyn Subscriber).is::() +} + +/// Does a type implementing `Layer` contain any per-layer filters? +pub(crate) fn layer_has_plf(layer: &L) -> bool +where + L: Layer, + S: Subscriber, +{ + unsafe { + // Safety: we're not actually *doing* anything with this pointer --- we + // only care about the `Option`, which we're turning into a `bool`. So + // even if the layer decides to be evil and give us some kind of invalid + // pointer, we don't ever dereference it, so this is always safe. + layer.downcast_raw(TypeId::of::()) + } + .is_some() +} + +struct FmtBitset(u64); + +impl fmt::Debug for FmtBitset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut set = f.debug_set(); + for bit in 0..64 { + // if the `bit`-th bit is set, add it to the debug set + if self.0 & (1 << bit) != 0 { + set.entry(&bit); + } + } + set.finish() + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/level.rs b/vendor/tracing-subscriber-0.3.3/src/filter/level.rs new file mode 100644 index 000000000..0fa601260 --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/level.rs @@ -0,0 +1,27 @@ +use tracing_core::{ + subscriber::{Interest, Subscriber}, + Metadata, +}; + +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use tracing_core::metadata::{LevelFilter, ParseLevelFilterError as ParseError}; + +// === impl LevelFilter === + +impl crate::Layer for LevelFilter { + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + if self >= metadata.level() { + Interest::always() + } else { + Interest::never() + } + } + + fn enabled(&self, metadata: &Metadata<'_>, _: crate::layer::Context<'_, S>) -> bool { + self >= metadata.level() + } + + fn max_level_hint(&self) -> Option { + Some(*self) + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/mod.rs b/vendor/tracing-subscriber-0.3.3/src/filter/mod.rs new file mode 100644 index 000000000..000a27195 --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/mod.rs @@ -0,0 +1,66 @@ +//! [`Layer`]s that control which spans and events are enabled by the wrapped +//! subscriber. +//! +//! This module contains a number of types that provide implementations of +//! various strategies for filtering which spans and events are enabled. For +//! details on filtering spans and events using [`Layer`]s, see the +//! [`layer` module's documentation]. +//! +//! [`layer` module's documentation]: crate::layer#filtering-with-layers +//! [`Layer`]: crate::layer +mod filter_fn; + +feature! { + #![all(feature = "env-filter", feature = "std")] + mod env; + pub use self::env::*; +} + +feature! { + #![all(feature = "registry", feature = "std")] + mod layer_filters; + pub use self::layer_filters::*; +} + +mod level; + +pub use self::filter_fn::*; +pub use self::level::{LevelFilter, ParseError as LevelParseError}; + +#[cfg(not(all(feature = "registry", feature = "std")))] +pub(crate) use self::has_plf_stubs::*; + +feature! { + #![any(feature = "std", feature = "alloc")] + pub mod targets; + pub use self::targets::Targets; + + mod directive; + pub use self::directive::ParseError; +} + +/// Stub implementations of the per-layer-fitler detection functions for when the +/// `registry` feature is disabled. +#[cfg(not(all(feature = "registry", feature = "std")))] +mod has_plf_stubs { + pub(crate) fn is_plf_downcast_marker(_: core::any::TypeId) -> bool { + false + } + + /// Does a type implementing `Subscriber` contain any per-layer filters? + pub(crate) fn subscriber_has_plf(_: &S) -> bool + where + S: tracing_core::Subscriber, + { + false + } + + /// Does a type implementing `Layer` contain any per-layer filters? + pub(crate) fn layer_has_plf(_: &L) -> bool + where + L: crate::Layer, + S: tracing_core::Subscriber, + { + false + } +} diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/targets.rs b/vendor/tracing-subscriber-0.3.3/src/filter/targets.rs new file mode 100644 index 000000000..e0c7fcf82 --- /dev/null +++ b/vendor/tracing-subscriber-0.3.3/src/filter/targets.rs @@ -0,0 +1,681 @@ +//! A [filter] that enables or disables spans and events based on their [target] and [level]. +//! +//! See [`Targets`] for details. +//! +//! [target]: tracing_core::Metadata::target +//! [level]: tracing_core::Level +//! [filter]: crate::layer#filtering-with-layers + +use crate::{ + filter::{ + directive::{DirectiveSet, ParseError, StaticDirective}, + LevelFilter, + }, + layer, +}; +#[cfg(not(feature = "std"))] +use alloc::string::String; +use core::{ + iter::{Extend, FilterMap, FromIterator}, + slice, + str::FromStr, +}; +use tracing_core::{Interest, Metadata, Subscriber}; + +/// A filter that enables or disables spans and events based on their [target] +/// and [level]. +/// +/// Targets are typically equal to the Rust module path of the code where the +/// span or event was recorded, although they may be overridden. +/// +/// This type can be used for both [per-layer filtering][plf] (using its +/// [`Filter`] implementation) and [global filtering][global] (using its +/// [`Layer`] implementation). +/// +/// See the [documentation on filtering with layers][filtering] for details. +/// +/// # Filtering With `Targets` +/// +/// A `Targets` filter consists of one or more [target] prefixes, paired with +/// [`LevelFilter`]s. If a span or event's [target] begins with one of those +/// prefixes, and its [level] is at or below the [`LevelFilter`] enabled for +/// that prefix, then the span or event will be enabled. +/// +/// This is similar to the behavior implemented by the [`env_logger` crate] in +/// the `log` ecosystem. +/// +/// The [`EnvFilter`] type also provided by this crate is very similar to `Targets`, +/// but is capable of a more sophisticated form of filtering where events may +/// also be enabled or disabled based on the span they are recorded in. +/// `Targets` can be thought of as a lighter-weight form of [`EnvFilter`] that +/// can be used instead when this dynamic filtering is not required. +/// +/// # Examples +/// +/// A `Targets` filter can be constructed by programmatically adding targets and +/// levels to enable: +/// +/// ``` +/// use tracing_subscriber::{filter, prelude::*}; +/// use tracing_core::Level; +/// +/// let filter = filter::Targets::new() +/// // Enable the `INFO` level for anything in `my_crate` +/// .with_target("my_crate", Level::INFO) +/// // Enable the `DEBUG` level for a specific module. +/// .with_target("my_crate::interesting_module", Level::DEBUG); +/// +/// // Build a new subscriber with the `fmt` layer using the `Targets` +/// // filter we constructed above. +/// tracing_subscriber::registry() +/// .with(tracing_subscriber::fmt::layer()) +/// .with(filter) +/// .init(); +/// ``` +/// +/// [`LevelFilter::OFF`] can be used to disable a particular target: +/// ``` +/// use tracing_subscriber::filter::{Targets, LevelFilter}; +/// use tracing_core::Level; +/// +/// let filter = Targets::new() +/// .with_target("my_crate", Level::INFO) +/// // Disable all traces from `annoying_module`. +/// .with_target("my_crate::annoying_module", LevelFilter::OFF); +/// # drop(filter); +/// ``` +/// +/// Alternatively, `Targets` implements [`std::str::FromStr`], allowing it to be +/// parsed from a comma-delimited list of `target=level` pairs. For example: +/// +/// ```rust +/// # fn main() -> Result<(), Box> { +/// use tracing_subscriber::filter; +/// use tracing_core::Level; +/// +/// let filter = "my_crate=info,my_crate::interesting_module=trace,other_crate=debug" +/// .parse::()?; +/// +/// // The parsed filter is identical to a filter constructed using `with_target`: +/// assert_eq!( +/// filter, +/// filter::Targets::new() +/// .with_target("my_crate", Level::INFO) +/// .with_target("my_crate::interesting_module", Level::TRACE) +/// .with_target("other_crate", Level::DEBUG) +/// ); +/// # Ok(()) } +/// ``` +/// +/// This is particularly useful when the list of enabled targets is configurable +/// by the user at runtime. +/// +/// The `Targets` filter can be used as a [per-layer filter][plf] *and* as a +/// [global filter]: +/// +/// ```rust +/// use tracing_subscriber::{ +/// fmt, +/// filter::{Targets, LevelFilter}, +/// prelude::*, +/// }; +/// use tracing_core::Level; +/// use std::{sync::Arc, fs::File}; +/// # fn docs() -> Result<(), Box> { +/// +/// // A layer that logs events to stdout using the human-readable "pretty" +/// // format. +/// let stdout_log = fmt::layer().pretty(); +/// +/// // A layer that logs events to a file, using the JSON format. +/// let file = File::create("debug_log.json")?; +/// let debug_log = fmt::layer() +/// .with_writer(Arc::new(file)) +/// .json(); +/// +/// tracing_subscriber::registry() +/// // Only log INFO and above to stdout, unless the span or event +/// // has the `my_crate::cool_module` target prefix. +/// .with(stdout_log +/// .with_filter( +/// Targets::default() +/// .with_target("my_crate::cool_module", Level::DEBUG) +/// .with_default(Level::INFO) +/// ) +/// ) +/// // Log everything enabled by the global filter to `debug_log.json`. +/// .with(debug_log) +/// // Configure a global filter for the whole subscriber stack. This will +/// // control what spans and events are recorded by both the `debug_log` +/// // and the `stdout_log` layers, and `stdout_log` will *additionally* be +/// // filtered by its per-layer filter. +/// .with( +/// Targets::default() +/// .with_target("my_crate", Level::TRACE) +/// .with_target("other_crate", Level::INFO) +/// .with_target("other_crate::annoying_module", LevelFilter::OFF) +/// .with_target("third_crate", Level::DEBUG) +/// ).init(); +/// # Ok(()) } +///``` +/// +/// [target]: tracing_core::Metadata::target +/// [level]: tracing_core::Level +/// [`Filter`]: crate::layer::Filter +/// [`Layer`]: crate::layer::Layer +/// [plf]: crate::layer#per-layer-filtering +/// [global]: crate::layer#global-filtering +/// [filtering]: crate::layer#filtering-with-layers +/// [`env_logger` crate]: https://docs.rs/env_logger/0.9.0/env_logger/index.html#enabling-logging +/// [`EnvFilter`]: crate::filter::EnvFilter +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Targets(DirectiveSet); + +impl Targets { + /// Returns a new `Targets` filter. + /// + /// This filter will enable no targets. Call [`with_target`] or [`with_targets`] + /// to add enabled targets, and [`with_default`] to change the default level + /// enabled for spans and events that didn't match any of the provided targets. + /// + /// [`with_target`]: Targets::with_target + /// [`with_targets`]: Targets::with_targets + /// [`with_default`]: Targets::with_default + pub fn new() -> Self { + Self::default() + } + + /// Enables spans and events with [target]s starting with the provided target + /// prefix if they are at or below the provided [`LevelFilter`]. + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::filter; + /// use tracing_core::Level; + /// + /// let filter = filter::Targets::new() + /// // Enable the `INFO` level for anything in `my_crate` + /// .with_target("my_crate", Level::INFO) + /// // Enable the `DEBUG` level for a specific module. + /// .with_target("my_crate::interesting_module", Level::DEBUG); + /// # drop(filter); + /// ``` + /// + /// [`LevelFilter::OFF`] can be used to disable a particular target: + /// ``` + /// use tracing_subscriber::filter::{Targets, LevelFilter}; + /// use tracing_core::Level; + /// + /// let filter = Targets::new() + /// .with_target("my_crate", Level::INFO) + /// // Disable all traces from `annoying_module`. + /// .with_target("my_crate::interesting_module", LevelFilter::OFF); + /// # drop(filter); + /// ``` + /// + /// [target]: tracing_core::Metadata::target + pub fn with_target(mut self, target: impl Into, level: impl Into) -> Self { + self.0.add(StaticDirective::new( + Some(target.into()), + Default::default(), + level.into(), + )); + self + } + /// Adds [target]s from an iterator of [target]-[`LevelFilter`] pairs to this filter. + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::filter; + /// use tracing_core::Level; + /// + /// let filter = filter::Targets::new() + /// .with_targets(vec![ + /// ("my_crate", Level::INFO), + /// ("my_crate::some_module", Level::DEBUG), + /// ("my_crate::other_module::cool_stuff", Level::TRACE), + /// ("other_crate", Level::WARN) + /// ]); + /// # drop(filter); + /// ``` + /// + /// [`LevelFilter::OFF`] can be used to disable a particular target: + /// ``` + /// use tracing_subscriber::filter::{Targets, LevelFilter}; + /// use tracing_core::Level; + /// + /// let filter = Targets::new() + /// .with_target("my_crate", Level::INFO) + /// // Disable all traces from `annoying_module`. + /// .with_target("my_crate::interesting_module", LevelFilter::OFF); + /// # drop(filter); + /// ``` + /// + /// [target]: tracing_core::Metadata::target + pub fn with_targets(mut self, targets: impl IntoIterator) -> Self + where + String: From, + LevelFilter: From, + { + self.extend(targets); + self + } + + /// Sets the default level to enable for spans and events whose targets did + /// not match any of the configured prefixes. + /// + /// By default, this is [`LevelFilter::OFF`]. This means that spans and + /// events will only be enabled if they match one of the configured target + /// prefixes. If this is changed to a different [`LevelFilter`], spans and + /// events with targets that did not match any of the configured prefixes + /// will be enabled if their level is at or below the provided level. + pub fn with_default(mut self, level: impl Into) -> Self { + self.0 + .add(StaticDirective::new(None, Default::default(), level.into())); + self + } + + /// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter. + /// + /// The order of iteration is undefined. + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::filter::{Targets, LevelFilter}; + /// use tracing_core::Level; + /// + /// let filter = Targets::new() + /// .with_target("my_crate", Level::INFO) + /// .with_target("my_crate::interesting_module", Level::DEBUG); + /// + /// let mut targets: Vec<_> = filter.iter().collect(); + /// targets.sort(); + /// + /// assert_eq!(targets, vec![ + /// ("my_crate", LevelFilter::INFO), + /// ("my_crate::interesting_module", LevelFilter::DEBUG), + /// ]); + /// ``` + /// + /// [target]: tracing_core::Metadata::target + pub fn iter(&self) -> Iter<'_> { + self.into_iter() + } + + #[inline] + fn interested(&self, metadata: &'static Metadata<'static>) -> Interest { + if self.0.enabled(metadata) { + Interest::always() + } else { + Interest::never() + } + } +} + +impl Extend<(T, L)> for Targets +where + T: Into, + L: Into, +{ + fn extend>(&mut self, iter: I) { + let iter = iter.into_iter().map(|(target, level)| { + StaticDirective::new(Some(target.into()), Default::default(), level.into()) + }); + self.0.extend(iter); + } +} + +impl FromIterator<(T, L)> for Targets +where + T: Into, + L: Into, +{ + fn from_iter>(iter: I) -> Self { + let mut this = Self::default(); + this.extend(iter); + this + } +} + +impl FromStr for Targets { + type Err = ParseError; + fn from_str(s: &str) -> Result { + s.split(',') + .map(StaticDirective::from_str) + .collect::>() + .map(Self) + } +} + +impl layer::Layer for Targets +where + S: Subscriber, +{ + fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { + self.0.enabled(metadata) + } + + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + self.interested(metadata) + } + + fn max_level_hint(&self) -> Option { + Some(self.0.max_level) + } +} + +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +impl layer::Filter for Targets { + fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { + self.0.enabled(metadata) + } + + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + self.interested(metadata) + } + + fn max_level_hint(&self) -> Option { + Some(self.0.max_level) + } +} + +impl IntoIterator for Targets { + type Item = (String, LevelFilter); + + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self) + } +} + +impl<'a> IntoIterator for &'a Targets { + type Item = (&'a str, LevelFilter); + + type IntoIter = Iter<'a>; + + fn into_iter(self) -> Self::IntoIter { + Iter::new(self) + } +} + +/// An owning iterator over the [target]-[level] pairs of a `Targets` filter. +/// +/// This struct is created by the `IntoIterator` trait implementation of [`Targets`]. +/// +/// # Examples +/// +/// Merge the targets from one `Targets` with another: +/// +/// ``` +/// use tracing_subscriber::filter::Targets; +/// use tracing_core::Level; +/// +/// let mut filter = Targets::new().with_target("my_crate", Level::INFO); +/// let overrides = Targets::new().with_target("my_crate::interesting_module", Level::DEBUG); +/// +/// filter.extend(overrides); +/// # drop(filter); +/// ``` +/// +/// [target]: tracing_core::Metadata::target +/// [level]: tracing_core::Level +#[derive(Debug)] +pub struct IntoIter( + #[allow(clippy::type_complexity)] // alias indirection would probably make this more confusing + FilterMap< + as IntoIterator>::IntoIter, + fn(StaticDirective) -> Option<(String, LevelFilter)>, + >, +); + +impl IntoIter { + fn new(targets: Targets) -> Self { + Self(targets.0.into_iter().filter_map(|directive| { + let level = directive.level; + directive.target.map(|target| (target, level)) + })) + } +} + +impl Iterator for IntoIter { + type Item = (String, LevelFilter); + + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +/// A borrowing iterator over the [target]-[level] pairs of a `Targets` filter. +/// +/// This struct is created by [`iter`] method of [`Targets`], or from the `IntoIterator` +/// implementation for `&Targets`. +/// +/// [target]: tracing_core::Metadata::target +/// [level]: tracing_core::Level +/// [`iter`]: Targets::iter +#[derive(Debug)] +pub struct Iter<'a>( + FilterMap< + slice::Iter<'a, StaticDirective>, + fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>, + >, +); + +impl<'a> Iter<'a> { + fn new(targets: &'a Targets) -> Self { + Self(targets.0.iter().filter_map(|directive| { + directive + .target + .as_deref() + .map(|target| (target, directive.level)) + })) + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = (&'a str, LevelFilter); + + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + feature! { + #![not(feature = "std")] + use alloc::{vec, vec::Vec, string::ToString}; + + // `dbg!` is only available with `libstd`; just nop it out when testing + // with alloc only. + macro_rules! dbg { + ($x:expr) => { $x } + } + } + + fn expect_parse(s: &str) -> Targets { + match dbg!(s).parse::() { + Err(e) => panic!("string {:?} did not parse successfully: {}", s, e), + Ok(e) => e, + } + } + + fn expect_parse_ralith(s: &str) { + let dirs = expect_parse(s).0.into_vec(); + assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("server".to_string())); + assert_eq!(dirs[0].level, LevelFilter::DEBUG); + assert_eq!(dirs[0].field_names, Vec::::new()); + + assert_eq!(dirs[1].target, Some("common".to_string())); + assert_eq!(dirs[1].level, LevelFilter::INFO); + assert_eq!(dirs[1].field_names, Vec::::new()); + } + + fn expect_parse_level_directives(s: &str) { + let dirs = expect_parse(s).0.into_vec(); + assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); + + assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string())); + assert_eq!(dirs[0].level, LevelFilter::OFF); + assert_eq!(dirs[0].field_names, Vec::::new()); + + assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string())); + assert_eq!(dirs[1].level, LevelFilter::INFO); + assert_eq!(dirs[1].field_names, Vec::::new()); + + assert_eq!(dirs[2].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[2].level, LevelFilter::WARN); + assert_eq!(dirs[2].field_names, Vec::::new()); + + assert_eq!(dirs[3].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[3].level, LevelFilter::ERROR); + assert_eq!(dirs[3].field_names, Vec::::new()); + + assert_eq!(dirs[4].target, Some("crate3".to_string())); + assert_eq!(dirs[4].level, LevelFilter::TRACE); + assert_eq!(dirs[4].field_names, Vec::::new()); + + assert_eq!(dirs[5].target, Some("crate2".to_string())); + assert_eq!(dirs[5].level, LevelFilter::DEBUG); + assert_eq!(dirs[5].field_names, Vec::::new()); + } + + #[test] + fn parse_ralith() { + expect_parse_ralith("common=info,server=debug"); + } + + #[test] + fn parse_ralith_uc() { + expect_parse_ralith("common=INFO,server=DEBUG"); + } + + #[test] + fn parse_ralith_mixed() { + expect_parse("common=iNfo,server=dEbUg"); + } + + #[test] + fn expect_parse_valid() { + let dirs = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .0 + .into_vec(); + assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("crate1::mod2".to_string())); + assert_eq!(dirs[0].level, LevelFilter::TRACE); + assert_eq!(dirs[0].field_names, Vec::::new()); + + assert_eq!(dirs[1].target, Some("crate1::mod1".to_string())); + assert_eq!(dirs[1].level, LevelFilter::ERROR); + assert_eq!(dirs[1].field_names, Vec::::new()); + + assert_eq!(dirs[2].target, Some("crate3".to_string())); + assert_eq!(dirs[2].level, LevelFilter::OFF); + assert_eq!(dirs[2].field_names, Vec::::new()); + + assert_eq!(dirs[3].target, Some("crate2".to_string())); + assert_eq!(dirs[3].level, LevelFilter::DEBUG); + assert_eq!(dirs[3].field_names, Vec::::new()); + } + + #[test] + fn parse_level_directives() { + expect_parse_level_directives( + "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ + crate2=debug,crate3=trace,crate3::mod2::mod1=off", + ) + } + + #[test] + fn parse_uppercase_level_directives() { + expect_parse_level_directives( + "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ + crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", + ) + } + + #[test] + fn parse_numeric_level_directives() { + expect_parse_level_directives( + "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\ + crate3=5,crate3::mod2::mod1=0", + ) + } + + #[test] + fn targets_iter() { + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::WARN); + + let mut targets: Vec<_> = filter.iter().collect(); + targets.sort(); + + assert_eq!( + targets, + vec![ + ("crate1::mod1", LevelFilter::ERROR), + ("crate1::mod2", LevelFilter::TRACE), + ("crate2", LevelFilter::DEBUG), + ("crate3", LevelFilter::OFF), + ] + ); + } + + #[test] + fn targets_into_iter() { + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::WARN); + + let mut targets: Vec<_> = filter.into_iter().collect(); + targets.sort(); + + assert_eq!( + targets, + vec![ + ("crate1::mod1".to_string(), LevelFilter::ERROR), + ("crate1::mod2".to_string(), LevelFilter::TRACE), + ("crate2".to_string(), LevelFilter::DEBUG), + ("crate3".to_string(), LevelFilter::OFF), + ] + ); + } + + #[test] + // `println!` is only available with `libstd`. + #[cfg(feature = "std")] + fn size_of_filters() { + fn print_sz(s: &str) { + let filter = s.parse::().expect("filter should parse"); + println!( + "size_of_val({:?})\n -> {}B", + s, + std::mem::size_of_val(&filter) + ); + } + + print_sz("info"); + + print_sz("foo=debug"); + + print_sz( + "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ + crate2=debug,crate3=trace,crate3::mod2::mod1=off", + ); + } +} -- cgit v1.2.3