summaryrefslogtreecommitdiffstats
path: root/vendor/tracing-subscriber/src/filter/env
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vendor/tracing-subscriber/src/filter/env/builder.rs325
-rw-r--r--vendor/tracing-subscriber/src/filter/env/directive.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs)160
-rw-r--r--vendor/tracing-subscriber/src/filter/env/field.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs)250
-rw-r--r--vendor/tracing-subscriber/src/filter/env/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs)677
4 files changed, 1107 insertions, 305 deletions
diff --git a/vendor/tracing-subscriber/src/filter/env/builder.rs b/vendor/tracing-subscriber/src/filter/env/builder.rs
new file mode 100644
index 000000000..c814707e6
--- /dev/null
+++ b/vendor/tracing-subscriber/src/filter/env/builder.rs
@@ -0,0 +1,325 @@
+use super::{
+ directive::{self, Directive},
+ EnvFilter, FromEnvError,
+};
+use crate::sync::RwLock;
+use std::env;
+use thread_local::ThreadLocal;
+use tracing::level_filters::STATIC_MAX_LEVEL;
+
+/// A [builder] for constructing new [`EnvFilter`]s.
+///
+/// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html
+#[derive(Debug, Clone)]
+#[must_use]
+pub struct Builder {
+ regex: bool,
+ env: Option<String>,
+ default_directive: Option<Directive>,
+}
+
+impl Builder {
+ /// Sets whether span field values can be matched with regular expressions.
+ ///
+ /// If this is `true`, field filter directives will be interpreted as
+ /// regular expressions if they are not able to be interpreted as a `bool`,
+ /// `i64`, `u64`, or `f64` literal. If this is `false,` those field values
+ /// will be interpreted as literal [`std::fmt::Debug`] output instead.
+ ///
+ /// By default, regular expressions are enabled.
+ ///
+ /// **Note**: when [`EnvFilter`]s are constructed from untrusted inputs,
+ /// disabling regular expressions is strongly encouraged.
+ pub fn with_regex(self, regex: bool) -> Self {
+ Self { regex, ..self }
+ }
+
+ /// Sets a default [filtering directive] that will be added to the filter if
+ /// the parsed string or environment variable contains no filter directives.
+ ///
+ /// By default, there is no default directive.
+ ///
+ /// # Examples
+ ///
+ /// If [`parse`], [`parse_lossy`], [`from_env`], or [`from_env_lossy`] are
+ /// called with an empty string or environment variable, the default
+ /// directive is used instead:
+ ///
+ /// ```rust
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::INFO.into())
+ /// .parse("")?;
+ ///
+ /// assert_eq!(format!("{}", filter), "info");
+ /// # Ok(()) }
+ /// ```
+ ///
+ /// Note that the `lossy` variants ([`parse_lossy`] and [`from_env_lossy`])
+ /// will ignore any invalid directives. If all directives in a filter
+ /// string or environment variable are invalid, those methods will also use
+ /// the default directive:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::INFO.into())
+ /// .parse_lossy("some_target=fake level,foo::bar=lolwut");
+ ///
+ /// assert_eq!(format!("{}", filter), "info");
+ /// ```
+ ///
+ ///
+ /// If the string or environment variable contains valid filtering
+ /// directives, the default directive is not used:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::INFO.into())
+ /// .parse_lossy("foo=trace");
+ ///
+ /// // The default directive is *not* used:
+ /// assert_eq!(format!("{}", filter), "foo=trace");
+ /// ```
+ ///
+ /// Parsing a more complex default directive from a string:
+ ///
+ /// ```rust
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let default = "myapp=debug".parse()
+ /// .expect("hard-coded default directive should be valid");
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(default)
+ /// .parse("")?;
+ ///
+ /// assert_eq!(format!("{}", filter), "myapp=debug");
+ /// # Ok(()) }
+ /// ```
+ ///
+ /// [`parse_lossy`]: Self::parse_lossy
+ /// [`from_env_lossy`]: Self::from_env_lossy
+ /// [`parse`]: Self::parse
+ /// [`from_env`]: Self::from_env
+ pub fn with_default_directive(self, default_directive: Directive) -> Self {
+ Self {
+ default_directive: Some(default_directive),
+ ..self
+ }
+ }
+
+ /// Sets the name of the environment variable used by the [`from_env`],
+ /// [`from_env_lossy`], and [`try_from_env`] methods.
+ ///
+ /// By default, this is the value of [`EnvFilter::DEFAULT_ENV`]
+ /// (`RUST_LOG`).
+ ///
+ /// [`from_env`]: Self::from_env
+ /// [`from_env_lossy`]: Self::from_env_lossy
+ /// [`try_from_env`]: Self::try_from_env
+ pub fn with_env_var(self, var: impl ToString) -> Self {
+ Self {
+ env: Some(var.to_string()),
+ ..self
+ }
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the given string,
+ /// *ignoring* any that are invalid.
+ pub fn parse_lossy<S: AsRef<str>>(&self, dirs: S) -> EnvFilter {
+ let directives = dirs
+ .as_ref()
+ .split(',')
+ .filter(|s| !s.is_empty())
+ .filter_map(|s| match Directive::parse(s, self.regex) {
+ Ok(d) => Some(d),
+ Err(err) => {
+ eprintln!("ignoring `{}`: {}", s, err);
+ None
+ }
+ });
+ self.from_directives(directives)
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the given string,
+ /// or an error if any are invalid.
+ pub fn parse<S: AsRef<str>>(&self, dirs: S) -> Result<EnvFilter, directive::ParseError> {
+ let dirs = dirs.as_ref();
+ if dirs.is_empty() {
+ return Ok(self.from_directives(std::iter::empty()));
+ }
+ let directives = dirs
+ .split(',')
+ .filter(|s| !s.is_empty())
+ .map(|s| Directive::parse(s, self.regex))
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(self.from_directives(directives))
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the configured
+ /// environment variable, ignoring any directives that are invalid.
+ pub fn from_env_lossy(&self) -> EnvFilter {
+ let var = env::var(self.env_var_name()).unwrap_or_default();
+ self.parse_lossy(var)
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the in the configured
+ /// environment variable, or an error if the environment variable is not set
+ /// or contains invalid directives.
+ pub fn from_env(&self) -> Result<EnvFilter, FromEnvError> {
+ let var = env::var(self.env_var_name()).unwrap_or_default();
+ self.parse(var).map_err(Into::into)
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the in the configured
+ /// environment variable, or an error if the environment variable is not set
+ /// or contains invalid directives.
+ pub fn try_from_env(&self) -> Result<EnvFilter, FromEnvError> {
+ let var = env::var(self.env_var_name())?;
+ self.parse(var).map_err(Into::into)
+ }
+
+ // TODO(eliza): consider making this a public API?
+ // Clippy doesn't love this naming, because it suggests that `from_` methods
+ // should not take a `Self`...but in this case, it's the `EnvFilter` that is
+ // being constructed "from" the directives, rather than the builder itself.
+ #[allow(clippy::wrong_self_convention)]
+ pub(super) fn from_directives(
+ &self,
+ directives: impl IntoIterator<Item = Directive>,
+ ) -> EnvFilter {
+ use tracing::Level;
+
+ let mut directives: Vec<_> = directives.into_iter().collect();
+ let mut disabled = Vec::new();
+ for directive in &mut directives {
+ if directive.level > STATIC_MAX_LEVEL {
+ disabled.push(directive.clone());
+ }
+ if !self.regex {
+ directive.deregexify();
+ }
+ }
+
+ if !disabled.is_empty() {
+ #[cfg(feature = "nu_ansi_term")]
+ use nu_ansi_term::{Color, Style};
+ // NOTE: We can't use a configured `MakeWriter` because the EnvFilter
+ // has no knowledge of any underlying subscriber or collector, which
+ // may or may not use a `MakeWriter`.
+ let warn = |msg: &str| {
+ #[cfg(not(feature = "nu_ansi_term"))]
+ let msg = format!("warning: {}", msg);
+ #[cfg(feature = "nu_ansi_term")]
+ let msg = {
+ let bold = Style::new().bold();
+ let mut warning = Color::Yellow.paint("warning");
+ warning.style_ref_mut().is_bold = true;
+ format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg))
+ };
+ eprintln!("{}", msg);
+ };
+ let ctx_prefixed = |prefix: &str, msg: &str| {
+ #[cfg(not(feature = "nu_ansi_term"))]
+ let msg = format!("{} {}", prefix, msg);
+ #[cfg(feature = "nu_ansi_term")]
+ let msg = {
+ let mut equal = Color::Fixed(21).paint("="); // dark blue
+ equal.style_ref_mut().is_bold = true;
+ format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg)
+ };
+ eprintln!("{}", msg);
+ };
+ let ctx_help = |msg| ctx_prefixed("help:", msg);
+ let ctx_note = |msg| ctx_prefixed("note:", msg);
+ let ctx = |msg: &str| {
+ #[cfg(not(feature = "nu_ansi_term"))]
+ let msg = format!("note: {}", msg);
+ #[cfg(feature = "nu_ansi_term")]
+ let msg = {
+ let mut pipe = Color::Fixed(21).paint("|");
+ pipe.style_ref_mut().is_bold = true;
+ format!(" {} {}", pipe, msg)
+ };
+ eprintln!("{}", msg);
+ };
+ warn("some trace filter directives would enable traces that are disabled statically");
+ for directive in disabled {
+ let target = if let Some(target) = &directive.target {
+ format!("the `{}` target", target)
+ } else {
+ "all targets".into()
+ };
+ let level = directive
+ .level
+ .into_level()
+ .expect("=off would not have enabled any filters");
+ ctx(&format!(
+ "`{}` would enable the {} level for {}",
+ directive, level, target
+ ));
+ }
+ ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL));
+ let help_msg = || {
+ let (feature, filter) = match STATIC_MAX_LEVEL.into_level() {
+ Some(Level::TRACE) => unreachable!(
+ "if the max level is trace, no static filtering features are enabled"
+ ),
+ Some(Level::DEBUG) => ("max_level_debug", Level::TRACE),
+ Some(Level::INFO) => ("max_level_info", Level::DEBUG),
+ Some(Level::WARN) => ("max_level_warn", Level::INFO),
+ Some(Level::ERROR) => ("max_level_error", Level::WARN),
+ None => return ("max_level_off", String::new()),
+ };
+ (feature, format!("{} ", filter))
+ };
+ let (feature, earlier_level) = help_msg();
+ ctx_help(&format!(
+ "to enable {}logging, remove the `{}` feature from the `tracing` crate",
+ earlier_level, feature
+ ));
+ }
+
+ let (dynamics, statics) = Directive::make_tables(directives);
+ let has_dynamics = !dynamics.is_empty();
+
+ let mut filter = EnvFilter {
+ statics,
+ dynamics,
+ has_dynamics,
+ by_id: RwLock::new(Default::default()),
+ by_cs: RwLock::new(Default::default()),
+ scope: ThreadLocal::new(),
+ regex: self.regex,
+ };
+
+ if !has_dynamics && filter.statics.is_empty() {
+ if let Some(ref default) = self.default_directive {
+ filter = filter.add_directive(default.clone());
+ }
+ }
+
+ filter
+ }
+
+ fn env_var_name(&self) -> &str {
+ self.env.as_deref().unwrap_or(EnvFilter::DEFAULT_ENV)
+ }
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Self {
+ regex: true,
+ env: None,
+ default_directive: None,
+ }
+ }
+}
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs b/vendor/tracing-subscriber/src/filter/env/directive.rs
index 66ca23dc4..f062e6ef9 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs
+++ b/vendor/tracing-subscriber/src/filter/env/directive.rs
@@ -4,14 +4,14 @@ use crate::filter::{
env::{field, FieldMap},
level::LevelFilter,
};
-use lazy_static::lazy_static;
+use once_cell::sync::Lazy;
use regex::Regex;
use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
use tracing_core::{span, Level, Metadata};
/// A single filtering directive.
// TODO(eliza): add a builder for programmatically constructing directives?
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
pub struct Directive {
in_span: Option<String>,
@@ -107,80 +107,52 @@ impl Directive {
.collect();
(Dynamics::from_iter(dyns), statics)
}
-}
-
-impl Match for Directive {
- fn cares_about(&self, meta: &Metadata<'_>) -> bool {
- // Does this directive have a target filter, and does it match the
- // metadata's target?
- if let Some(ref target) = self.target {
- if !meta.target().starts_with(&target[..]) {
- return false;
- }
- }
-
- // Do we have a name filter, and does it match the metadata's name?
- // TODO(eliza): put name globbing here?
- if let Some(ref name) = self.in_span {
- if name != meta.name() {
- return false;
- }
- }
- // Does the metadata define all the fields that this directive cares about?
- let fields = meta.fields();
- for field in &self.fields {
- if fields.field(&field.name).is_none() {
- return false;
+ pub(super) fn deregexify(&mut self) {
+ for field in &mut self.fields {
+ field.value = match field.value.take() {
+ Some(field::ValueMatch::Pat(pat)) => {
+ Some(field::ValueMatch::Debug(pat.into_debug_match()))
+ }
+ x => x,
}
}
-
- true
- }
-
- fn level(&self) -> &LevelFilter {
- &self.level
}
-}
-impl FromStr for Directive {
- type Err = ParseError;
- fn from_str(from: &str) -> Result<Self, Self::Err> {
- lazy_static! {
- static ref DIRECTIVE_RE: Regex = Regex::new(
- r"(?x)
- ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
- # ^^^.
- # `note: we match log level names case-insensitively
- ^
- (?: # target name or span name
- (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
- ){1,2}
- (?: # level or nothing
- =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
- # ^^^.
- # `note: we match log level names case-insensitively
- )?
- $
- "
- )
- .unwrap();
- static ref SPAN_PART_RE: Regex =
- Regex::new(r#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap();
- static ref FIELD_FILTER_RE: Regex =
- // TODO(eliza): this doesn't _currently_ handle value matchers that include comma
- // characters. We should fix that.
- Regex::new(r#"(?x)
- (
- # field name
- [[:word:]][[[:word:]]\.]*
- # value part (optional)
- (?:=[^,]+)?
- )
- # trailing comma or EOS
- (?:,\s?|$)
- "#).unwrap();
- }
+ pub(super) fn parse(from: &str, regex: bool) -> Result<Self, ParseError> {
+ static DIRECTIVE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(
+ r"(?x)
+ ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
+ # ^^^.
+ # `note: we match log level names case-insensitively
+ ^
+ (?: # target name or span name
+ (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
+ ){1,2}
+ (?: # level or nothing
+ =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
+ # ^^^.
+ # `note: we match log level names case-insensitively
+ )?
+ $
+ "
+ )
+ .unwrap());
+ static SPAN_PART_RE: Lazy<Regex> =
+ Lazy::new(|| Regex::new(r#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap());
+ static FIELD_FILTER_RE: Lazy<Regex> =
+ // TODO(eliza): this doesn't _currently_ handle value matchers that include comma
+ // characters. We should fix that.
+ Lazy::new(|| Regex::new(r#"(?x)
+ (
+ # field name
+ [[:word:]][[[:word:]]\.]*
+ # value part (optional)
+ (?:=[^,]+)?
+ )
+ # trailing comma or EOS
+ (?:,\s?|$)
+ "#).unwrap());
let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?;
@@ -214,7 +186,7 @@ impl FromStr for Directive {
.map(|c| {
FIELD_FILTER_RE
.find_iter(c.as_str())
- .map(|c| c.as_str().parse())
+ .map(|c| field::Match::parse(c.as_str(), regex))
.collect::<Result<Vec<_>, _>>()
})
.unwrap_or_else(|| Ok(Vec::new()));
@@ -228,7 +200,7 @@ impl FromStr for Directive {
// Setting the target without the level enables every level for that target
.unwrap_or(LevelFilter::TRACE);
- Ok(Directive {
+ Ok(Self {
level,
target,
in_span,
@@ -237,6 +209,48 @@ impl FromStr for Directive {
}
}
+impl Match for Directive {
+ fn cares_about(&self, meta: &Metadata<'_>) -> bool {
+ // Does this directive have a target filter, and does it match the
+ // metadata's target?
+ if let Some(ref target) = self.target {
+ if !meta.target().starts_with(&target[..]) {
+ return false;
+ }
+ }
+
+ // Do we have a name filter, and does it match the metadata's name?
+ // TODO(eliza): put name globbing here?
+ if let Some(ref name) = self.in_span {
+ if name != meta.name() {
+ return false;
+ }
+ }
+
+ // Does the metadata define all the fields that this directive cares about?
+ let actual_fields = meta.fields();
+ for expected_field in &self.fields {
+ // Does the actual field set (from the metadata) contain this field?
+ if actual_fields.field(&expected_field.name).is_none() {
+ return false;
+ }
+ }
+
+ true
+ }
+
+ fn level(&self) -> &LevelFilter {
+ &self.level
+ }
+}
+
+impl FromStr for Directive {
+ type Err = ParseError;
+ fn from_str(from: &str) -> Result<Self, Self::Err> {
+ Directive::parse(from, true)
+ }
+}
+
impl Default for Directive {
fn default() -> Self {
Directive {
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs b/vendor/tracing-subscriber/src/filter/env/field.rs
index 970850f92..1394fd04a 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs
+++ b/vendor/tracing-subscriber/src/filter/env/field.rs
@@ -2,7 +2,7 @@ use matchers::Pattern;
use std::{
cmp::Ordering,
error::Error,
- fmt,
+ fmt::{self, Write},
str::FromStr,
sync::{
atomic::{AtomicBool, Ordering::*},
@@ -13,7 +13,7 @@ use std::{
use super::{FieldMap, LevelFilter};
use tracing_core::field::{Field, Visit};
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Match {
pub(crate) name: String, // TODO: allow match patterns for names?
pub(crate) value: Option<ValueMatch>,
@@ -38,11 +38,20 @@ pub(crate) struct MatchVisitor<'a> {
#[derive(Debug, Clone)]
pub(crate) enum ValueMatch {
+ /// Matches a specific `bool` value.
Bool(bool),
+ /// Matches a specific `f64` value.
F64(f64),
+ /// Matches a specific `u64` value.
U64(u64),
+ /// Matches a specific `i64` value.
I64(i64),
+ /// Matches any `NaN` `f64` value.
NaN,
+ /// Matches any field whose `fmt::Debug` output is equal to a fixed string.
+ Debug(MatchDebug),
+ /// Matches any field whose `fmt::Debug` output matches a regular expression
+ /// pattern.
Pat(Box<MatchPattern>),
}
@@ -97,6 +106,9 @@ impl Ord for ValueMatch {
(Pat(this), Pat(that)) => this.cmp(that),
(Pat(_), _) => Ordering::Greater,
+
+ (Debug(this), Debug(that)) => this.cmp(that),
+ (Debug(_), _) => Ordering::Greater,
}
}
}
@@ -107,12 +119,25 @@ impl PartialOrd for ValueMatch {
}
}
+/// Matches a field's `fmt::Debug` output against a regular expression pattern.
+///
+/// This is used for matching all non-literal field value filters when regular
+/// expressions are enabled.
#[derive(Debug, Clone)]
pub(crate) struct MatchPattern {
pub(crate) matcher: Pattern,
pattern: Arc<str>,
}
+/// Matches a field's `fmt::Debug` output against a fixed string pattern.
+///
+/// This is used for matching all non-literal field value filters when regular
+/// expressions are disabled.
+#[derive(Debug, Clone)]
+pub(crate) struct MatchDebug {
+ pattern: Arc<str>,
+}
+
/// Indicates that a field name specified in a filter directive was invalid.
#[derive(Clone, Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
@@ -122,9 +147,17 @@ pub struct BadName {
// === impl Match ===
-impl FromStr for Match {
- type Err = Box<dyn Error + Send + Sync>;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
+impl Match {
+ pub(crate) fn has_value(&self) -> bool {
+ self.value.is_some()
+ }
+
+ // TODO: reference count these strings?
+ pub(crate) fn name(&self) -> String {
+ self.name.clone()
+ }
+
+ pub(crate) fn parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>> {
let mut parts = s.split('=');
let name = parts
.next()
@@ -133,22 +166,17 @@ impl FromStr for Match {
})?
// TODO: validate field name
.to_string();
- let value = parts.next().map(ValueMatch::from_str).transpose()?;
+ let value = parts
+ .next()
+ .map(|part| match regex {
+ true => ValueMatch::parse_regex(part),
+ false => Ok(ValueMatch::parse_non_regex(part)),
+ })
+ .transpose()?;
Ok(Match { name, value })
}
}
-impl Match {
- pub(crate) fn has_value(&self) -> bool {
- self.value.is_some()
- }
-
- // TODO: reference count these strings?
- pub(crate) fn name(&self) -> String {
- self.name.clone()
- }
-}
-
impl fmt::Display for Match {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)?;
@@ -199,9 +227,14 @@ fn value_match_f64(v: f64) -> ValueMatch {
}
}
-impl FromStr for ValueMatch {
- type Err = matchers::Error;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
+impl ValueMatch {
+ /// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular
+ /// expressions.
+ ///
+ /// This returns an error if the string didn't contain a valid `bool`,
+ /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular
+ /// expression.
+ fn parse_regex(s: &str) -> Result<Self, matchers::Error> {
s.parse::<bool>()
.map(ValueMatch::Bool)
.or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
@@ -212,6 +245,21 @@ impl FromStr for ValueMatch {
.map(|p| ValueMatch::Pat(Box::new(p)))
})
}
+
+ /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed
+ /// string.
+ ///
+ /// This does *not* return an error, because any string that isn't a valid
+ /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected
+ /// `fmt::Debug` output.
+ fn parse_non_regex(s: &str) -> Self {
+ s.parse::<bool>()
+ .map(ValueMatch::Bool)
+ .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
+ .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
+ .or_else(|_| s.parse::<f64>().map(value_match_f64))
+ .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s)))
+ }
}
impl fmt::Display for ValueMatch {
@@ -222,6 +270,7 @@ impl fmt::Display for ValueMatch {
ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f),
ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
+ ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f),
}
}
@@ -264,6 +313,12 @@ impl MatchPattern {
fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
self.matcher.debug_matches(d)
}
+
+ pub(super) fn into_debug_match(self) -> MatchDebug {
+ MatchDebug {
+ pattern: self.pattern,
+ }
+ }
}
impl PartialEq for MatchPattern {
@@ -289,6 +344,102 @@ impl Ord for MatchPattern {
}
}
+// === impl MatchDebug ===
+
+impl MatchDebug {
+ fn new(s: &str) -> Self {
+ Self {
+ pattern: s.to_owned().into(),
+ }
+ }
+
+ #[inline]
+ fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
+ // Naively, we would probably match a value's `fmt::Debug` output by
+ // formatting it to a string, and then checking if the string is equal
+ // to the expected pattern. However, this would require allocating every
+ // time we want to match a field value against a `Debug` matcher, which
+ // can be avoided.
+ //
+ // Instead, we implement `fmt::Write` for a type that, rather than
+ // actually _writing_ the strings to something, matches them against the
+ // expected pattern, and returns an error if the pattern does not match.
+ struct Matcher<'a> {
+ pattern: &'a str,
+ }
+
+ impl fmt::Write for Matcher<'_> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ // If the string is longer than the remaining expected string,
+ // we know it won't match, so bail.
+ if s.len() > self.pattern.len() {
+ return Err(fmt::Error);
+ }
+
+ // If the expected string begins with the string that was
+ // written, we are still potentially a match. Advance the
+ // position in the expected pattern to chop off the matched
+ // output, and continue.
+ if self.pattern.starts_with(s) {
+ self.pattern = &self.pattern[s.len()..];
+ return Ok(());
+ }
+
+ // Otherwise, the expected string doesn't include the string
+ // that was written at the current position, so the `fmt::Debug`
+ // output doesn't match! Return an error signalling that this
+ // doesn't match.
+ Err(fmt::Error)
+ }
+ }
+ let mut matcher = Matcher {
+ pattern: &self.pattern,
+ };
+
+ // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This
+ // returns an error if the `fmt::Debug` implementation wrote any
+ // characters that did not match the expected pattern.
+ write!(matcher, "{:?}", d).is_ok()
+ }
+}
+
+impl fmt::Display for MatchDebug {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&*self.pattern, f)
+ }
+}
+
+impl AsRef<str> for MatchDebug {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ self.pattern.as_ref()
+ }
+}
+
+impl PartialEq for MatchDebug {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.pattern == other.pattern
+ }
+}
+
+impl Eq for MatchDebug {}
+
+impl PartialOrd for MatchDebug {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.pattern.cmp(&other.pattern))
+ }
+}
+
+impl Ord for MatchDebug {
+ #[inline]
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.pattern.cmp(&other.pattern)
+ }
+}
+
// === impl BadName ===
impl Error for BadName {}
@@ -401,6 +552,9 @@ impl<'a> Visit for MatchVisitor<'a> {
Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => {
matched.store(true, Release);
}
+ Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
+ matched.store(true, Release)
+ }
_ => {}
}
}
@@ -410,7 +564,63 @@ impl<'a> Visit for MatchVisitor<'a> {
Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => {
matched.store(true, Release);
}
+ Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
+ matched.store(true, Release)
+ }
_ => {}
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[derive(Debug)]
+ #[allow(dead_code)]
+ struct MyStruct {
+ answer: usize,
+ question: &'static str,
+ }
+
+ #[test]
+ fn debug_struct_match() {
+ let my_struct = MyStruct {
+ answer: 42,
+ question: "life, the universe, and everything",
+ };
+
+ let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
+
+ assert_eq!(
+ format!("{:?}", my_struct),
+ pattern,
+ "`MyStruct`'s `Debug` impl doesn't output the expected string"
+ );
+
+ let matcher = MatchDebug {
+ pattern: pattern.into(),
+ };
+ assert!(matcher.debug_matches(&my_struct))
+ }
+
+ #[test]
+ fn debug_struct_not_match() {
+ let my_struct = MyStruct {
+ answer: 42,
+ question: "what shall we have for lunch?",
+ };
+
+ let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
+
+ assert_eq!(
+ format!("{:?}", my_struct),
+ "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }",
+ "`MyStruct`'s `Debug` impl doesn't output the expected string"
+ );
+
+ let matcher = MatchDebug {
+ pattern: pattern.into(),
+ };
+ assert!(!matcher.debug_matches(&my_struct))
+ }
+}
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs b/vendor/tracing-subscriber/src/filter/env/mod.rs
index 81fe0e62d..81a9ae2bd 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs
+++ b/vendor/tracing-subscriber/src/filter/env/mod.rs
@@ -4,7 +4,8 @@
// these are publicly re-exported, but the compiler doesn't realize
// that for some reason.
#[allow(unreachable_pub)]
-pub use self::{directive::Directive, field::BadName as BadFieldName};
+pub use self::{builder::Builder, directive::Directive, field::BadName as BadFieldName};
+mod builder;
mod directive;
mod field;
@@ -15,6 +16,7 @@ use crate::{
};
use directive::ParseError;
use std::{cell::RefCell, collections::HashMap, env, error::Error, fmt, str::FromStr};
+use thread_local::ThreadLocal;
use tracing_core::{
callsite,
field::Field,
@@ -26,6 +28,16 @@ use tracing_core::{
/// A [`Layer`] which filters spans and events based on a set of filter
/// directives.
///
+/// `EnvFilter` implements both the [`Layer`](#impl-Layer<S>) and [`Filter`] traits, so it may
+/// be used for both [global filtering][global] and [per-layer filtering][plf],
+/// respectively. See [the documentation on filtering with `Layer`s][filtering]
+/// for details.
+///
+/// The [`Targets`] type implements a similar form of filtering, but without the
+/// ability to dynamically enable events based on the current span context, and
+/// without filtering on field values. When these features are not required,
+/// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
+///
/// # Directives
///
/// A filter consists of one or more comma-separated directives which match on [`Span`]s and [`Event`]s.
@@ -52,10 +64,27 @@ use tracing_core::{
/// and will match on any [`Span`] or [`Event`] that has a field with that name.
/// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`.
/// - `value` matches on the value of a span's field. If a value is a numeric literal or a bool,
-/// it will match _only_ on that value. Otherwise, this filter acts as a regex on
-/// the `std::fmt::Debug` output from the value.
+/// it will match _only_ on that value. Otherwise, this filter matches the
+/// [`std::fmt::Debug`] output from the value.
/// - `level` sets a maximum verbosity level accepted by this directive.
///
+/// When a field value directive (`[{<FIELD NAME>=<FIELD_VALUE>}]=...`) matches a
+/// value's [`std::fmt::Debug`] output (i.e., the field value in the directive
+/// is not a `bool`, `i64`, `u64`, or `f64` literal), the matched pattern may be
+/// interpreted as either a regular expression or as the precise expected
+/// output of the field's [`std::fmt::Debug`] implementation. By default, these
+/// filters are interpreted as regular expressions, but this can be disabled
+/// using the [`Builder::with_regex`] builder method to use precise matching
+/// instead.
+///
+/// When field value filters are interpreted as regular expressions, the
+/// [`regex-automata` crate's regular expression syntax][re-syntax] is
+/// supported.
+///
+/// **Note**: When filters are constructed from potentially untrusted inputs,
+/// [disabling regular expression matching](Builder::with_regex) is strongly
+/// recommended.
+///
/// ## Usage Notes
///
/// - The portion of the directive which is included within the square brackets is `tracing`-specific.
@@ -72,7 +101,7 @@ use tracing_core::{
/// - A dash in a target will only appear when being specified explicitly:
/// `tracing::info!(target: "target-name", ...);`
///
-/// ## Examples
+/// ## Example Syntax
///
/// - `tokio::net=info` will enable all spans or events that:
/// - have the `tokio::net` target,
@@ -89,10 +118,68 @@ use tracing_core::{
/// - which has a field named `name` with value `bob`,
/// - at _any_ level.
///
-/// The [`Targets`] type implements a similar form of filtering, but without the
-/// ability to dynamically enable events based on the current span context, and
-/// without filtering on field values. When these features are not required,
-/// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
+/// # Examples
+///
+/// Parsing an `EnvFilter` from the [default environment
+/// variable](EnvFilter::from_default_env) (`RUST_LOG`):
+///
+/// ```
+/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
+///
+/// tracing_subscriber::registry()
+/// .with(fmt::layer())
+/// .with(EnvFilter::from_default_env())
+/// .init();
+/// ```
+///
+/// Parsing an `EnvFilter` [from a user-provided environment
+/// variable](EnvFilter::from_env):
+///
+/// ```
+/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
+///
+/// tracing_subscriber::registry()
+/// .with(fmt::layer())
+/// .with(EnvFilter::from_env("MYAPP_LOG"))
+/// .init();
+/// ```
+///
+/// Using `EnvFilter` as a [per-layer filter][plf] to filter only a single
+/// [`Layer`]:
+///
+/// ```
+/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
+///
+/// // Parse an `EnvFilter` configuration from the `RUST_LOG`
+/// // environment variable.
+/// let filter = EnvFilter::from_default_env();
+///
+/// // Apply the filter to this layer *only*.
+/// let filtered_layer = fmt::layer().with_filter(filter);
+///
+/// // Some other layer, whose output we don't want to filter.
+/// let unfiltered_layer = // ...
+/// # fmt::layer();
+///
+/// tracing_subscriber::registry()
+/// .with(filtered_layer)
+/// .with(unfiltered_layer)
+/// .init();
+/// ```
+/// # Constructing `EnvFilter`s
+///
+/// An `EnvFilter` is be constructed by parsing a string containing one or more
+/// directives. The [`EnvFilter::new`] constructor parses an `EnvFilter` from a
+/// string, ignoring any invalid directives, while [`EnvFilter::try_new`]
+/// returns an error if invalid directives are encountered. Similarly, the
+/// [`EnvFilter::from_env`] and [`EnvFilter::try_from_env`] constructors parse
+/// an `EnvFilter` from the value of the provided environment variable, with
+/// lossy and strict validation, respectively.
+///
+/// A [builder](EnvFilter::builder) interface is available to set additional
+/// configuration options prior to parsing an `EnvFilter`. See the [`Builder`
+/// type's documentation](Builder) for details on the options that can be
+/// configured using the builder.
///
/// [`Span`]: tracing_core::span
/// [fields]: tracing_core::Field
@@ -100,6 +187,11 @@ use tracing_core::{
/// [`level`]: tracing_core::Level
/// [`Metadata`]: tracing_core::Metadata
/// [`Targets`]: crate::filter::Targets
+/// [`env_logger`]: https://crates.io/crates/env_logger
+/// [`Filter`]: #impl-Filter<S>
+/// [global]: crate::layer#global-filtering
+/// [plf]: crate::layer#per-layer-filtering
+/// [filtering]: crate::layer#filtering-with-layers
#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))]
#[derive(Debug)]
pub struct EnvFilter {
@@ -108,10 +200,8 @@ pub struct EnvFilter {
has_dynamics: bool,
by_id: RwLock<HashMap<span::Id, directive::SpanMatcher>>,
by_cs: RwLock<HashMap<callsite::Identifier, directive::CallsiteMatcher>>,
-}
-
-thread_local! {
- static SCOPE: RefCell<Vec<LevelFilter>> = RefCell::new(Vec::new());
+ scope: ThreadLocal<RefCell<Vec<LevelFilter>>>,
+ regex: bool,
}
type FieldMap<T> = HashMap<Field, T>;
@@ -134,58 +224,181 @@ impl EnvFilter {
/// `RUST_LOG` is the default environment variable used by
/// [`EnvFilter::from_default_env`] and [`EnvFilter::try_from_default_env`].
///
- /// [`EnvFilter::from_default_env`]: #method.from_default_env
- /// [`EnvFilter::try_from_default_env`]: #method.try_from_default_env
+ /// [`EnvFilter::from_default_env`]: EnvFilter::from_default_env()
+ /// [`EnvFilter::try_from_default_env`]: EnvFilter::try_from_default_env()
pub const DEFAULT_ENV: &'static str = "RUST_LOG";
+ // === constructors, etc ===
+
+ /// Returns a [builder] that can be used to configure a new [`EnvFilter`]
+ /// instance.
+ ///
+ /// The [`Builder`] type is used to set additional configurations, such as
+ /// [whether regular expressions are enabled](Builder::with_regex) or [the
+ /// default directive](Builder::with_default_directive) before parsing an
+ /// [`EnvFilter`] from a string or environment variable.
+ ///
+ /// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html
+ pub fn builder() -> Builder {
+ Builder::default()
+ }
+
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
/// variable, ignoring any invalid filter directives.
+ ///
+ /// If the environment variable is empty or not set, or if it contains only
+ /// invalid directives, a default directive enabling the [`ERROR`] level is
+ /// added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> EnvFilter {
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .from_env_lossy()
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
pub fn from_default_env() -> Self {
- Self::from_env(Self::DEFAULT_ENV)
+ Self::builder()
+ .with_default_directive(LevelFilter::ERROR.into())
+ .from_env_lossy()
}
/// Returns a new `EnvFilter` from the value of the given environment
/// variable, ignoring any invalid filter directives.
+ ///
+ /// If the environment variable is empty or not set, or if it contains only
+ /// invalid directives, a default directive enabling the [`ERROR`] level is
+ /// added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> EnvFilter {
+ /// # let env = "";
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .with_env_var(env)
+ /// .from_env_lossy()
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
pub fn from_env<A: AsRef<str>>(env: A) -> Self {
- env::var(env.as_ref()).map(Self::new).unwrap_or_default()
+ Self::builder()
+ .with_default_directive(LevelFilter::ERROR.into())
+ .with_env_var(env.as_ref())
+ .from_env_lossy()
}
/// Returns a new `EnvFilter` from the directives in the given string,
/// ignoring any that are invalid.
- pub fn new<S: AsRef<str>>(dirs: S) -> Self {
- let directives = dirs.as_ref().split(',').filter_map(|s| match s.parse() {
- Ok(d) => Some(d),
- Err(err) => {
- eprintln!("ignoring `{}`: {}", s, err);
- None
- }
- });
- Self::from_directives(directives)
+ ///
+ /// If the string is empty or contains only invalid directives, a default
+ /// directive enabling the [`ERROR`] level is added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> EnvFilter {
+ /// # let directives = "";
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .parse_lossy(directives)
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
+ pub fn new<S: AsRef<str>>(directives: S) -> Self {
+ Self::builder()
+ .with_default_directive(LevelFilter::ERROR.into())
+ .parse_lossy(directives)
}
/// Returns a new `EnvFilter` from the directives in the given string,
/// or an error if any are invalid.
+ ///
+ /// If the string is empty, a default directive enabling the [`ERROR`] level
+ /// is added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::ParseError> {
+ /// # let directives = "";
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .parse(directives)
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
pub fn try_new<S: AsRef<str>>(dirs: S) -> Result<Self, directive::ParseError> {
- let directives = dirs
- .as_ref()
- .split(',')
- .map(|s| s.parse())
- .collect::<Result<Vec<_>, _>>()?;
- Ok(Self::from_directives(directives))
+ Self::builder().parse(dirs)
}
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
- /// variable, or an error if the environment variable contains any invalid
- /// filter directives.
+ /// variable, or an error if the environment variable is unset or contains
+ /// any invalid filter directives.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::EnvFilter;
+ ///
+ /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::FromEnvError> {
+ /// EnvFilter::builder().try_from_env()
+ /// # }
+ /// ```
pub fn try_from_default_env() -> Result<Self, FromEnvError> {
- Self::try_from_env(Self::DEFAULT_ENV)
+ Self::builder().try_from_env()
}
/// Returns a new `EnvFilter` from the value of the given environment
/// variable, or an error if the environment variable is unset or contains
/// any invalid filter directives.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::EnvFilter;
+ ///
+ /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::FromEnvError> {
+ /// # let env = "";
+ /// EnvFilter::builder().with_env_var(env).try_from_env()
+ /// # }
+ /// ```
pub fn try_from_env<A: AsRef<str>>(env: A) -> Result<Self, FromEnvError> {
- env::var(env.as_ref())?.parse().map_err(Into::into)
+ Self::builder().with_env_var(env.as_ref()).try_from_env()
}
/// Add a filtering directive to this `EnvFilter`.
@@ -202,13 +415,13 @@ impl EnvFilter {
/// and events as a previous filter, but sets a different level for those
/// spans and events, the previous directive is overwritten.
///
- /// [`LevelFilter`]: ../filter/struct.LevelFilter.html
- /// [`Level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html
+ /// [`LevelFilter`]: super::LevelFilter
+ /// [`Level`]: tracing_core::Level
///
/// # Examples
///
/// From [`LevelFilter`]:
- ////
+ ///
/// ```rust
/// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
/// let mut filter = EnvFilter::from_default_env()
@@ -223,9 +436,9 @@ impl EnvFilter {
/// let mut filter = EnvFilter::from_default_env()
/// .add_directive(Level::INFO.into());
/// ```
- ////
+ ///
/// Parsed from a string:
- ////
+ ///
/// ```rust
/// use tracing_subscriber::filter::{EnvFilter, Directive};
///
@@ -236,7 +449,15 @@ impl EnvFilter {
/// # Ok(())
/// # }
/// ```
- pub fn add_directive(mut self, directive: Directive) -> Self {
+ /// In the above example, substitute `my_crate`, `module`, etc. with the
+ /// name your target crate/module is imported with. This might be
+ /// different from the package name in Cargo.toml (`-` is replaced by `_`).
+ /// Example, if the package name in your Cargo.toml is `MY-FANCY-LIB`, then
+ /// the corresponding Rust identifier would be `MY_FANCY_LIB`:
+ pub fn add_directive(mut self, mut directive: Directive) -> Self {
+ if !self.regex {
+ directive.deregexify();
+ }
if let Some(stat) = directive.to_static() {
self.statics.add(stat)
} else {
@@ -246,165 +467,19 @@ impl EnvFilter {
self
}
- fn from_directives(directives: impl IntoIterator<Item = Directive>) -> Self {
- use tracing::level_filters::STATIC_MAX_LEVEL;
- use tracing::Level;
-
- let directives: Vec<_> = directives.into_iter().collect();
-
- let disabled: Vec<_> = directives
- .iter()
- .filter(|directive| directive.level > STATIC_MAX_LEVEL)
- .collect();
-
- if !disabled.is_empty() {
- #[cfg(feature = "ansi_term")]
- use ansi_term::{Color, Style};
- // NOTE: We can't use a configured `MakeWriter` because the EnvFilter
- // has no knowledge of any underlying subscriber or subscriber, which
- // may or may not use a `MakeWriter`.
- let warn = |msg: &str| {
- #[cfg(not(feature = "ansi_term"))]
- let msg = format!("warning: {}", msg);
- #[cfg(feature = "ansi_term")]
- let msg = {
- let bold = Style::new().bold();
- let mut warning = Color::Yellow.paint("warning");
- warning.style_ref_mut().is_bold = true;
- format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg))
- };
- eprintln!("{}", msg);
- };
- let ctx_prefixed = |prefix: &str, msg: &str| {
- #[cfg(not(feature = "ansi_term"))]
- let msg = format!("note: {}", msg);
- #[cfg(feature = "ansi_term")]
- let msg = {
- let mut equal = Color::Fixed(21).paint("="); // dark blue
- equal.style_ref_mut().is_bold = true;
- format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg)
- };
- eprintln!("{}", msg);
- };
- let ctx_help = |msg| ctx_prefixed("help:", msg);
- let ctx_note = |msg| ctx_prefixed("note:", msg);
- let ctx = |msg: &str| {
- #[cfg(not(feature = "ansi_term"))]
- let msg = format!("note: {}", msg);
- #[cfg(feature = "ansi_term")]
- let msg = {
- let mut pipe = Color::Fixed(21).paint("|");
- pipe.style_ref_mut().is_bold = true;
- format!(" {} {}", pipe, msg)
- };
- eprintln!("{}", msg);
- };
- warn("some trace filter directives would enable traces that are disabled statically");
- for directive in disabled {
- let target = if let Some(target) = &directive.target {
- format!("the `{}` target", target)
- } else {
- "all targets".into()
- };
- let level = directive
- .level
- .into_level()
- .expect("=off would not have enabled any filters");
- ctx(&format!(
- "`{}` would enable the {} level for {}",
- directive, level, target
- ));
- }
- ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL));
- let help_msg = || {
- let (feature, filter) = match STATIC_MAX_LEVEL.into_level() {
- Some(Level::TRACE) => unreachable!(
- "if the max level is trace, no static filtering features are enabled"
- ),
- Some(Level::DEBUG) => ("max_level_debug", Level::TRACE),
- Some(Level::INFO) => ("max_level_info", Level::DEBUG),
- Some(Level::WARN) => ("max_level_warn", Level::INFO),
- Some(Level::ERROR) => ("max_level_error", Level::WARN),
- None => return ("max_level_off", String::new()),
- };
- (feature, format!("{} ", filter))
- };
- let (feature, earlier_level) = help_msg();
- ctx_help(&format!(
- "to enable {}logging, remove the `{}` feature",
- earlier_level, feature
- ));
- }
-
- let (dynamics, mut statics) = Directive::make_tables(directives);
- let has_dynamics = !dynamics.is_empty();
-
- if statics.is_empty() && !has_dynamics {
- statics.add(directive::StaticDirective::default());
- }
-
- Self {
- statics,
- dynamics,
- has_dynamics,
- by_id: RwLock::new(HashMap::new()),
- by_cs: RwLock::new(HashMap::new()),
- }
- }
-
- fn cares_about_span(&self, span: &span::Id) -> bool {
- let spans = try_lock!(self.by_id.read(), else return false);
- spans.contains_key(span)
- }
-
- fn base_interest(&self) -> Interest {
- if self.has_dynamics {
- Interest::sometimes()
- } else {
- Interest::never()
- }
- }
-}
-
-impl<S: Subscriber> Layer<S> for EnvFilter {
- fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
- if self.has_dynamics && metadata.is_span() {
- // If this metadata describes a span, first, check if there is a
- // dynamic filter that should be constructed for it. If so, it
- // should always be enabled, since it influences filtering.
- if let Some(matcher) = self.dynamics.matcher(metadata) {
- let mut by_cs = try_lock!(self.by_cs.write(), else return self.base_interest());
- by_cs.insert(metadata.callsite(), matcher);
- return Interest::always();
- }
- }
-
- // Otherwise, check if any of our static filters enable this metadata.
- if self.statics.enabled(metadata) {
- Interest::always()
- } else {
- self.base_interest()
- }
- }
-
- fn max_level_hint(&self) -> Option<LevelFilter> {
- if self.dynamics.has_value_filters() {
- // If we perform any filtering on span field *values*, we will
- // enable *all* spans, because their field values are not known
- // until recording.
- return Some(LevelFilter::TRACE);
- }
- std::cmp::max(
- self.statics.max_level.into(),
- self.dynamics.max_level.into(),
- )
- }
+ // === filtering methods ===
- fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
+ /// Returns `true` if this `EnvFilter` would enable the provided `metadata`
+ /// in the current context.
+ ///
+ /// This is equivalent to calling the [`Layer::enabled`] or
+ /// [`Filter::enabled`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn enabled<S>(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
let level = metadata.level();
// is it possible for a dynamic filter directive to enable this event?
- // if not, we can avoid the thread local access + iterating over the
+ // if not, we can avoid the thread loca'l access + iterating over the
// spans in the current scope.
if self.has_dynamics && self.dynamics.max_level >= *level {
if metadata.is_span() {
@@ -420,14 +495,15 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
}
}
- let enabled_by_scope = SCOPE.with(|scope| {
- for filter in scope.borrow().iter() {
+ let enabled_by_scope = {
+ let scope = self.scope.get_or_default().borrow();
+ for filter in &*scope {
if filter >= level {
return true;
}
}
false
- });
+ };
if enabled_by_scope {
return true;
}
@@ -443,7 +519,33 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
false
}
- fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
+ /// Returns an optional hint of the highest [verbosity level][level] that
+ /// this `EnvFilter` will enable.
+ ///
+ /// This is equivalent to calling the [`Layer::max_level_hint`] or
+ /// [`Filter::max_level_hint`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ ///
+ /// [level]: tracing_core::metadata::Level
+ pub fn max_level_hint(&self) -> Option<LevelFilter> {
+ if self.dynamics.has_value_filters() {
+ // If we perform any filtering on span field *values*, we will
+ // enable *all* spans, because their field values are not known
+ // until recording.
+ return Some(LevelFilter::TRACE);
+ }
+ std::cmp::max(
+ self.statics.max_level.into(),
+ self.dynamics.max_level.into(),
+ )
+ }
+
+ /// Informs the filter that a new span was created.
+ ///
+ /// This is equivalent to calling the [`Layer::on_new_span`] or
+ /// [`Filter::on_new_span`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_new_span<S>(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
let by_cs = try_lock!(self.by_cs.read());
if let Some(cs) = by_cs.get(&attrs.metadata().callsite()) {
let span = cs.to_span_match(attrs);
@@ -451,28 +553,37 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
}
}
- fn on_record(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) {
- if let Some(span) = try_lock!(self.by_id.read()).get(id) {
- span.record_update(values);
- }
- }
-
- fn on_enter(&self, id: &span::Id, _: Context<'_, S>) {
+ /// Informs the filter that the span with the provided `id` was entered.
+ ///
+ /// This is equivalent to calling the [`Layer::on_enter`] or
+ /// [`Filter::on_enter`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_enter<S>(&self, id: &span::Id, _: Context<'_, S>) {
// XXX: This is where _we_ could push IDs to the stack instead, and use
// that to allow changing the filter while a span is already entered.
// But that might be much less efficient...
if let Some(span) = try_lock!(self.by_id.read()).get(id) {
- SCOPE.with(|scope| scope.borrow_mut().push(span.level()));
+ self.scope.get_or_default().borrow_mut().push(span.level());
}
}
- fn on_exit(&self, id: &span::Id, _: Context<'_, S>) {
+ /// Informs the filter that the span with the provided `id` was exited.
+ ///
+ /// This is equivalent to calling the [`Layer::on_exit`] or
+ /// [`Filter::on_exit`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_exit<S>(&self, id: &span::Id, _: Context<'_, S>) {
if self.cares_about_span(id) {
- SCOPE.with(|scope| scope.borrow_mut().pop());
+ self.scope.get_or_default().borrow_mut().pop();
}
}
- fn on_close(&self, id: span::Id, _: Context<'_, S>) {
+ /// Informs the filter that the span with the provided `id` was closed.
+ ///
+ /// This is equivalent to calling the [`Layer::on_close`] or
+ /// [`Filter::on_close`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_close<S>(&self, id: span::Id, _: Context<'_, S>) {
// If we don't need to acquire a write lock, avoid doing so.
if !self.cares_about_span(&id) {
return;
@@ -481,6 +592,140 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
let mut spans = try_lock!(self.by_id.write());
spans.remove(&id);
}
+
+ /// Informs the filter that the span with the provided `id` recorded the
+ /// provided field `values`.
+ ///
+ /// This is equivalent to calling the [`Layer::on_record`] or
+ /// [`Filter::on_record`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope
+ pub fn on_record<S>(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) {
+ if let Some(span) = try_lock!(self.by_id.read()).get(id) {
+ span.record_update(values);
+ }
+ }
+
+ fn cares_about_span(&self, span: &span::Id) -> bool {
+ let spans = try_lock!(self.by_id.read(), else return false);
+ spans.contains_key(span)
+ }
+
+ fn base_interest(&self) -> Interest {
+ if self.has_dynamics {
+ Interest::sometimes()
+ } else {
+ Interest::never()
+ }
+ }
+
+ fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
+ if self.has_dynamics && metadata.is_span() {
+ // If this metadata describes a span, first, check if there is a
+ // dynamic filter that should be constructed for it. If so, it
+ // should always be enabled, since it influences filtering.
+ if let Some(matcher) = self.dynamics.matcher(metadata) {
+ let mut by_cs = try_lock!(self.by_cs.write(), else return self.base_interest());
+ by_cs.insert(metadata.callsite(), matcher);
+ return Interest::always();
+ }
+ }
+
+ // Otherwise, check if any of our static filters enable this metadata.
+ if self.statics.enabled(metadata) {
+ Interest::always()
+ } else {
+ self.base_interest()
+ }
+ }
+}
+
+impl<S: Subscriber> Layer<S> for EnvFilter {
+ #[inline]
+ fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
+ EnvFilter::register_callsite(self, metadata)
+ }
+
+ #[inline]
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ EnvFilter::max_level_hint(self)
+ }
+
+ #[inline]
+ fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
+ self.enabled(metadata, ctx)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+ self.on_record(id, values, ctx);
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_enter(id, ctx);
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_exit(id, ctx);
+ }
+
+ #[inline]
+ fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+ self.on_close(id, ctx);
+ }
+}
+
+feature! {
+ #![all(feature = "registry", feature = "std")]
+ use crate::layer::Filter;
+
+ impl<S> Filter<S> for EnvFilter {
+ #[inline]
+ fn enabled(&self, meta: &Metadata<'_>, ctx: &Context<'_, S>) -> bool {
+ self.enabled(meta, ctx.clone())
+ }
+
+ #[inline]
+ fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
+ self.register_callsite(meta)
+ }
+
+ #[inline]
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ EnvFilter::max_level_hint(self)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+ self.on_record(id, values, ctx);
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_enter(id, ctx);
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_exit(id, ctx);
+ }
+
+ #[inline]
+ fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+ self.on_close(id, ctx);
+ }
+ }
}
impl FromStr for EnvFilter {
@@ -502,7 +747,7 @@ where
impl Default for EnvFilter {
fn default() -> Self {
- Self::from_directives(std::iter::empty())
+ Builder::default().from_directives(std::iter::empty())
}
}
@@ -735,4 +980,12 @@ mod tests {
[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug",
);
}
+
+ #[test]
+ fn parse_empty_string() {
+ // There is no corresponding test for [`Builder::parse_lossy`] as failed
+ // parsing does not produce any observable side effects. If this test fails
+ // check that [`Builder::parse_lossy`] is behaving correctly as well.
+ assert!(EnvFilter::builder().parse("").is_ok());
+ }
}