summaryrefslogtreecommitdiffstats
path: root/vendor/tracing-subscriber/src/filter/env/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-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
1 files changed, 465 insertions, 212 deletions
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());
+ }
}