summaryrefslogtreecommitdiffstats
path: root/vendor/tracing-subscriber/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:41 +0000
commit4f9fe856a25ab29345b90e7725509e9ee38a37be (patch)
treee4ffd8a9374cae7b21f7cbfb352927e0e074aff6 /vendor/tracing-subscriber/src
parentAdding upstream version 1.68.2+dfsg1. (diff)
downloadrustc-upstream/1.69.0+dfsg1.tar.xz
rustc-upstream/1.69.0+dfsg1.zip
Adding upstream version 1.69.0+dfsg1.upstream/1.69.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--vendor/tracing-subscriber/src/field/debug.rs (renamed from vendor/tracing-subscriber-0.3.3/src/field/debug.rs)0
-rw-r--r--vendor/tracing-subscriber/src/field/delimited.rs (renamed from vendor/tracing-subscriber-0.3.3/src/field/delimited.rs)4
-rw-r--r--vendor/tracing-subscriber/src/field/display.rs (renamed from vendor/tracing-subscriber-0.3.3/src/field/display.rs)2
-rw-r--r--vendor/tracing-subscriber/src/field/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/field/mod.rs)14
-rw-r--r--vendor/tracing-subscriber/src/filter/directive.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/directive.rs)34
-rw-r--r--vendor/tracing-subscriber/src/filter/env/builder.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
-rw-r--r--vendor/tracing-subscriber/src/filter/filter_fn.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs)0
-rw-r--r--vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs542
-rw-r--r--vendor/tracing-subscriber/src/filter/layer_filters/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs)454
-rw-r--r--vendor/tracing-subscriber/src/filter/level.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/level.rs)0
-rw-r--r--vendor/tracing-subscriber/src/filter/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/mod.rs)0
-rw-r--r--vendor/tracing-subscriber/src/filter/targets.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/targets.rs)104
-rw-r--r--vendor/tracing-subscriber/src/fmt/fmt_layer.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/fmt_layer.rs)321
-rw-r--r--vendor/tracing-subscriber/src/fmt/format/json.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/format/json.rs)175
-rw-r--r--vendor/tracing-subscriber/src/fmt/format/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/format/mod.rs)459
-rw-r--r--vendor/tracing-subscriber/src/fmt/format/pretty.rs511
-rw-r--r--vendor/tracing-subscriber/src/fmt/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/mod.rs)392
-rw-r--r--vendor/tracing-subscriber/src/fmt/time/datetime.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/time/datetime.rs)0
-rw-r--r--vendor/tracing-subscriber/src/fmt/time/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/time/mod.rs)8
-rw-r--r--vendor/tracing-subscriber/src/fmt/time/time_crate.rs470
-rw-r--r--vendor/tracing-subscriber/src/fmt/writer.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/writer.rs)30
-rw-r--r--vendor/tracing-subscriber/src/layer/context.rs (renamed from vendor/tracing-subscriber-0.3.3/src/layer/context.rs)22
-rw-r--r--vendor/tracing-subscriber/src/layer/layered.rs (renamed from vendor/tracing-subscriber-0.3.3/src/layer/layered.rs)107
-rw-r--r--vendor/tracing-subscriber/src/layer/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/layer/mod.rs)820
-rw-r--r--vendor/tracing-subscriber/src/layer/tests.rs (renamed from vendor/tracing-subscriber-0.3.3/src/layer/tests.rs)0
-rw-r--r--vendor/tracing-subscriber/src/lib.rs (renamed from vendor/tracing-subscriber-0.3.3/src/lib.rs)54
-rw-r--r--vendor/tracing-subscriber/src/macros.rs (renamed from vendor/tracing-subscriber-0.3.3/src/macros.rs)0
-rw-r--r--vendor/tracing-subscriber/src/prelude.rs (renamed from vendor/tracing-subscriber-0.3.3/src/prelude.rs)0
-rw-r--r--vendor/tracing-subscriber/src/registry/extensions.rs (renamed from vendor/tracing-subscriber-0.3.3/src/registry/extensions.rs)2
-rw-r--r--vendor/tracing-subscriber/src/registry/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/registry/mod.rs)20
-rw-r--r--vendor/tracing-subscriber/src/registry/sharded.rs (renamed from vendor/tracing-subscriber-0.3.3/src/registry/sharded.rs)34
-rw-r--r--vendor/tracing-subscriber/src/registry/stack.rs (renamed from vendor/tracing-subscriber-0.3.3/src/registry/stack.rs)0
-rw-r--r--vendor/tracing-subscriber/src/reload.rs384
-rw-r--r--vendor/tracing-subscriber/src/sync.rs (renamed from vendor/tracing-subscriber-0.3.3/src/sync.rs)0
-rw-r--r--vendor/tracing-subscriber/src/util.rs (renamed from vendor/tracing-subscriber-0.3.3/src/util.rs)0
38 files changed, 5588 insertions, 787 deletions
diff --git a/vendor/tracing-subscriber-0.3.3/src/field/debug.rs b/vendor/tracing-subscriber/src/field/debug.rs
index cc67d29fe..cc67d29fe 100644
--- a/vendor/tracing-subscriber-0.3.3/src/field/debug.rs
+++ b/vendor/tracing-subscriber/src/field/debug.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/field/delimited.rs b/vendor/tracing-subscriber/src/field/delimited.rs
index 8c78c4b20..98634cea9 100644
--- a/vendor/tracing-subscriber-0.3.3/src/field/delimited.rs
+++ b/vendor/tracing-subscriber/src/field/delimited.rs
@@ -41,7 +41,7 @@ impl<D, V> Delimited<D, V> {
/// Returns a new [`MakeVisitor`] implementation that wraps `inner` so that
/// it will format each visited field separated by the provided `delimiter`.
///
- /// [`MakeVisitor`]: ../trait.MakeVisitor.html
+ /// [`MakeVisitor`]: super::MakeVisitor
pub fn new(delimiter: D, inner: V) -> Self {
Self { delimiter, inner }
}
@@ -53,7 +53,7 @@ impl<D, V> VisitDelimited<D, V> {
/// Returns a new [`Visit`] implementation that wraps `inner` so that
/// each formatted field is separated by the provided `delimiter`.
///
- /// [`Visit`]: https://docs.rs/tracing-core/0.1.6/tracing_core/field/trait.Visit.html
+ /// [`Visit`]: tracing_core::field::Visit
pub fn new(delimiter: D, inner: V) -> Self {
Self {
delimiter,
diff --git a/vendor/tracing-subscriber-0.3.3/src/field/display.rs b/vendor/tracing-subscriber/src/field/display.rs
index e0bbc55ed..78a039ce1 100644
--- a/vendor/tracing-subscriber-0.3.3/src/field/display.rs
+++ b/vendor/tracing-subscriber/src/field/display.rs
@@ -18,7 +18,7 @@ impl<V> Messages<V> {
/// Returns a new [`MakeVisitor`] implementation that will wrap `inner` so
/// that any strings named `message` are formatted using `fmt::Display`.
///
- /// [`MakeVisitor`]: ../trait.MakeVisitor.html
+ /// [`MakeVisitor`]: super::MakeVisitor
pub fn new(inner: V) -> Self {
Messages(inner)
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/field/mod.rs b/vendor/tracing-subscriber/src/field/mod.rs
index f7d03f2cc..5dfddb362 100644
--- a/vendor/tracing-subscriber-0.3.3/src/field/mod.rs
+++ b/vendor/tracing-subscriber/src/field/mod.rs
@@ -22,7 +22,7 @@ pub mod display;
/// data to, configuration variables that determine the visitor's behavior, or
/// `()` when no input is required to produce a visitor.
///
-/// [visitors]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html
+/// [visitors]: tracing_core::field::Visit
pub trait MakeVisitor<T> {
/// The visitor type produced by this `MakeVisitor`.
type Visitor: Visit;
@@ -33,7 +33,7 @@ pub trait MakeVisitor<T> {
/// A [visitor] that produces output once it has visited a set of fields.
///
-/// [visitor]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html
+/// [visitor]: tracing_core::field::Visit
pub trait VisitOutput<Out>: Visit {
/// Completes the visitor, returning any output.
///
@@ -82,10 +82,10 @@ pub trait VisitOutput<Out>: Visit {
/// r.record(&mut visitor);
/// }
/// ```
-/// [visitor]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html
-/// [attr]: https://docs.rs/tracing-core/latest/tracing_core/span/struct.Attributes.html
-/// [rec]: https://docs.rs/tracing-core/latest/tracing_core/span/struct.Record.html
-/// [event]: https://docs.rs/tracing-core/latest/tracing_core/event/struct.Event.html
+/// [visitor]: tracing_core::field::Visit
+/// [attr]: tracing_core::span::Attributes
+/// [rec]: tracing_core::span::Record
+/// [event]: tracing_core::event::Event
pub trait RecordFields: crate::sealed::Sealed<RecordFieldsMarker> {
/// Record all the fields in `self` with the provided `visitor`.
fn record(&self, visitor: &mut dyn Visit);
@@ -341,7 +341,7 @@ pub(in crate::field) mod test_util {
impl<'a> Visit for DebugVisitor<'a> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
- write!(&mut self.writer, "{}={:?}", field, value).unwrap();
+ write!(self.writer, "{}={:?}", field, value).unwrap();
}
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/directive.rs b/vendor/tracing-subscriber/src/filter/directive.rs
index dd6b063c4..2ae3f0f24 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/directive.rs
+++ b/vendor/tracing-subscriber/src/filter/directive.rs
@@ -5,7 +5,7 @@ use alloc::vec;
use alloc::{string::String, vec::Vec};
use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr};
-use tracing_core::Metadata;
+use tracing_core::{Level, Metadata};
/// Indicates that a string could not be parsed as a filtering directive.
#[derive(Debug)]
pub struct ParseError {
@@ -142,6 +142,22 @@ impl DirectiveSet<StaticDirective> {
None => false,
}
}
+
+ /// Same as `enabled` above, but skips `Directive`'s with fields.
+ pub(crate) fn target_enabled(&self, target: &str, level: &Level) -> bool {
+ match self.directives_for_target(target).next() {
+ Some(d) => d.level >= *level,
+ None => false,
+ }
+ }
+
+ pub(crate) fn directives_for_target<'a>(
+ &'a self,
+ target: &'a str,
+ ) -> impl Iterator<Item = &'a StaticDirective> + 'a {
+ self.directives()
+ .filter(move |d| d.cares_about_target(target))
+ }
}
// === impl StaticDirective ===
@@ -158,6 +174,22 @@ impl StaticDirective {
level,
}
}
+
+ pub(in crate::filter) fn cares_about_target(&self, to_check: &str) -> bool {
+ // Does this directive have a target filter, and does it match the
+ // metadata's target?
+ if let Some(ref target) = self.target {
+ if !to_check.starts_with(&target[..]) {
+ return false;
+ }
+ }
+
+ if !self.field_names.is_empty() {
+ return false;
+ }
+
+ true
+ }
}
impl Ord for StaticDirective {
diff --git a/vendor/tracing-subscriber/src/filter/env/builder.rs b/vendor/tracing-subscriber/src/filter/env/builder.rs
new file mode 100644
index 000000000..c814707e6
--- /dev/null
+++ b/vendor/tracing-subscriber/src/filter/env/builder.rs
@@ -0,0 +1,325 @@
+use super::{
+ directive::{self, Directive},
+ EnvFilter, FromEnvError,
+};
+use crate::sync::RwLock;
+use std::env;
+use thread_local::ThreadLocal;
+use tracing::level_filters::STATIC_MAX_LEVEL;
+
+/// A [builder] for constructing new [`EnvFilter`]s.
+///
+/// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html
+#[derive(Debug, Clone)]
+#[must_use]
+pub struct Builder {
+ regex: bool,
+ env: Option<String>,
+ default_directive: Option<Directive>,
+}
+
+impl Builder {
+ /// Sets whether span field values can be matched with regular expressions.
+ ///
+ /// If this is `true`, field filter directives will be interpreted as
+ /// regular expressions if they are not able to be interpreted as a `bool`,
+ /// `i64`, `u64`, or `f64` literal. If this is `false,` those field values
+ /// will be interpreted as literal [`std::fmt::Debug`] output instead.
+ ///
+ /// By default, regular expressions are enabled.
+ ///
+ /// **Note**: when [`EnvFilter`]s are constructed from untrusted inputs,
+ /// disabling regular expressions is strongly encouraged.
+ pub fn with_regex(self, regex: bool) -> Self {
+ Self { regex, ..self }
+ }
+
+ /// Sets a default [filtering directive] that will be added to the filter if
+ /// the parsed string or environment variable contains no filter directives.
+ ///
+ /// By default, there is no default directive.
+ ///
+ /// # Examples
+ ///
+ /// If [`parse`], [`parse_lossy`], [`from_env`], or [`from_env_lossy`] are
+ /// called with an empty string or environment variable, the default
+ /// directive is used instead:
+ ///
+ /// ```rust
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::INFO.into())
+ /// .parse("")?;
+ ///
+ /// assert_eq!(format!("{}", filter), "info");
+ /// # Ok(()) }
+ /// ```
+ ///
+ /// Note that the `lossy` variants ([`parse_lossy`] and [`from_env_lossy`])
+ /// will ignore any invalid directives. If all directives in a filter
+ /// string or environment variable are invalid, those methods will also use
+ /// the default directive:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::INFO.into())
+ /// .parse_lossy("some_target=fake level,foo::bar=lolwut");
+ ///
+ /// assert_eq!(format!("{}", filter), "info");
+ /// ```
+ ///
+ ///
+ /// If the string or environment variable contains valid filtering
+ /// directives, the default directive is not used:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::INFO.into())
+ /// .parse_lossy("foo=trace");
+ ///
+ /// // The default directive is *not* used:
+ /// assert_eq!(format!("{}", filter), "foo=trace");
+ /// ```
+ ///
+ /// Parsing a more complex default directive from a string:
+ ///
+ /// ```rust
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// let default = "myapp=debug".parse()
+ /// .expect("hard-coded default directive should be valid");
+ ///
+ /// let filter = EnvFilter::builder()
+ /// .with_default_directive(default)
+ /// .parse("")?;
+ ///
+ /// assert_eq!(format!("{}", filter), "myapp=debug");
+ /// # Ok(()) }
+ /// ```
+ ///
+ /// [`parse_lossy`]: Self::parse_lossy
+ /// [`from_env_lossy`]: Self::from_env_lossy
+ /// [`parse`]: Self::parse
+ /// [`from_env`]: Self::from_env
+ pub fn with_default_directive(self, default_directive: Directive) -> Self {
+ Self {
+ default_directive: Some(default_directive),
+ ..self
+ }
+ }
+
+ /// Sets the name of the environment variable used by the [`from_env`],
+ /// [`from_env_lossy`], and [`try_from_env`] methods.
+ ///
+ /// By default, this is the value of [`EnvFilter::DEFAULT_ENV`]
+ /// (`RUST_LOG`).
+ ///
+ /// [`from_env`]: Self::from_env
+ /// [`from_env_lossy`]: Self::from_env_lossy
+ /// [`try_from_env`]: Self::try_from_env
+ pub fn with_env_var(self, var: impl ToString) -> Self {
+ Self {
+ env: Some(var.to_string()),
+ ..self
+ }
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the given string,
+ /// *ignoring* any that are invalid.
+ pub fn parse_lossy<S: AsRef<str>>(&self, dirs: S) -> EnvFilter {
+ let directives = dirs
+ .as_ref()
+ .split(',')
+ .filter(|s| !s.is_empty())
+ .filter_map(|s| match Directive::parse(s, self.regex) {
+ Ok(d) => Some(d),
+ Err(err) => {
+ eprintln!("ignoring `{}`: {}", s, err);
+ None
+ }
+ });
+ self.from_directives(directives)
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the given string,
+ /// or an error if any are invalid.
+ pub fn parse<S: AsRef<str>>(&self, dirs: S) -> Result<EnvFilter, directive::ParseError> {
+ let dirs = dirs.as_ref();
+ if dirs.is_empty() {
+ return Ok(self.from_directives(std::iter::empty()));
+ }
+ let directives = dirs
+ .split(',')
+ .filter(|s| !s.is_empty())
+ .map(|s| Directive::parse(s, self.regex))
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(self.from_directives(directives))
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the configured
+ /// environment variable, ignoring any directives that are invalid.
+ pub fn from_env_lossy(&self) -> EnvFilter {
+ let var = env::var(self.env_var_name()).unwrap_or_default();
+ self.parse_lossy(var)
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the in the configured
+ /// environment variable, or an error if the environment variable is not set
+ /// or contains invalid directives.
+ pub fn from_env(&self) -> Result<EnvFilter, FromEnvError> {
+ let var = env::var(self.env_var_name()).unwrap_or_default();
+ self.parse(var).map_err(Into::into)
+ }
+
+ /// Returns a new [`EnvFilter`] from the directives in the in the configured
+ /// environment variable, or an error if the environment variable is not set
+ /// or contains invalid directives.
+ pub fn try_from_env(&self) -> Result<EnvFilter, FromEnvError> {
+ let var = env::var(self.env_var_name())?;
+ self.parse(var).map_err(Into::into)
+ }
+
+ // TODO(eliza): consider making this a public API?
+ // Clippy doesn't love this naming, because it suggests that `from_` methods
+ // should not take a `Self`...but in this case, it's the `EnvFilter` that is
+ // being constructed "from" the directives, rather than the builder itself.
+ #[allow(clippy::wrong_self_convention)]
+ pub(super) fn from_directives(
+ &self,
+ directives: impl IntoIterator<Item = Directive>,
+ ) -> EnvFilter {
+ use tracing::Level;
+
+ let mut directives: Vec<_> = directives.into_iter().collect();
+ let mut disabled = Vec::new();
+ for directive in &mut directives {
+ if directive.level > STATIC_MAX_LEVEL {
+ disabled.push(directive.clone());
+ }
+ if !self.regex {
+ directive.deregexify();
+ }
+ }
+
+ if !disabled.is_empty() {
+ #[cfg(feature = "nu_ansi_term")]
+ use nu_ansi_term::{Color, Style};
+ // NOTE: We can't use a configured `MakeWriter` because the EnvFilter
+ // has no knowledge of any underlying subscriber or collector, which
+ // may or may not use a `MakeWriter`.
+ let warn = |msg: &str| {
+ #[cfg(not(feature = "nu_ansi_term"))]
+ let msg = format!("warning: {}", msg);
+ #[cfg(feature = "nu_ansi_term")]
+ let msg = {
+ let bold = Style::new().bold();
+ let mut warning = Color::Yellow.paint("warning");
+ warning.style_ref_mut().is_bold = true;
+ format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg))
+ };
+ eprintln!("{}", msg);
+ };
+ let ctx_prefixed = |prefix: &str, msg: &str| {
+ #[cfg(not(feature = "nu_ansi_term"))]
+ let msg = format!("{} {}", prefix, msg);
+ #[cfg(feature = "nu_ansi_term")]
+ let msg = {
+ let mut equal = Color::Fixed(21).paint("="); // dark blue
+ equal.style_ref_mut().is_bold = true;
+ format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg)
+ };
+ eprintln!("{}", msg);
+ };
+ let ctx_help = |msg| ctx_prefixed("help:", msg);
+ let ctx_note = |msg| ctx_prefixed("note:", msg);
+ let ctx = |msg: &str| {
+ #[cfg(not(feature = "nu_ansi_term"))]
+ let msg = format!("note: {}", msg);
+ #[cfg(feature = "nu_ansi_term")]
+ let msg = {
+ let mut pipe = Color::Fixed(21).paint("|");
+ pipe.style_ref_mut().is_bold = true;
+ format!(" {} {}", pipe, msg)
+ };
+ eprintln!("{}", msg);
+ };
+ warn("some trace filter directives would enable traces that are disabled statically");
+ for directive in disabled {
+ let target = if let Some(target) = &directive.target {
+ format!("the `{}` target", target)
+ } else {
+ "all targets".into()
+ };
+ let level = directive
+ .level
+ .into_level()
+ .expect("=off would not have enabled any filters");
+ ctx(&format!(
+ "`{}` would enable the {} level for {}",
+ directive, level, target
+ ));
+ }
+ ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL));
+ let help_msg = || {
+ let (feature, filter) = match STATIC_MAX_LEVEL.into_level() {
+ Some(Level::TRACE) => unreachable!(
+ "if the max level is trace, no static filtering features are enabled"
+ ),
+ Some(Level::DEBUG) => ("max_level_debug", Level::TRACE),
+ Some(Level::INFO) => ("max_level_info", Level::DEBUG),
+ Some(Level::WARN) => ("max_level_warn", Level::INFO),
+ Some(Level::ERROR) => ("max_level_error", Level::WARN),
+ None => return ("max_level_off", String::new()),
+ };
+ (feature, format!("{} ", filter))
+ };
+ let (feature, earlier_level) = help_msg();
+ ctx_help(&format!(
+ "to enable {}logging, remove the `{}` feature from the `tracing` crate",
+ earlier_level, feature
+ ));
+ }
+
+ let (dynamics, statics) = Directive::make_tables(directives);
+ let has_dynamics = !dynamics.is_empty();
+
+ let mut filter = EnvFilter {
+ statics,
+ dynamics,
+ has_dynamics,
+ by_id: RwLock::new(Default::default()),
+ by_cs: RwLock::new(Default::default()),
+ scope: ThreadLocal::new(),
+ regex: self.regex,
+ };
+
+ if !has_dynamics && filter.statics.is_empty() {
+ if let Some(ref default) = self.default_directive {
+ filter = filter.add_directive(default.clone());
+ }
+ }
+
+ filter
+ }
+
+ fn env_var_name(&self) -> &str {
+ self.env.as_deref().unwrap_or(EnvFilter::DEFAULT_ENV)
+ }
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Self {
+ regex: true,
+ env: None,
+ default_directive: None,
+ }
+ }
+}
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs b/vendor/tracing-subscriber/src/filter/env/directive.rs
index 66ca23dc4..f062e6ef9 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/env/directive.rs
+++ b/vendor/tracing-subscriber/src/filter/env/directive.rs
@@ -4,14 +4,14 @@ use crate::filter::{
env::{field, FieldMap},
level::LevelFilter,
};
-use lazy_static::lazy_static;
+use once_cell::sync::Lazy;
use regex::Regex;
use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
use tracing_core::{span, Level, Metadata};
/// A single filtering directive.
// TODO(eliza): add a builder for programmatically constructing directives?
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
pub struct Directive {
in_span: Option<String>,
@@ -107,80 +107,52 @@ impl Directive {
.collect();
(Dynamics::from_iter(dyns), statics)
}
-}
-
-impl Match for Directive {
- fn cares_about(&self, meta: &Metadata<'_>) -> bool {
- // Does this directive have a target filter, and does it match the
- // metadata's target?
- if let Some(ref target) = self.target {
- if !meta.target().starts_with(&target[..]) {
- return false;
- }
- }
-
- // Do we have a name filter, and does it match the metadata's name?
- // TODO(eliza): put name globbing here?
- if let Some(ref name) = self.in_span {
- if name != meta.name() {
- return false;
- }
- }
- // Does the metadata define all the fields that this directive cares about?
- let fields = meta.fields();
- for field in &self.fields {
- if fields.field(&field.name).is_none() {
- return false;
+ pub(super) fn deregexify(&mut self) {
+ for field in &mut self.fields {
+ field.value = match field.value.take() {
+ Some(field::ValueMatch::Pat(pat)) => {
+ Some(field::ValueMatch::Debug(pat.into_debug_match()))
+ }
+ x => x,
}
}
-
- true
- }
-
- fn level(&self) -> &LevelFilter {
- &self.level
}
-}
-impl FromStr for Directive {
- type Err = ParseError;
- fn from_str(from: &str) -> Result<Self, Self::Err> {
- lazy_static! {
- static ref DIRECTIVE_RE: Regex = Regex::new(
- r"(?x)
- ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
- # ^^^.
- # `note: we match log level names case-insensitively
- ^
- (?: # target name or span name
- (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
- ){1,2}
- (?: # level or nothing
- =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
- # ^^^.
- # `note: we match log level names case-insensitively
- )?
- $
- "
- )
- .unwrap();
- static ref SPAN_PART_RE: Regex =
- Regex::new(r#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap();
- static ref FIELD_FILTER_RE: Regex =
- // TODO(eliza): this doesn't _currently_ handle value matchers that include comma
- // characters. We should fix that.
- Regex::new(r#"(?x)
- (
- # field name
- [[:word:]][[[:word:]]\.]*
- # value part (optional)
- (?:=[^,]+)?
- )
- # trailing comma or EOS
- (?:,\s?|$)
- "#).unwrap();
- }
+ pub(super) fn parse(from: &str, regex: bool) -> Result<Self, ParseError> {
+ static DIRECTIVE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(
+ r"(?x)
+ ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
+ # ^^^.
+ # `note: we match log level names case-insensitively
+ ^
+ (?: # target name or span name
+ (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
+ ){1,2}
+ (?: # level or nothing
+ =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
+ # ^^^.
+ # `note: we match log level names case-insensitively
+ )?
+ $
+ "
+ )
+ .unwrap());
+ static SPAN_PART_RE: Lazy<Regex> =
+ Lazy::new(|| Regex::new(r#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap());
+ static FIELD_FILTER_RE: Lazy<Regex> =
+ // TODO(eliza): this doesn't _currently_ handle value matchers that include comma
+ // characters. We should fix that.
+ Lazy::new(|| Regex::new(r#"(?x)
+ (
+ # field name
+ [[:word:]][[[:word:]]\.]*
+ # value part (optional)
+ (?:=[^,]+)?
+ )
+ # trailing comma or EOS
+ (?:,\s?|$)
+ "#).unwrap());
let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?;
@@ -214,7 +186,7 @@ impl FromStr for Directive {
.map(|c| {
FIELD_FILTER_RE
.find_iter(c.as_str())
- .map(|c| c.as_str().parse())
+ .map(|c| field::Match::parse(c.as_str(), regex))
.collect::<Result<Vec<_>, _>>()
})
.unwrap_or_else(|| Ok(Vec::new()));
@@ -228,7 +200,7 @@ impl FromStr for Directive {
// Setting the target without the level enables every level for that target
.unwrap_or(LevelFilter::TRACE);
- Ok(Directive {
+ Ok(Self {
level,
target,
in_span,
@@ -237,6 +209,48 @@ impl FromStr for Directive {
}
}
+impl Match for Directive {
+ fn cares_about(&self, meta: &Metadata<'_>) -> bool {
+ // Does this directive have a target filter, and does it match the
+ // metadata's target?
+ if let Some(ref target) = self.target {
+ if !meta.target().starts_with(&target[..]) {
+ return false;
+ }
+ }
+
+ // Do we have a name filter, and does it match the metadata's name?
+ // TODO(eliza): put name globbing here?
+ if let Some(ref name) = self.in_span {
+ if name != meta.name() {
+ return false;
+ }
+ }
+
+ // Does the metadata define all the fields that this directive cares about?
+ let actual_fields = meta.fields();
+ for expected_field in &self.fields {
+ // Does the actual field set (from the metadata) contain this field?
+ if actual_fields.field(&expected_field.name).is_none() {
+ return false;
+ }
+ }
+
+ true
+ }
+
+ fn level(&self) -> &LevelFilter {
+ &self.level
+ }
+}
+
+impl FromStr for Directive {
+ type Err = ParseError;
+ fn from_str(from: &str) -> Result<Self, Self::Err> {
+ Directive::parse(from, true)
+ }
+}
+
impl Default for Directive {
fn default() -> Self {
Directive {
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs b/vendor/tracing-subscriber/src/filter/env/field.rs
index 970850f92..1394fd04a 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs
+++ b/vendor/tracing-subscriber/src/filter/env/field.rs
@@ -2,7 +2,7 @@ use matchers::Pattern;
use std::{
cmp::Ordering,
error::Error,
- fmt,
+ fmt::{self, Write},
str::FromStr,
sync::{
atomic::{AtomicBool, Ordering::*},
@@ -13,7 +13,7 @@ use std::{
use super::{FieldMap, LevelFilter};
use tracing_core::field::{Field, Visit};
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Match {
pub(crate) name: String, // TODO: allow match patterns for names?
pub(crate) value: Option<ValueMatch>,
@@ -38,11 +38,20 @@ pub(crate) struct MatchVisitor<'a> {
#[derive(Debug, Clone)]
pub(crate) enum ValueMatch {
+ /// Matches a specific `bool` value.
Bool(bool),
+ /// Matches a specific `f64` value.
F64(f64),
+ /// Matches a specific `u64` value.
U64(u64),
+ /// Matches a specific `i64` value.
I64(i64),
+ /// Matches any `NaN` `f64` value.
NaN,
+ /// Matches any field whose `fmt::Debug` output is equal to a fixed string.
+ Debug(MatchDebug),
+ /// Matches any field whose `fmt::Debug` output matches a regular expression
+ /// pattern.
Pat(Box<MatchPattern>),
}
@@ -97,6 +106,9 @@ impl Ord for ValueMatch {
(Pat(this), Pat(that)) => this.cmp(that),
(Pat(_), _) => Ordering::Greater,
+
+ (Debug(this), Debug(that)) => this.cmp(that),
+ (Debug(_), _) => Ordering::Greater,
}
}
}
@@ -107,12 +119,25 @@ impl PartialOrd for ValueMatch {
}
}
+/// Matches a field's `fmt::Debug` output against a regular expression pattern.
+///
+/// This is used for matching all non-literal field value filters when regular
+/// expressions are enabled.
#[derive(Debug, Clone)]
pub(crate) struct MatchPattern {
pub(crate) matcher: Pattern,
pattern: Arc<str>,
}
+/// Matches a field's `fmt::Debug` output against a fixed string pattern.
+///
+/// This is used for matching all non-literal field value filters when regular
+/// expressions are disabled.
+#[derive(Debug, Clone)]
+pub(crate) struct MatchDebug {
+ pattern: Arc<str>,
+}
+
/// Indicates that a field name specified in a filter directive was invalid.
#[derive(Clone, Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
@@ -122,9 +147,17 @@ pub struct BadName {
// === impl Match ===
-impl FromStr for Match {
- type Err = Box<dyn Error + Send + Sync>;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
+impl Match {
+ pub(crate) fn has_value(&self) -> bool {
+ self.value.is_some()
+ }
+
+ // TODO: reference count these strings?
+ pub(crate) fn name(&self) -> String {
+ self.name.clone()
+ }
+
+ pub(crate) fn parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>> {
let mut parts = s.split('=');
let name = parts
.next()
@@ -133,22 +166,17 @@ impl FromStr for Match {
})?
// TODO: validate field name
.to_string();
- let value = parts.next().map(ValueMatch::from_str).transpose()?;
+ let value = parts
+ .next()
+ .map(|part| match regex {
+ true => ValueMatch::parse_regex(part),
+ false => Ok(ValueMatch::parse_non_regex(part)),
+ })
+ .transpose()?;
Ok(Match { name, value })
}
}
-impl Match {
- pub(crate) fn has_value(&self) -> bool {
- self.value.is_some()
- }
-
- // TODO: reference count these strings?
- pub(crate) fn name(&self) -> String {
- self.name.clone()
- }
-}
-
impl fmt::Display for Match {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)?;
@@ -199,9 +227,14 @@ fn value_match_f64(v: f64) -> ValueMatch {
}
}
-impl FromStr for ValueMatch {
- type Err = matchers::Error;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
+impl ValueMatch {
+ /// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular
+ /// expressions.
+ ///
+ /// This returns an error if the string didn't contain a valid `bool`,
+ /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular
+ /// expression.
+ fn parse_regex(s: &str) -> Result<Self, matchers::Error> {
s.parse::<bool>()
.map(ValueMatch::Bool)
.or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
@@ -212,6 +245,21 @@ impl FromStr for ValueMatch {
.map(|p| ValueMatch::Pat(Box::new(p)))
})
}
+
+ /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed
+ /// string.
+ ///
+ /// This does *not* return an error, because any string that isn't a valid
+ /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected
+ /// `fmt::Debug` output.
+ fn parse_non_regex(s: &str) -> Self {
+ s.parse::<bool>()
+ .map(ValueMatch::Bool)
+ .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
+ .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
+ .or_else(|_| s.parse::<f64>().map(value_match_f64))
+ .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s)))
+ }
}
impl fmt::Display for ValueMatch {
@@ -222,6 +270,7 @@ impl fmt::Display for ValueMatch {
ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f),
ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
+ ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f),
}
}
@@ -264,6 +313,12 @@ impl MatchPattern {
fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
self.matcher.debug_matches(d)
}
+
+ pub(super) fn into_debug_match(self) -> MatchDebug {
+ MatchDebug {
+ pattern: self.pattern,
+ }
+ }
}
impl PartialEq for MatchPattern {
@@ -289,6 +344,102 @@ impl Ord for MatchPattern {
}
}
+// === impl MatchDebug ===
+
+impl MatchDebug {
+ fn new(s: &str) -> Self {
+ Self {
+ pattern: s.to_owned().into(),
+ }
+ }
+
+ #[inline]
+ fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
+ // Naively, we would probably match a value's `fmt::Debug` output by
+ // formatting it to a string, and then checking if the string is equal
+ // to the expected pattern. However, this would require allocating every
+ // time we want to match a field value against a `Debug` matcher, which
+ // can be avoided.
+ //
+ // Instead, we implement `fmt::Write` for a type that, rather than
+ // actually _writing_ the strings to something, matches them against the
+ // expected pattern, and returns an error if the pattern does not match.
+ struct Matcher<'a> {
+ pattern: &'a str,
+ }
+
+ impl fmt::Write for Matcher<'_> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ // If the string is longer than the remaining expected string,
+ // we know it won't match, so bail.
+ if s.len() > self.pattern.len() {
+ return Err(fmt::Error);
+ }
+
+ // If the expected string begins with the string that was
+ // written, we are still potentially a match. Advance the
+ // position in the expected pattern to chop off the matched
+ // output, and continue.
+ if self.pattern.starts_with(s) {
+ self.pattern = &self.pattern[s.len()..];
+ return Ok(());
+ }
+
+ // Otherwise, the expected string doesn't include the string
+ // that was written at the current position, so the `fmt::Debug`
+ // output doesn't match! Return an error signalling that this
+ // doesn't match.
+ Err(fmt::Error)
+ }
+ }
+ let mut matcher = Matcher {
+ pattern: &self.pattern,
+ };
+
+ // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This
+ // returns an error if the `fmt::Debug` implementation wrote any
+ // characters that did not match the expected pattern.
+ write!(matcher, "{:?}", d).is_ok()
+ }
+}
+
+impl fmt::Display for MatchDebug {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&*self.pattern, f)
+ }
+}
+
+impl AsRef<str> for MatchDebug {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ self.pattern.as_ref()
+ }
+}
+
+impl PartialEq for MatchDebug {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.pattern == other.pattern
+ }
+}
+
+impl Eq for MatchDebug {}
+
+impl PartialOrd for MatchDebug {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.pattern.cmp(&other.pattern))
+ }
+}
+
+impl Ord for MatchDebug {
+ #[inline]
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.pattern.cmp(&other.pattern)
+ }
+}
+
// === impl BadName ===
impl Error for BadName {}
@@ -401,6 +552,9 @@ impl<'a> Visit for MatchVisitor<'a> {
Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => {
matched.store(true, Release);
}
+ Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
+ matched.store(true, Release)
+ }
_ => {}
}
}
@@ -410,7 +564,63 @@ impl<'a> Visit for MatchVisitor<'a> {
Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => {
matched.store(true, Release);
}
+ Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
+ matched.store(true, Release)
+ }
_ => {}
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[derive(Debug)]
+ #[allow(dead_code)]
+ struct MyStruct {
+ answer: usize,
+ question: &'static str,
+ }
+
+ #[test]
+ fn debug_struct_match() {
+ let my_struct = MyStruct {
+ answer: 42,
+ question: "life, the universe, and everything",
+ };
+
+ let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
+
+ assert_eq!(
+ format!("{:?}", my_struct),
+ pattern,
+ "`MyStruct`'s `Debug` impl doesn't output the expected string"
+ );
+
+ let matcher = MatchDebug {
+ pattern: pattern.into(),
+ };
+ assert!(matcher.debug_matches(&my_struct))
+ }
+
+ #[test]
+ fn debug_struct_not_match() {
+ let my_struct = MyStruct {
+ answer: 42,
+ question: "what shall we have for lunch?",
+ };
+
+ let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
+
+ assert_eq!(
+ format!("{:?}", my_struct),
+ "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }",
+ "`MyStruct`'s `Debug` impl doesn't output the expected string"
+ );
+
+ let matcher = MatchDebug {
+ pattern: pattern.into(),
+ };
+ assert!(!matcher.debug_matches(&my_struct))
+ }
+}
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs b/vendor/tracing-subscriber/src/filter/env/mod.rs
index 81fe0e62d..81a9ae2bd 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/env/mod.rs
+++ b/vendor/tracing-subscriber/src/filter/env/mod.rs
@@ -4,7 +4,8 @@
// these are publicly re-exported, but the compiler doesn't realize
// that for some reason.
#[allow(unreachable_pub)]
-pub use self::{directive::Directive, field::BadName as BadFieldName};
+pub use self::{builder::Builder, directive::Directive, field::BadName as BadFieldName};
+mod builder;
mod directive;
mod field;
@@ -15,6 +16,7 @@ use crate::{
};
use directive::ParseError;
use std::{cell::RefCell, collections::HashMap, env, error::Error, fmt, str::FromStr};
+use thread_local::ThreadLocal;
use tracing_core::{
callsite,
field::Field,
@@ -26,6 +28,16 @@ use tracing_core::{
/// A [`Layer`] which filters spans and events based on a set of filter
/// directives.
///
+/// `EnvFilter` implements both the [`Layer`](#impl-Layer<S>) and [`Filter`] traits, so it may
+/// be used for both [global filtering][global] and [per-layer filtering][plf],
+/// respectively. See [the documentation on filtering with `Layer`s][filtering]
+/// for details.
+///
+/// The [`Targets`] type implements a similar form of filtering, but without the
+/// ability to dynamically enable events based on the current span context, and
+/// without filtering on field values. When these features are not required,
+/// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
+///
/// # Directives
///
/// A filter consists of one or more comma-separated directives which match on [`Span`]s and [`Event`]s.
@@ -52,10 +64,27 @@ use tracing_core::{
/// and will match on any [`Span`] or [`Event`] that has a field with that name.
/// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`.
/// - `value` matches on the value of a span's field. If a value is a numeric literal or a bool,
-/// it will match _only_ on that value. Otherwise, this filter acts as a regex on
-/// the `std::fmt::Debug` output from the value.
+/// it will match _only_ on that value. Otherwise, this filter matches the
+/// [`std::fmt::Debug`] output from the value.
/// - `level` sets a maximum verbosity level accepted by this directive.
///
+/// When a field value directive (`[{<FIELD NAME>=<FIELD_VALUE>}]=...`) matches a
+/// value's [`std::fmt::Debug`] output (i.e., the field value in the directive
+/// is not a `bool`, `i64`, `u64`, or `f64` literal), the matched pattern may be
+/// interpreted as either a regular expression or as the precise expected
+/// output of the field's [`std::fmt::Debug`] implementation. By default, these
+/// filters are interpreted as regular expressions, but this can be disabled
+/// using the [`Builder::with_regex`] builder method to use precise matching
+/// instead.
+///
+/// When field value filters are interpreted as regular expressions, the
+/// [`regex-automata` crate's regular expression syntax][re-syntax] is
+/// supported.
+///
+/// **Note**: When filters are constructed from potentially untrusted inputs,
+/// [disabling regular expression matching](Builder::with_regex) is strongly
+/// recommended.
+///
/// ## Usage Notes
///
/// - The portion of the directive which is included within the square brackets is `tracing`-specific.
@@ -72,7 +101,7 @@ use tracing_core::{
/// - A dash in a target will only appear when being specified explicitly:
/// `tracing::info!(target: "target-name", ...);`
///
-/// ## Examples
+/// ## Example Syntax
///
/// - `tokio::net=info` will enable all spans or events that:
/// - have the `tokio::net` target,
@@ -89,10 +118,68 @@ use tracing_core::{
/// - which has a field named `name` with value `bob`,
/// - at _any_ level.
///
-/// The [`Targets`] type implements a similar form of filtering, but without the
-/// ability to dynamically enable events based on the current span context, and
-/// without filtering on field values. When these features are not required,
-/// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
+/// # Examples
+///
+/// Parsing an `EnvFilter` from the [default environment
+/// variable](EnvFilter::from_default_env) (`RUST_LOG`):
+///
+/// ```
+/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
+///
+/// tracing_subscriber::registry()
+/// .with(fmt::layer())
+/// .with(EnvFilter::from_default_env())
+/// .init();
+/// ```
+///
+/// Parsing an `EnvFilter` [from a user-provided environment
+/// variable](EnvFilter::from_env):
+///
+/// ```
+/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
+///
+/// tracing_subscriber::registry()
+/// .with(fmt::layer())
+/// .with(EnvFilter::from_env("MYAPP_LOG"))
+/// .init();
+/// ```
+///
+/// Using `EnvFilter` as a [per-layer filter][plf] to filter only a single
+/// [`Layer`]:
+///
+/// ```
+/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
+///
+/// // Parse an `EnvFilter` configuration from the `RUST_LOG`
+/// // environment variable.
+/// let filter = EnvFilter::from_default_env();
+///
+/// // Apply the filter to this layer *only*.
+/// let filtered_layer = fmt::layer().with_filter(filter);
+///
+/// // Some other layer, whose output we don't want to filter.
+/// let unfiltered_layer = // ...
+/// # fmt::layer();
+///
+/// tracing_subscriber::registry()
+/// .with(filtered_layer)
+/// .with(unfiltered_layer)
+/// .init();
+/// ```
+/// # Constructing `EnvFilter`s
+///
+/// An `EnvFilter` is be constructed by parsing a string containing one or more
+/// directives. The [`EnvFilter::new`] constructor parses an `EnvFilter` from a
+/// string, ignoring any invalid directives, while [`EnvFilter::try_new`]
+/// returns an error if invalid directives are encountered. Similarly, the
+/// [`EnvFilter::from_env`] and [`EnvFilter::try_from_env`] constructors parse
+/// an `EnvFilter` from the value of the provided environment variable, with
+/// lossy and strict validation, respectively.
+///
+/// A [builder](EnvFilter::builder) interface is available to set additional
+/// configuration options prior to parsing an `EnvFilter`. See the [`Builder`
+/// type's documentation](Builder) for details on the options that can be
+/// configured using the builder.
///
/// [`Span`]: tracing_core::span
/// [fields]: tracing_core::Field
@@ -100,6 +187,11 @@ use tracing_core::{
/// [`level`]: tracing_core::Level
/// [`Metadata`]: tracing_core::Metadata
/// [`Targets`]: crate::filter::Targets
+/// [`env_logger`]: https://crates.io/crates/env_logger
+/// [`Filter`]: #impl-Filter<S>
+/// [global]: crate::layer#global-filtering
+/// [plf]: crate::layer#per-layer-filtering
+/// [filtering]: crate::layer#filtering-with-layers
#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))]
#[derive(Debug)]
pub struct EnvFilter {
@@ -108,10 +200,8 @@ pub struct EnvFilter {
has_dynamics: bool,
by_id: RwLock<HashMap<span::Id, directive::SpanMatcher>>,
by_cs: RwLock<HashMap<callsite::Identifier, directive::CallsiteMatcher>>,
-}
-
-thread_local! {
- static SCOPE: RefCell<Vec<LevelFilter>> = RefCell::new(Vec::new());
+ scope: ThreadLocal<RefCell<Vec<LevelFilter>>>,
+ regex: bool,
}
type FieldMap<T> = HashMap<Field, T>;
@@ -134,58 +224,181 @@ impl EnvFilter {
/// `RUST_LOG` is the default environment variable used by
/// [`EnvFilter::from_default_env`] and [`EnvFilter::try_from_default_env`].
///
- /// [`EnvFilter::from_default_env`]: #method.from_default_env
- /// [`EnvFilter::try_from_default_env`]: #method.try_from_default_env
+ /// [`EnvFilter::from_default_env`]: EnvFilter::from_default_env()
+ /// [`EnvFilter::try_from_default_env`]: EnvFilter::try_from_default_env()
pub const DEFAULT_ENV: &'static str = "RUST_LOG";
+ // === constructors, etc ===
+
+ /// Returns a [builder] that can be used to configure a new [`EnvFilter`]
+ /// instance.
+ ///
+ /// The [`Builder`] type is used to set additional configurations, such as
+ /// [whether regular expressions are enabled](Builder::with_regex) or [the
+ /// default directive](Builder::with_default_directive) before parsing an
+ /// [`EnvFilter`] from a string or environment variable.
+ ///
+ /// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html
+ pub fn builder() -> Builder {
+ Builder::default()
+ }
+
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
/// variable, ignoring any invalid filter directives.
+ ///
+ /// If the environment variable is empty or not set, or if it contains only
+ /// invalid directives, a default directive enabling the [`ERROR`] level is
+ /// added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> EnvFilter {
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .from_env_lossy()
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
pub fn from_default_env() -> Self {
- Self::from_env(Self::DEFAULT_ENV)
+ Self::builder()
+ .with_default_directive(LevelFilter::ERROR.into())
+ .from_env_lossy()
}
/// Returns a new `EnvFilter` from the value of the given environment
/// variable, ignoring any invalid filter directives.
+ ///
+ /// If the environment variable is empty or not set, or if it contains only
+ /// invalid directives, a default directive enabling the [`ERROR`] level is
+ /// added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> EnvFilter {
+ /// # let env = "";
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .with_env_var(env)
+ /// .from_env_lossy()
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
pub fn from_env<A: AsRef<str>>(env: A) -> Self {
- env::var(env.as_ref()).map(Self::new).unwrap_or_default()
+ Self::builder()
+ .with_default_directive(LevelFilter::ERROR.into())
+ .with_env_var(env.as_ref())
+ .from_env_lossy()
}
/// Returns a new `EnvFilter` from the directives in the given string,
/// ignoring any that are invalid.
- pub fn new<S: AsRef<str>>(dirs: S) -> Self {
- let directives = dirs.as_ref().split(',').filter_map(|s| match s.parse() {
- Ok(d) => Some(d),
- Err(err) => {
- eprintln!("ignoring `{}`: {}", s, err);
- None
- }
- });
- Self::from_directives(directives)
+ ///
+ /// If the string is empty or contains only invalid directives, a default
+ /// directive enabling the [`ERROR`] level is added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> EnvFilter {
+ /// # let directives = "";
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .parse_lossy(directives)
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
+ pub fn new<S: AsRef<str>>(directives: S) -> Self {
+ Self::builder()
+ .with_default_directive(LevelFilter::ERROR.into())
+ .parse_lossy(directives)
}
/// Returns a new `EnvFilter` from the directives in the given string,
/// or an error if any are invalid.
+ ///
+ /// If the string is empty, a default directive enabling the [`ERROR`] level
+ /// is added.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
+ ///
+ /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::ParseError> {
+ /// # let directives = "";
+ /// EnvFilter::builder()
+ /// .with_default_directive(LevelFilter::ERROR.into())
+ /// .parse(directives)
+ /// # }
+ /// ```
+ ///
+ /// [`ERROR`]: tracing::Level::ERROR
pub fn try_new<S: AsRef<str>>(dirs: S) -> Result<Self, directive::ParseError> {
- let directives = dirs
- .as_ref()
- .split(',')
- .map(|s| s.parse())
- .collect::<Result<Vec<_>, _>>()?;
- Ok(Self::from_directives(directives))
+ Self::builder().parse(dirs)
}
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
- /// variable, or an error if the environment variable contains any invalid
- /// filter directives.
+ /// variable, or an error if the environment variable is unset or contains
+ /// any invalid filter directives.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::EnvFilter;
+ ///
+ /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::FromEnvError> {
+ /// EnvFilter::builder().try_from_env()
+ /// # }
+ /// ```
pub fn try_from_default_env() -> Result<Self, FromEnvError> {
- Self::try_from_env(Self::DEFAULT_ENV)
+ Self::builder().try_from_env()
}
/// Returns a new `EnvFilter` from the value of the given environment
/// variable, or an error if the environment variable is unset or contains
/// any invalid filter directives.
+ ///
+ /// To set additional configuration options prior to parsing the filter, use
+ /// the [`Builder`] type instead.
+ ///
+ /// This function is equivalent to the following:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::EnvFilter;
+ ///
+ /// # fn docs() -> Result<EnvFilter, tracing_subscriber::filter::FromEnvError> {
+ /// # let env = "";
+ /// EnvFilter::builder().with_env_var(env).try_from_env()
+ /// # }
+ /// ```
pub fn try_from_env<A: AsRef<str>>(env: A) -> Result<Self, FromEnvError> {
- env::var(env.as_ref())?.parse().map_err(Into::into)
+ Self::builder().with_env_var(env.as_ref()).try_from_env()
}
/// Add a filtering directive to this `EnvFilter`.
@@ -202,13 +415,13 @@ impl EnvFilter {
/// and events as a previous filter, but sets a different level for those
/// spans and events, the previous directive is overwritten.
///
- /// [`LevelFilter`]: ../filter/struct.LevelFilter.html
- /// [`Level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html
+ /// [`LevelFilter`]: super::LevelFilter
+ /// [`Level`]: tracing_core::Level
///
/// # Examples
///
/// From [`LevelFilter`]:
- ////
+ ///
/// ```rust
/// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
/// let mut filter = EnvFilter::from_default_env()
@@ -223,9 +436,9 @@ impl EnvFilter {
/// let mut filter = EnvFilter::from_default_env()
/// .add_directive(Level::INFO.into());
/// ```
- ////
+ ///
/// Parsed from a string:
- ////
+ ///
/// ```rust
/// use tracing_subscriber::filter::{EnvFilter, Directive};
///
@@ -236,7 +449,15 @@ impl EnvFilter {
/// # Ok(())
/// # }
/// ```
- pub fn add_directive(mut self, directive: Directive) -> Self {
+ /// In the above example, substitute `my_crate`, `module`, etc. with the
+ /// name your target crate/module is imported with. This might be
+ /// different from the package name in Cargo.toml (`-` is replaced by `_`).
+ /// Example, if the package name in your Cargo.toml is `MY-FANCY-LIB`, then
+ /// the corresponding Rust identifier would be `MY_FANCY_LIB`:
+ pub fn add_directive(mut self, mut directive: Directive) -> Self {
+ if !self.regex {
+ directive.deregexify();
+ }
if let Some(stat) = directive.to_static() {
self.statics.add(stat)
} else {
@@ -246,165 +467,19 @@ impl EnvFilter {
self
}
- fn from_directives(directives: impl IntoIterator<Item = Directive>) -> Self {
- use tracing::level_filters::STATIC_MAX_LEVEL;
- use tracing::Level;
-
- let directives: Vec<_> = directives.into_iter().collect();
-
- let disabled: Vec<_> = directives
- .iter()
- .filter(|directive| directive.level > STATIC_MAX_LEVEL)
- .collect();
-
- if !disabled.is_empty() {
- #[cfg(feature = "ansi_term")]
- use ansi_term::{Color, Style};
- // NOTE: We can't use a configured `MakeWriter` because the EnvFilter
- // has no knowledge of any underlying subscriber or subscriber, which
- // may or may not use a `MakeWriter`.
- let warn = |msg: &str| {
- #[cfg(not(feature = "ansi_term"))]
- let msg = format!("warning: {}", msg);
- #[cfg(feature = "ansi_term")]
- let msg = {
- let bold = Style::new().bold();
- let mut warning = Color::Yellow.paint("warning");
- warning.style_ref_mut().is_bold = true;
- format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg))
- };
- eprintln!("{}", msg);
- };
- let ctx_prefixed = |prefix: &str, msg: &str| {
- #[cfg(not(feature = "ansi_term"))]
- let msg = format!("note: {}", msg);
- #[cfg(feature = "ansi_term")]
- let msg = {
- let mut equal = Color::Fixed(21).paint("="); // dark blue
- equal.style_ref_mut().is_bold = true;
- format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg)
- };
- eprintln!("{}", msg);
- };
- let ctx_help = |msg| ctx_prefixed("help:", msg);
- let ctx_note = |msg| ctx_prefixed("note:", msg);
- let ctx = |msg: &str| {
- #[cfg(not(feature = "ansi_term"))]
- let msg = format!("note: {}", msg);
- #[cfg(feature = "ansi_term")]
- let msg = {
- let mut pipe = Color::Fixed(21).paint("|");
- pipe.style_ref_mut().is_bold = true;
- format!(" {} {}", pipe, msg)
- };
- eprintln!("{}", msg);
- };
- warn("some trace filter directives would enable traces that are disabled statically");
- for directive in disabled {
- let target = if let Some(target) = &directive.target {
- format!("the `{}` target", target)
- } else {
- "all targets".into()
- };
- let level = directive
- .level
- .into_level()
- .expect("=off would not have enabled any filters");
- ctx(&format!(
- "`{}` would enable the {} level for {}",
- directive, level, target
- ));
- }
- ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL));
- let help_msg = || {
- let (feature, filter) = match STATIC_MAX_LEVEL.into_level() {
- Some(Level::TRACE) => unreachable!(
- "if the max level is trace, no static filtering features are enabled"
- ),
- Some(Level::DEBUG) => ("max_level_debug", Level::TRACE),
- Some(Level::INFO) => ("max_level_info", Level::DEBUG),
- Some(Level::WARN) => ("max_level_warn", Level::INFO),
- Some(Level::ERROR) => ("max_level_error", Level::WARN),
- None => return ("max_level_off", String::new()),
- };
- (feature, format!("{} ", filter))
- };
- let (feature, earlier_level) = help_msg();
- ctx_help(&format!(
- "to enable {}logging, remove the `{}` feature",
- earlier_level, feature
- ));
- }
-
- let (dynamics, mut statics) = Directive::make_tables(directives);
- let has_dynamics = !dynamics.is_empty();
-
- if statics.is_empty() && !has_dynamics {
- statics.add(directive::StaticDirective::default());
- }
-
- Self {
- statics,
- dynamics,
- has_dynamics,
- by_id: RwLock::new(HashMap::new()),
- by_cs: RwLock::new(HashMap::new()),
- }
- }
-
- fn cares_about_span(&self, span: &span::Id) -> bool {
- let spans = try_lock!(self.by_id.read(), else return false);
- spans.contains_key(span)
- }
-
- fn base_interest(&self) -> Interest {
- if self.has_dynamics {
- Interest::sometimes()
- } else {
- Interest::never()
- }
- }
-}
-
-impl<S: Subscriber> Layer<S> for EnvFilter {
- fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
- if self.has_dynamics && metadata.is_span() {
- // If this metadata describes a span, first, check if there is a
- // dynamic filter that should be constructed for it. If so, it
- // should always be enabled, since it influences filtering.
- if let Some(matcher) = self.dynamics.matcher(metadata) {
- let mut by_cs = try_lock!(self.by_cs.write(), else return self.base_interest());
- by_cs.insert(metadata.callsite(), matcher);
- return Interest::always();
- }
- }
-
- // Otherwise, check if any of our static filters enable this metadata.
- if self.statics.enabled(metadata) {
- Interest::always()
- } else {
- self.base_interest()
- }
- }
-
- fn max_level_hint(&self) -> Option<LevelFilter> {
- if self.dynamics.has_value_filters() {
- // If we perform any filtering on span field *values*, we will
- // enable *all* spans, because their field values are not known
- // until recording.
- return Some(LevelFilter::TRACE);
- }
- std::cmp::max(
- self.statics.max_level.into(),
- self.dynamics.max_level.into(),
- )
- }
+ // === filtering methods ===
- fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
+ /// Returns `true` if this `EnvFilter` would enable the provided `metadata`
+ /// in the current context.
+ ///
+ /// This is equivalent to calling the [`Layer::enabled`] or
+ /// [`Filter::enabled`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn enabled<S>(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
let level = metadata.level();
// is it possible for a dynamic filter directive to enable this event?
- // if not, we can avoid the thread local access + iterating over the
+ // if not, we can avoid the thread loca'l access + iterating over the
// spans in the current scope.
if self.has_dynamics && self.dynamics.max_level >= *level {
if metadata.is_span() {
@@ -420,14 +495,15 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
}
}
- let enabled_by_scope = SCOPE.with(|scope| {
- for filter in scope.borrow().iter() {
+ let enabled_by_scope = {
+ let scope = self.scope.get_or_default().borrow();
+ for filter in &*scope {
if filter >= level {
return true;
}
}
false
- });
+ };
if enabled_by_scope {
return true;
}
@@ -443,7 +519,33 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
false
}
- fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
+ /// Returns an optional hint of the highest [verbosity level][level] that
+ /// this `EnvFilter` will enable.
+ ///
+ /// This is equivalent to calling the [`Layer::max_level_hint`] or
+ /// [`Filter::max_level_hint`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ ///
+ /// [level]: tracing_core::metadata::Level
+ pub fn max_level_hint(&self) -> Option<LevelFilter> {
+ if self.dynamics.has_value_filters() {
+ // If we perform any filtering on span field *values*, we will
+ // enable *all* spans, because their field values are not known
+ // until recording.
+ return Some(LevelFilter::TRACE);
+ }
+ std::cmp::max(
+ self.statics.max_level.into(),
+ self.dynamics.max_level.into(),
+ )
+ }
+
+ /// Informs the filter that a new span was created.
+ ///
+ /// This is equivalent to calling the [`Layer::on_new_span`] or
+ /// [`Filter::on_new_span`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_new_span<S>(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
let by_cs = try_lock!(self.by_cs.read());
if let Some(cs) = by_cs.get(&attrs.metadata().callsite()) {
let span = cs.to_span_match(attrs);
@@ -451,28 +553,37 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
}
}
- fn on_record(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) {
- if let Some(span) = try_lock!(self.by_id.read()).get(id) {
- span.record_update(values);
- }
- }
-
- fn on_enter(&self, id: &span::Id, _: Context<'_, S>) {
+ /// Informs the filter that the span with the provided `id` was entered.
+ ///
+ /// This is equivalent to calling the [`Layer::on_enter`] or
+ /// [`Filter::on_enter`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_enter<S>(&self, id: &span::Id, _: Context<'_, S>) {
// XXX: This is where _we_ could push IDs to the stack instead, and use
// that to allow changing the filter while a span is already entered.
// But that might be much less efficient...
if let Some(span) = try_lock!(self.by_id.read()).get(id) {
- SCOPE.with(|scope| scope.borrow_mut().push(span.level()));
+ self.scope.get_or_default().borrow_mut().push(span.level());
}
}
- fn on_exit(&self, id: &span::Id, _: Context<'_, S>) {
+ /// Informs the filter that the span with the provided `id` was exited.
+ ///
+ /// This is equivalent to calling the [`Layer::on_exit`] or
+ /// [`Filter::on_exit`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_exit<S>(&self, id: &span::Id, _: Context<'_, S>) {
if self.cares_about_span(id) {
- SCOPE.with(|scope| scope.borrow_mut().pop());
+ self.scope.get_or_default().borrow_mut().pop();
}
}
- fn on_close(&self, id: span::Id, _: Context<'_, S>) {
+ /// Informs the filter that the span with the provided `id` was closed.
+ ///
+ /// This is equivalent to calling the [`Layer::on_close`] or
+ /// [`Filter::on_close`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope.
+ pub fn on_close<S>(&self, id: span::Id, _: Context<'_, S>) {
// If we don't need to acquire a write lock, avoid doing so.
if !self.cares_about_span(&id) {
return;
@@ -481,6 +592,140 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
let mut spans = try_lock!(self.by_id.write());
spans.remove(&id);
}
+
+ /// Informs the filter that the span with the provided `id` recorded the
+ /// provided field `values`.
+ ///
+ /// This is equivalent to calling the [`Layer::on_record`] or
+ /// [`Filter::on_record`] methods on `EnvFilter`'s implementations of those
+ /// traits, but it does not require the trait to be in scope
+ pub fn on_record<S>(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) {
+ if let Some(span) = try_lock!(self.by_id.read()).get(id) {
+ span.record_update(values);
+ }
+ }
+
+ fn cares_about_span(&self, span: &span::Id) -> bool {
+ let spans = try_lock!(self.by_id.read(), else return false);
+ spans.contains_key(span)
+ }
+
+ fn base_interest(&self) -> Interest {
+ if self.has_dynamics {
+ Interest::sometimes()
+ } else {
+ Interest::never()
+ }
+ }
+
+ fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
+ if self.has_dynamics && metadata.is_span() {
+ // If this metadata describes a span, first, check if there is a
+ // dynamic filter that should be constructed for it. If so, it
+ // should always be enabled, since it influences filtering.
+ if let Some(matcher) = self.dynamics.matcher(metadata) {
+ let mut by_cs = try_lock!(self.by_cs.write(), else return self.base_interest());
+ by_cs.insert(metadata.callsite(), matcher);
+ return Interest::always();
+ }
+ }
+
+ // Otherwise, check if any of our static filters enable this metadata.
+ if self.statics.enabled(metadata) {
+ Interest::always()
+ } else {
+ self.base_interest()
+ }
+ }
+}
+
+impl<S: Subscriber> Layer<S> for EnvFilter {
+ #[inline]
+ fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
+ EnvFilter::register_callsite(self, metadata)
+ }
+
+ #[inline]
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ EnvFilter::max_level_hint(self)
+ }
+
+ #[inline]
+ fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
+ self.enabled(metadata, ctx)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+ self.on_record(id, values, ctx);
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_enter(id, ctx);
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_exit(id, ctx);
+ }
+
+ #[inline]
+ fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+ self.on_close(id, ctx);
+ }
+}
+
+feature! {
+ #![all(feature = "registry", feature = "std")]
+ use crate::layer::Filter;
+
+ impl<S> Filter<S> for EnvFilter {
+ #[inline]
+ fn enabled(&self, meta: &Metadata<'_>, ctx: &Context<'_, S>) -> bool {
+ self.enabled(meta, ctx.clone())
+ }
+
+ #[inline]
+ fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
+ self.register_callsite(meta)
+ }
+
+ #[inline]
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ EnvFilter::max_level_hint(self)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+ self.on_record(id, values, ctx);
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_enter(id, ctx);
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+ self.on_exit(id, ctx);
+ }
+
+ #[inline]
+ fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+ self.on_close(id, ctx);
+ }
+ }
}
impl FromStr for EnvFilter {
@@ -502,7 +747,7 @@ where
impl Default for EnvFilter {
fn default() -> Self {
- Self::from_directives(std::iter::empty())
+ Builder::default().from_directives(std::iter::empty())
}
}
@@ -735,4 +980,12 @@ mod tests {
[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug",
);
}
+
+ #[test]
+ fn parse_empty_string() {
+ // There is no corresponding test for [`Builder::parse_lossy`] as failed
+ // parsing does not produce any observable side effects. If this test fails
+ // check that [`Builder::parse_lossy`] is behaving correctly as well.
+ assert!(EnvFilter::builder().parse("").is_ok());
+ }
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs b/vendor/tracing-subscriber/src/filter/filter_fn.rs
index 332bf860a..332bf860a 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/filter_fn.rs
+++ b/vendor/tracing-subscriber/src/filter/filter_fn.rs
diff --git a/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs b/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs
new file mode 100644
index 000000000..3934a1326
--- /dev/null
+++ b/vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs
@@ -0,0 +1,542 @@
+//! Filter combinators
+use crate::layer::{Context, Filter};
+use std::{cmp, fmt, marker::PhantomData};
+use tracing_core::{
+ span::{Attributes, Id, Record},
+ subscriber::Interest,
+ LevelFilter, Metadata,
+};
+
+/// Combines two [`Filter`]s so that spans and events are enabled if and only if
+/// *both* filters return `true`.
+///
+/// This type is typically returned by the [`FilterExt::and`] method. See that
+/// method's documentation for details.
+///
+/// [`Filter`]: crate::layer::Filter
+/// [`FilterExt::and`]: crate::filter::FilterExt::and
+pub struct And<A, B, S> {
+ a: A,
+ b: B,
+ _s: PhantomData<fn(S)>,
+}
+
+/// Combines two [`Filter`]s so that spans and events are enabled if *either* filter
+/// returns `true`.
+///
+/// This type is typically returned by the [`FilterExt::or`] method. See that
+/// method's documentation for details.
+///
+/// [`Filter`]: crate::layer::Filter
+/// [`FilterExt::or`]: crate::filter::FilterExt::or
+pub struct Or<A, B, S> {
+ a: A,
+ b: B,
+ _s: PhantomData<fn(S)>,
+}
+
+/// Inverts the result of a [`Filter`].
+///
+/// If the wrapped filter would enable a span or event, it will be disabled. If
+/// it would disable a span or event, that span or event will be enabled.
+///
+/// This type is typically returned by the [`FilterExt::not`] method. See that
+/// method's documentation for details.
+///
+/// [`Filter`]: crate::layer::Filter
+/// [`FilterExt::not`]: crate::filter::FilterExt::not
+pub struct Not<A, S> {
+ a: A,
+ _s: PhantomData<fn(S)>,
+}
+
+// === impl And ===
+
+impl<A, B, S> And<A, B, S>
+where
+ A: Filter<S>,
+ B: Filter<S>,
+{
+ /// Combines two [`Filter`]s so that spans and events are enabled if and only if
+ /// *both* filters return `true`.
+ ///
+ /// # Examples
+ ///
+ /// Enabling spans or events if they have both a particular target *and* are
+ /// above a certain level:
+ ///
+ /// ```ignore
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, combinator::And},
+ /// prelude::*,
+ /// };
+ ///
+ /// // Enables spans and events with targets starting with `interesting_target`:
+ /// let target_filter = filter_fn(|meta| {
+ /// meta.target().starts_with("interesting_target")
+ /// });
+ ///
+ /// // Enables spans and events with levels `INFO` and below:
+ /// let level_filter = LevelFilter::INFO;
+ ///
+ /// // Combine the two filters together so that a span or event is only enabled
+ /// // if *both* filters would enable it:
+ /// let filter = And::new(level_filter, target_filter);
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ ///
+ /// // This event will *not* be enabled:
+ /// tracing::info!("an event with an uninteresting target");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::info!(target: "interesting_target", "a very interesting event");
+ ///
+ /// // This event will *not* be enabled:
+ /// tracing::debug!(target: "interesting_target", "interesting debug event...");
+ /// ```
+ ///
+ /// [`Filter`]: crate::layer::Filter
+ pub(crate) fn new(a: A, b: B) -> Self {
+ Self {
+ a,
+ b,
+ _s: PhantomData,
+ }
+ }
+}
+
+impl<A, B, S> Filter<S> for And<A, B, S>
+where
+ A: Filter<S>,
+ B: Filter<S>,
+{
+ #[inline]
+ fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
+ self.a.enabled(meta, cx) && self.b.enabled(meta, cx)
+ }
+
+ fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
+ let a = self.a.callsite_enabled(meta);
+ if a.is_never() {
+ return a;
+ }
+
+ let b = self.b.callsite_enabled(meta);
+
+ if !b.is_always() {
+ return b;
+ }
+
+ a
+ }
+
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ // If either hint is `None`, return `None`. Otherwise, return the most restrictive.
+ cmp::min(self.a.max_level_hint(), self.b.max_level_hint())
+ }
+
+ #[inline]
+ fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
+ self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_new_span(attrs, id, ctx.clone());
+ self.b.on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
+ self.a.on_record(id, values, ctx.clone());
+ self.b.on_record(id, values, ctx);
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_enter(id, ctx.clone());
+ self.b.on_enter(id, ctx);
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_exit(id, ctx.clone());
+ self.b.on_exit(id, ctx);
+ }
+
+ #[inline]
+ fn on_close(&self, id: Id, ctx: Context<'_, S>) {
+ self.a.on_close(id.clone(), ctx.clone());
+ self.b.on_close(id, ctx);
+ }
+}
+
+impl<A, B, S> Clone for And<A, B, S>
+where
+ A: Clone,
+ B: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ a: self.a.clone(),
+ b: self.b.clone(),
+ _s: PhantomData,
+ }
+ }
+}
+
+impl<A, B, S> fmt::Debug for And<A, B, S>
+where
+ A: fmt::Debug,
+ B: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("And")
+ .field("a", &self.a)
+ .field("b", &self.b)
+ .finish()
+ }
+}
+
+// === impl Or ===
+
+impl<A, B, S> Or<A, B, S>
+where
+ A: Filter<S>,
+ B: Filter<S>,
+{
+ /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter
+ /// returns `true`.
+ ///
+ /// # Examples
+ ///
+ /// Enabling spans and events at the `INFO` level and above, and all spans
+ /// and events with a particular target:
+ ///
+ /// ```ignore
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, combinator::Or},
+ /// prelude::*,
+ /// };
+ ///
+ /// // Enables spans and events with targets starting with `interesting_target`:
+ /// let target_filter = filter_fn(|meta| {
+ /// meta.target().starts_with("interesting_target")
+ /// });
+ ///
+ /// // Enables spans and events with levels `INFO` and below:
+ /// let level_filter = LevelFilter::INFO;
+ ///
+ /// // Combine the two filters together so that a span or event is enabled
+ /// // if it is at INFO or lower, or if it has a target starting with
+ /// // `interesting_target`.
+ /// let filter = Or::new(level_filter, target_filter);
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ ///
+ /// // This event will *not* be enabled:
+ /// tracing::debug!("an uninteresting event");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::info!("an uninteresting INFO event");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::info!(target: "interesting_target", "a very interesting event");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::debug!(target: "interesting_target", "interesting debug event...");
+ /// ```
+ ///
+ /// Enabling a higher level for a particular target by using `Or` in
+ /// conjunction with the [`And`] combinator:
+ ///
+ /// ```ignore
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, combinator},
+ /// prelude::*,
+ /// };
+ ///
+ /// // This filter will enable spans and events with targets beginning with
+ /// // `my_crate`:
+ /// let my_crate = filter_fn(|meta| {
+ /// meta.target().starts_with("my_crate")
+ /// });
+ ///
+ /// // Combine the `my_crate` filter with a `LevelFilter` to produce a filter
+ /// // that will enable the `INFO` level and lower for spans and events with
+ /// // `my_crate` targets:
+ /// let filter = combinator::And::new(my_crate, LevelFilter::INFO);
+ ///
+ /// // If a span or event *doesn't* have a target beginning with
+ /// // `my_crate`, enable it if it has the `WARN` level or lower:
+ /// // let filter = combinator::Or::new(filter, LevelFilter::WARN);
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ /// ```
+ ///
+ /// [`Filter`]: crate::layer::Filter
+ pub(crate) fn new(a: A, b: B) -> Self {
+ Self {
+ a,
+ b,
+ _s: PhantomData,
+ }
+ }
+}
+
+impl<A, B, S> Filter<S> for Or<A, B, S>
+where
+ A: Filter<S>,
+ B: Filter<S>,
+{
+ #[inline]
+ fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
+ self.a.enabled(meta, cx) || self.b.enabled(meta, cx)
+ }
+
+ fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
+ let a = self.a.callsite_enabled(meta);
+ let b = self.b.callsite_enabled(meta);
+
+ // If either filter will always enable the span or event, return `always`.
+ if a.is_always() || b.is_always() {
+ return Interest::always();
+ }
+
+ // Okay, if either filter will sometimes enable the span or event,
+ // return `sometimes`.
+ if a.is_sometimes() || b.is_sometimes() {
+ return Interest::sometimes();
+ }
+
+ debug_assert!(
+ a.is_never() && b.is_never(),
+ "if neither filter was `always` or `sometimes`, both must be `never` (a={:?}; b={:?})",
+ a,
+ b,
+ );
+ Interest::never()
+ }
+
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ // If either hint is `None`, return `None`. Otherwise, return the less restrictive.
+ Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?))
+ }
+
+ #[inline]
+ fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
+ self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_new_span(attrs, id, ctx.clone());
+ self.b.on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
+ self.a.on_record(id, values, ctx.clone());
+ self.b.on_record(id, values, ctx);
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_enter(id, ctx.clone());
+ self.b.on_enter(id, ctx);
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_exit(id, ctx.clone());
+ self.b.on_exit(id, ctx);
+ }
+
+ #[inline]
+ fn on_close(&self, id: Id, ctx: Context<'_, S>) {
+ self.a.on_close(id.clone(), ctx.clone());
+ self.b.on_close(id, ctx);
+ }
+}
+
+impl<A, B, S> Clone for Or<A, B, S>
+where
+ A: Clone,
+ B: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ a: self.a.clone(),
+ b: self.b.clone(),
+ _s: PhantomData,
+ }
+ }
+}
+
+impl<A, B, S> fmt::Debug for Or<A, B, S>
+where
+ A: fmt::Debug,
+ B: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Or")
+ .field("a", &self.a)
+ .field("b", &self.b)
+ .finish()
+ }
+}
+
+// === impl Not ===
+
+impl<A, S> Not<A, S>
+where
+ A: Filter<S>,
+{
+ /// Inverts the result of a [`Filter`].
+ ///
+ /// If the wrapped filter would enable a span or event, it will be disabled. If
+ /// it would disable a span or event, that span or event will be enabled.
+ ///
+ /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`]
+ /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as
+ /// filters which do not implement filtering on event field values will return
+ /// the default `true` even for events that their [`enabled`] method disables.
+ ///
+ /// Consider a normal filter defined as:
+ ///
+ /// ```ignore (pseudo-code)
+ /// // for spans
+ /// match callsite_enabled() {
+ /// ALWAYS => on_span(),
+ /// SOMETIMES => if enabled() { on_span() },
+ /// NEVER => (),
+ /// }
+ /// // for events
+ /// match callsite_enabled() {
+ /// ALWAYS => on_event(),
+ /// SOMETIMES => if enabled() && event_enabled() { on_event() },
+ /// NEVER => (),
+ /// }
+ /// ```
+ ///
+ /// and an inverted filter defined as:
+ ///
+ /// ```ignore (pseudo-code)
+ /// // for spans
+ /// match callsite_enabled() {
+ /// ALWAYS => (),
+ /// SOMETIMES => if !enabled() { on_span() },
+ /// NEVER => on_span(),
+ /// }
+ /// // for events
+ /// match callsite_enabled() {
+ /// ALWAYS => (),
+ /// SOMETIMES => if !enabled() { on_event() },
+ /// NEVER => on_event(),
+ /// }
+ /// ```
+ ///
+ /// A proper inversion would do `!(enabled() && event_enabled())` (or
+ /// `!enabled() || !event_enabled()`), but because of the implicit `&&`
+ /// relation between `enabled` and `event_enabled`, it is difficult to
+ /// short circuit and not call the wrapped `event_enabled`.
+ ///
+ /// A combinator which remembers the result of `enabled` in order to call
+ /// `event_enabled` only when `enabled() == true` is possible, but requires
+ /// additional thread-local mutable state to support a very niche use case.
+ //
+ // Also, it'd mean the wrapped layer's `enabled()` always gets called and
+ // globally applied to events where it doesn't today, since we can't know
+ // what `event_enabled` will say until we have the event to call it with.
+ ///
+ /// [`Filter`]: crate::layer::Filter
+ /// [`enabled`]: crate::layer::Filter::enabled
+ /// [`event_enabled`]: crate::layer::Filter::event_enabled
+ /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled
+ pub(crate) fn new(a: A) -> Self {
+ Self { a, _s: PhantomData }
+ }
+}
+
+impl<A, S> Filter<S> for Not<A, S>
+where
+ A: Filter<S>,
+{
+ #[inline]
+ fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
+ !self.a.enabled(meta, cx)
+ }
+
+ fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
+ match self.a.callsite_enabled(meta) {
+ i if i.is_always() => Interest::never(),
+ i if i.is_never() => Interest::always(),
+ _ => Interest::sometimes(),
+ }
+ }
+
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ // TODO(eliza): figure this out???
+ None
+ }
+
+ #[inline]
+ fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
+ // Never disable based on event_enabled; we "disabled" it in `enabled`,
+ // so the `not` has already been applied and filtered this not out.
+ let _ = (event, cx);
+ true
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_new_span(attrs, id, ctx);
+ }
+
+ #[inline]
+ fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
+ self.a.on_record(id, values, ctx.clone());
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_enter(id, ctx);
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
+ self.a.on_exit(id, ctx);
+ }
+
+ #[inline]
+ fn on_close(&self, id: Id, ctx: Context<'_, S>) {
+ self.a.on_close(id, ctx);
+ }
+}
+
+impl<A, S> Clone for Not<A, S>
+where
+ A: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ a: self.a.clone(),
+ _s: PhantomData,
+ }
+ }
+}
+
+impl<A, S> fmt::Debug for Not<A, S>
+where
+ A: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("Not").field(&self.a).finish()
+ }
+}
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs
index e77fd3751..e50ee6f00 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/layer_filters.rs
+++ b/vendor/tracing-subscriber/src/filter/layer_filters/mod.rs
@@ -37,14 +37,16 @@ use std::{
cell::{Cell, RefCell},
fmt,
marker::PhantomData,
+ ops::Deref,
sync::Arc,
thread_local,
};
use tracing_core::{
span,
subscriber::{Interest, Subscriber},
- Event, Metadata,
+ Dispatch, Event, Metadata,
};
+pub mod combinator;
/// A [`Layer`] that wraps an inner [`Layer`] and adds a [`Filter`] which
/// controls what spans and events are enabled for that layer.
@@ -158,7 +160,288 @@ thread_local! {
pub(crate) static FILTERING: FilterState = FilterState::new();
}
+/// Extension trait adding [combinators] for combining [`Filter`].
+///
+/// [combinators]: crate::filter::combinator
+/// [`Filter`]: crate::layer::Filter
+pub trait FilterExt<S>: layer::Filter<S> {
+ /// Combines this [`Filter`] with another [`Filter`] s so that spans and
+ /// events are enabled if and only if *both* filters return `true`.
+ ///
+ /// # Examples
+ ///
+ /// Enabling spans or events if they have both a particular target *and* are
+ /// above a certain level:
+ ///
+ /// ```
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, FilterExt},
+ /// prelude::*,
+ /// };
+ ///
+ /// // Enables spans and events with targets starting with `interesting_target`:
+ /// let target_filter = filter_fn(|meta| {
+ /// meta.target().starts_with("interesting_target")
+ /// });
+ ///
+ /// // Enables spans and events with levels `INFO` and below:
+ /// let level_filter = LevelFilter::INFO;
+ ///
+ /// // Combine the two filters together, returning a filter that only enables
+ /// // spans and events that *both* filters will enable:
+ /// let filter = target_filter.and(level_filter);
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ ///
+ /// // This event will *not* be enabled:
+ /// tracing::info!("an event with an uninteresting target");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::info!(target: "interesting_target", "a very interesting event");
+ ///
+ /// // This event will *not* be enabled:
+ /// tracing::debug!(target: "interesting_target", "interesting debug event...");
+ /// ```
+ ///
+ /// [`Filter`]: crate::layer::Filter
+ fn and<B>(self, other: B) -> combinator::And<Self, B, S>
+ where
+ Self: Sized,
+ B: layer::Filter<S>,
+ {
+ combinator::And::new(self, other)
+ }
+
+ /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter
+ /// returns `true`.
+ ///
+ /// # Examples
+ ///
+ /// Enabling spans and events at the `INFO` level and above, and all spans
+ /// and events with a particular target:
+ /// ```
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, FilterExt},
+ /// prelude::*,
+ /// };
+ ///
+ /// // Enables spans and events with targets starting with `interesting_target`:
+ /// let target_filter = filter_fn(|meta| {
+ /// meta.target().starts_with("interesting_target")
+ /// });
+ ///
+ /// // Enables spans and events with levels `INFO` and below:
+ /// let level_filter = LevelFilter::INFO;
+ ///
+ /// // Combine the two filters together so that a span or event is enabled
+ /// // if it is at INFO or lower, or if it has a target starting with
+ /// // `interesting_target`.
+ /// let filter = level_filter.or(target_filter);
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ ///
+ /// // This event will *not* be enabled:
+ /// tracing::debug!("an uninteresting event");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::info!("an uninteresting INFO event");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::info!(target: "interesting_target", "a very interesting event");
+ ///
+ /// // This event *will* be enabled:
+ /// tracing::debug!(target: "interesting_target", "interesting debug event...");
+ /// ```
+ ///
+ /// Enabling a higher level for a particular target by using `or` in
+ /// conjunction with the [`and`] combinator:
+ ///
+ /// ```
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, FilterExt},
+ /// prelude::*,
+ /// };
+ ///
+ /// // This filter will enable spans and events with targets beginning with
+ /// // `my_crate`:
+ /// let my_crate = filter_fn(|meta| {
+ /// meta.target().starts_with("my_crate")
+ /// });
+ ///
+ /// let filter = my_crate
+ /// // Combine the `my_crate` filter with a `LevelFilter` to produce a
+ /// // filter that will enable the `INFO` level and lower for spans and
+ /// // events with `my_crate` targets:
+ /// .and(LevelFilter::INFO)
+ /// // If a span or event *doesn't* have a target beginning with
+ /// // `my_crate`, enable it if it has the `WARN` level or lower:
+ /// .or(LevelFilter::WARN);
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ /// ```
+ ///
+ /// [`Filter`]: crate::layer::Filter
+ /// [`and`]: FilterExt::and
+ fn or<B>(self, other: B) -> combinator::Or<Self, B, S>
+ where
+ Self: Sized,
+ B: layer::Filter<S>,
+ {
+ combinator::Or::new(self, other)
+ }
+
+ /// Inverts `self`, returning a filter that enables spans and events only if
+ /// `self` would *not* enable them.
+ ///
+ /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`]
+ /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as
+ /// filters which do not implement filtering on event field values will return
+ /// the default `true` even for events that their [`enabled`] method disables.
+ ///
+ /// Consider a normal filter defined as:
+ ///
+ /// ```ignore (pseudo-code)
+ /// // for spans
+ /// match callsite_enabled() {
+ /// ALWAYS => on_span(),
+ /// SOMETIMES => if enabled() { on_span() },
+ /// NEVER => (),
+ /// }
+ /// // for events
+ /// match callsite_enabled() {
+ /// ALWAYS => on_event(),
+ /// SOMETIMES => if enabled() && event_enabled() { on_event() },
+ /// NEVER => (),
+ /// }
+ /// ```
+ ///
+ /// and an inverted filter defined as:
+ ///
+ /// ```ignore (pseudo-code)
+ /// // for spans
+ /// match callsite_enabled() {
+ /// ALWAYS => (),
+ /// SOMETIMES => if !enabled() { on_span() },
+ /// NEVER => on_span(),
+ /// }
+ /// // for events
+ /// match callsite_enabled() {
+ /// ALWAYS => (),
+ /// SOMETIMES => if !enabled() { on_event() },
+ /// NEVER => on_event(),
+ /// }
+ /// ```
+ ///
+ /// A proper inversion would do `!(enabled() && event_enabled())` (or
+ /// `!enabled() || !event_enabled()`), but because of the implicit `&&`
+ /// relation between `enabled` and `event_enabled`, it is difficult to
+ /// short circuit and not call the wrapped `event_enabled`.
+ ///
+ /// A combinator which remembers the result of `enabled` in order to call
+ /// `event_enabled` only when `enabled() == true` is possible, but requires
+ /// additional thread-local mutable state to support a very niche use case.
+ //
+ // Also, it'd mean the wrapped layer's `enabled()` always gets called and
+ // globally applied to events where it doesn't today, since we can't know
+ // what `event_enabled` will say until we have the event to call it with.
+ ///
+ /// [`Filter`]: crate::subscribe::Filter
+ /// [`enabled`]: crate::subscribe::Filter::enabled
+ /// [`event_enabled`]: crate::subscribe::Filter::event_enabled
+ /// [`callsite_enabled`]: crate::subscribe::Filter::callsite_enabled
+ fn not(self) -> combinator::Not<Self, S>
+ where
+ Self: Sized,
+ {
+ combinator::Not::new(self)
+ }
+
+ /// [Boxes] `self`, erasing its concrete type.
+ ///
+ /// This is equivalent to calling [`Box::new`], but in method form, so that
+ /// it can be used when chaining combinator methods.
+ ///
+ /// # Examples
+ ///
+ /// When different combinations of filters are used conditionally, they may
+ /// have different types. For example, the following code won't compile,
+ /// since the `if` and `else` clause produce filters of different types:
+ ///
+ /// ```compile_fail
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, FilterExt},
+ /// prelude::*,
+ /// };
+ ///
+ /// let enable_bar_target: bool = // ...
+ /// # false;
+ ///
+ /// let filter = if enable_bar_target {
+ /// filter_fn(|meta| meta.target().starts_with("foo"))
+ /// // If `enable_bar_target` is true, add a `filter_fn` enabling
+ /// // spans and events with the target `bar`:
+ /// .or(filter_fn(|meta| meta.target().starts_with("bar")))
+ /// .and(LevelFilter::INFO)
+ /// } else {
+ /// filter_fn(|meta| meta.target().starts_with("foo"))
+ /// .and(LevelFilter::INFO)
+ /// };
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ /// ```
+ ///
+ /// By using `boxed`, the types of the two different branches can be erased,
+ /// so the assignment to the `filter` variable is valid (as both branches
+ /// have the type `Box<dyn Filter<S> + Send + Sync + 'static>`). The
+ /// following code *does* compile:
+ ///
+ /// ```
+ /// use tracing_subscriber::{
+ /// filter::{filter_fn, LevelFilter, FilterExt},
+ /// prelude::*,
+ /// };
+ ///
+ /// let enable_bar_target: bool = // ...
+ /// # false;
+ ///
+ /// let filter = if enable_bar_target {
+ /// filter_fn(|meta| meta.target().starts_with("foo"))
+ /// .or(filter_fn(|meta| meta.target().starts_with("bar")))
+ /// .and(LevelFilter::INFO)
+ /// // Boxing the filter erases its type, so both branches now
+ /// // have the same type.
+ /// .boxed()
+ /// } else {
+ /// filter_fn(|meta| meta.target().starts_with("foo"))
+ /// .and(LevelFilter::INFO)
+ /// .boxed()
+ /// };
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(tracing_subscriber::fmt::layer().with_filter(filter))
+ /// .init();
+ /// ```
+ ///
+ /// [Boxes]: std::boxed
+ /// [`Box::new`]: std::boxed::Box::new
+ fn boxed(self) -> Box<dyn layer::Filter<S> + Send + Sync + 'static>
+ where
+ Self: Sized + Send + Sync + 'static,
+ {
+ Box::new(self)
+ }
+}
+
// === impl Filter ===
+
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
impl<S> layer::Filter<S> for LevelFilter {
@@ -179,38 +462,35 @@ impl<S> layer::Filter<S> for LevelFilter {
}
}
-impl<S> layer::Filter<S> for Arc<dyn layer::Filter<S> + Send + Sync + 'static> {
- #[inline]
- fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
- (**self).enabled(meta, cx)
- }
+macro_rules! filter_impl_body {
+ () => {
+ #[inline]
+ fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
+ self.deref().enabled(meta, cx)
+ }
- #[inline]
- fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
- (**self).callsite_enabled(meta)
- }
+ #[inline]
+ fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
+ self.deref().callsite_enabled(meta)
+ }
- #[inline]
- fn max_level_hint(&self) -> Option<LevelFilter> {
- (**self).max_level_hint()
- }
+ #[inline]
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ self.deref().max_level_hint()
+ }
+ };
}
-impl<S> layer::Filter<S> for Box<dyn layer::Filter<S> + Send + Sync + 'static> {
- #[inline]
- fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
- (**self).enabled(meta, cx)
- }
-
- #[inline]
- fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
- (**self).callsite_enabled(meta)
- }
+#[cfg(feature = "registry")]
+#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
+impl<S> layer::Filter<S> for Arc<dyn layer::Filter<S> + Send + Sync + 'static> {
+ filter_impl_body!();
+}
- #[inline]
- fn max_level_hint(&self) -> Option<LevelFilter> {
- (**self).max_level_hint()
- }
+#[cfg(feature = "registry")]
+#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
+impl<S> layer::Filter<S> for Box<dyn layer::Filter<S> + Send + Sync + 'static> {
+ filter_impl_body!();
}
// === impl Filtered ===
@@ -247,6 +527,78 @@ impl<L, F, S> Filtered<L, F, S> {
fn did_enable(&self, f: impl FnOnce()) {
FILTERING.with(|filtering| filtering.did_enable(self.id(), f))
}
+
+ /// Borrows the [`Filter`](crate::layer::Filter) used by this layer.
+ pub fn filter(&self) -> &F {
+ &self.filter
+ }
+
+ /// Mutably borrows the [`Filter`](crate::layer::Filter) used by this layer.
+ ///
+ /// When this layer can be mutably borrowed, this may be used to mutate the filter.
+ /// Generally, this will primarily be used with the
+ /// [`reload::Handle::modify`](crate::reload::Handle::modify) method.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use tracing::info;
+ /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*};
+ /// # fn main() {
+ /// let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN);
+ /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer);
+ /// #
+ /// # // specifying the Registry type is required
+ /// # let _: &reload::Handle<filter::Filtered<fmt::Layer<Registry>,
+ /// # filter::LevelFilter, Registry>,Registry>
+ /// # = &reload_handle;
+ /// #
+ /// info!("This will be ignored");
+ /// reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO);
+ /// info!("This will be logged");
+ /// # }
+ /// ```
+ pub fn filter_mut(&mut self) -> &mut F {
+ &mut self.filter
+ }
+
+ /// Borrows the inner [`Layer`] wrapped by this `Filtered` layer.
+ pub fn inner(&self) -> &L {
+ &self.layer
+ }
+
+ /// Mutably borrows the inner [`Layer`] wrapped by this `Filtered` layer.
+ ///
+ /// This method is primarily expected to be used with the
+ /// [`reload::Handle::modify`](crate::reload::Handle::modify) method.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use tracing::info;
+ /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*};
+ /// # fn non_blocking<T: std::io::Write>(writer: T) -> (fn() -> std::io::Stdout) {
+ /// # std::io::stdout
+ /// # }
+ /// # fn main() {
+ /// let filtered_layer = fmt::layer().with_writer(non_blocking(std::io::stderr())).with_filter(filter::LevelFilter::INFO);
+ /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer);
+ /// #
+ /// # // specifying the Registry type is required
+ /// # let _: &reload::Handle<filter::Filtered<fmt::Layer<Registry, _, _, fn() -> std::io::Stdout>,
+ /// # filter::LevelFilter, Registry>, Registry>
+ /// # = &reload_handle;
+ /// #
+ /// info!("This will be logged to stderr");
+ /// reload_handle.modify(|layer| *layer.inner_mut().writer_mut() = non_blocking(std::io::stdout()));
+ /// info!("This will be logged to stdout");
+ /// # }
+ /// ```
+ ///
+ /// [subscriber]: Subscribe
+ pub fn inner_mut(&mut self) -> &mut L {
+ &mut self.layer
+ }
}
impl<S, L, F> Layer<S> for Filtered<L, F, S>
@@ -255,6 +607,10 @@ where
F: layer::Filter<S> + 'static,
L: Layer<S>,
{
+ fn on_register_dispatch(&self, collector: &Dispatch) {
+ self.layer.on_register_dispatch(collector);
+ }
+
fn on_layer(&mut self, subscriber: &mut S) {
self.id = MagicPlfDowncastMarker(subscriber.register_filter());
self.layer.on_layer(subscriber);
@@ -322,7 +678,9 @@ where
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, cx: Context<'_, S>) {
self.did_enable(|| {
- self.layer.on_new_span(attrs, id, cx.with_filter(self.id()));
+ let cx = cx.with_filter(self.id());
+ self.filter.on_new_span(attrs, id, cx.clone());
+ self.layer.on_new_span(attrs, id, cx);
})
}
@@ -333,6 +691,7 @@ where
fn on_record(&self, span: &span::Id, values: &span::Record<'_>, cx: Context<'_, S>) {
if let Some(cx) = cx.if_enabled_for(span, self.id()) {
+ self.filter.on_record(span, values, cx.clone());
self.layer.on_record(span, values, cx)
}
}
@@ -345,6 +704,22 @@ where
}
}
+ fn event_enabled(&self, event: &Event<'_>, cx: Context<'_, S>) -> bool {
+ let cx = cx.with_filter(self.id());
+ let enabled = FILTERING
+ .with(|filtering| filtering.and(self.id(), || self.filter.event_enabled(event, &cx)));
+
+ if enabled {
+ // If the filter enabled this event, ask the wrapped subscriber if
+ // _it_ wants it --- it might have a global filter.
+ self.layer.event_enabled(event, cx)
+ } else {
+ // Otherwise, return `true`. See the comment in `enabled` for why this
+ // is necessary.
+ true
+ }
+ }
+
fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) {
self.did_enable(|| {
self.layer.on_event(event, cx.with_filter(self.id()));
@@ -353,19 +728,22 @@ where
fn on_enter(&self, id: &span::Id, cx: Context<'_, S>) {
if let Some(cx) = cx.if_enabled_for(id, self.id()) {
- self.layer.on_enter(id, cx)
+ self.filter.on_enter(id, cx.clone());
+ self.layer.on_enter(id, cx);
}
}
fn on_exit(&self, id: &span::Id, cx: Context<'_, S>) {
if let Some(cx) = cx.if_enabled_for(id, self.id()) {
- self.layer.on_exit(id, cx)
+ self.filter.on_exit(id, cx.clone());
+ self.layer.on_exit(id, cx);
}
}
fn on_close(&self, id: span::Id, cx: Context<'_, S>) {
if let Some(cx) = cx.if_enabled_for(&id, self.id()) {
- self.layer.on_close(id, cx)
+ self.filter.on_close(id.clone(), cx.clone());
+ self.layer.on_close(id, cx);
}
}
@@ -544,6 +922,10 @@ impl fmt::Binary for FilterId {
}
}
+// === impl FilterExt ===
+
+impl<F, S> FilterExt<S> for F where F: layer::Filter<S> {}
+
// === impl FilterMap ===
impl FilterMap {
@@ -709,6 +1091,14 @@ impl FilterState {
}
}
+ /// Run a second filtering pass, e.g. for Subscribe::event_enabled.
+ fn and(&self, filter: FilterId, f: impl FnOnce() -> bool) -> bool {
+ let map = self.enabled.get();
+ let enabled = map.is_enabled(filter) && f();
+ self.enabled.set(map.set(filter, enabled));
+ enabled
+ }
+
/// Clears the current in-progress filter state.
///
/// This resets the [`FilterMap`] and current [`Interest`] as well as
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/level.rs b/vendor/tracing-subscriber/src/filter/level.rs
index 0fa601260..0fa601260 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/level.rs
+++ b/vendor/tracing-subscriber/src/filter/level.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/mod.rs b/vendor/tracing-subscriber/src/filter/mod.rs
index 000a27195..000a27195 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/mod.rs
+++ b/vendor/tracing-subscriber/src/filter/mod.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/targets.rs b/vendor/tracing-subscriber/src/filter/targets.rs
index e0c7fcf82..e1407114b 100644
--- a/vendor/tracing-subscriber-0.3.3/src/filter/targets.rs
+++ b/vendor/tracing-subscriber/src/filter/targets.rs
@@ -20,7 +20,7 @@ use core::{
slice,
str::FromStr,
};
-use tracing_core::{Interest, Metadata, Subscriber};
+use tracing_core::{Interest, Level, Metadata, Subscriber};
/// A filter that enables or disables spans and events based on their [target]
/// and [level].
@@ -111,7 +111,7 @@ use tracing_core::{Interest, Metadata, Subscriber};
/// by the user at runtime.
///
/// The `Targets` filter can be used as a [per-layer filter][plf] *and* as a
-/// [global filter]:
+/// [global filter][global]:
///
/// ```rust
/// use tracing_subscriber::{
@@ -277,6 +277,62 @@ impl Targets {
self
}
+ /// Returns the default level for this filter, if one is set.
+ ///
+ /// The default level is used to filter any spans or events with targets
+ /// that do not match any of the configured set of prefixes.
+ ///
+ /// The default level can be set for a filter either by using
+ /// [`with_default`](Self::with_default) or when parsing from a filter string that includes a
+ /// level without a target (e.g. `"trace"`).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_subscriber::filter::{LevelFilter, Targets};
+ ///
+ /// let filter = Targets::new().with_default(LevelFilter::INFO);
+ /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO));
+ ///
+ /// let filter: Targets = "info".parse().unwrap();
+ /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO));
+ /// ```
+ ///
+ /// The default level is `None` if no default is set:
+ ///
+ /// ```
+ /// use tracing_subscriber::filter::Targets;
+ ///
+ /// let filter = Targets::new();
+ /// assert_eq!(filter.default_level(), None);
+ ///
+ /// let filter: Targets = "my_crate=info".parse().unwrap();
+ /// assert_eq!(filter.default_level(), None);
+ /// ```
+ ///
+ /// Note that an unset default level (`None`) behaves like [`LevelFilter::OFF`] when the filter is
+ /// used, but it could also be set explicitly which may be useful to distinguish (such as when
+ /// merging multiple `Targets`).
+ ///
+ /// ```
+ /// use tracing_subscriber::filter::{LevelFilter, Targets};
+ ///
+ /// let filter = Targets::new().with_default(LevelFilter::OFF);
+ /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
+ ///
+ /// let filter: Targets = "off".parse().unwrap();
+ /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
+ /// ```
+ pub fn default_level(&self) -> Option<LevelFilter> {
+ self.0.directives().into_iter().find_map(|d| {
+ if d.target.is_none() {
+ Some(d.level)
+ } else {
+ None
+ }
+ })
+ }
+
/// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter.
///
/// The order of iteration is undefined.
@@ -313,6 +369,35 @@ impl Targets {
Interest::never()
}
}
+
+ /// Returns whether a [target]-[`Level`] pair would be enabled
+ /// by this `Targets`.
+ ///
+ /// This method can be used with [`module_path!`] from `std` as the target
+ /// in order to emulate the behavior of the [`tracing::event!`] and [`tracing::span!`]
+ /// macros.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_subscriber::filter::{Targets, LevelFilter};
+ /// use tracing_core::Level;
+ ///
+ /// let filter = Targets::new()
+ /// .with_target("my_crate", Level::INFO)
+ /// .with_target("my_crate::interesting_module", Level::DEBUG);
+ ///
+ /// assert!(filter.would_enable("my_crate", &Level::INFO));
+ /// assert!(!filter.would_enable("my_crate::interesting_module", &Level::TRACE));
+ /// ```
+ ///
+ /// [target]: tracing_core::Metadata::target
+ /// [`module_path!`]: std::module_path!
+ pub fn would_enable(&self, target: &str, level: &Level) -> bool {
+ // "Correct" to call because `Targets` only produces `StaticDirective`'s with NO
+ // fields
+ self.0.target_enabled(target, level)
+ }
}
impl<T, L> Extend<(T, L)> for Targets
@@ -657,6 +742,21 @@ mod tests {
}
#[test]
+ fn targets_default_level() {
+ let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
+ assert_eq!(filter.default_level(), None);
+
+ let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
+ .with_default(LevelFilter::OFF);
+ assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
+
+ let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
+ .with_default(LevelFilter::OFF)
+ .with_default(LevelFilter::INFO);
+ assert_eq!(filter.default_level(), Some(LevelFilter::INFO));
+ }
+
+ #[test]
// `println!` is only available with `libstd`.
#[cfg(feature = "std")]
fn size_of_filters() {
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/fmt_layer.rs b/vendor/tracing-subscriber/src/fmt/fmt_layer.rs
index 0e0d5e0eb..6e4e2ac0b 100644
--- a/vendor/tracing-subscriber-0.3.3/src/fmt/fmt_layer.rs
+++ b/vendor/tracing-subscriber/src/fmt/fmt_layer.rs
@@ -56,7 +56,7 @@ use tracing_core::{
/// # tracing::subscriber::set_global_default(subscriber).unwrap();
/// ```
///
-/// [`Layer`]: ../layer/trait.Layer.html
+/// [`Layer`]: super::layer::Layer
#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))]
#[derive(Debug)]
pub struct Layer<
@@ -70,11 +70,12 @@ pub struct Layer<
fmt_event: E,
fmt_span: format::FmtSpanConfig,
is_ansi: bool,
- _inner: PhantomData<S>,
+ log_internal_errors: bool,
+ _inner: PhantomData<fn(S)>,
}
impl<S> Layer<S> {
- /// Returns a new [`Layer`](struct.Layer.html) with the default configuration.
+ /// Returns a new [`Layer`][self::Layer] with the default configuration.
pub fn new() -> Self {
Self::default()
}
@@ -87,8 +88,8 @@ where
N: for<'writer> FormatFields<'writer> + 'static,
W: for<'writer> MakeWriter<'writer> + 'static,
{
- /// Sets the [event formatter][`FormatEvent`] that the layer will use to
- /// format events.
+ /// Sets the [event formatter][`FormatEvent`] that the layer being built will
+ /// use to format events.
///
/// The event formatter may be any type implementing the [`FormatEvent`]
/// trait, which is implemented for all functions taking a [`FmtContext`], a
@@ -108,7 +109,7 @@ where
/// ```
/// [`FormatEvent`]: format::FormatEvent
/// [`Event`]: tracing::Event
- /// [`Writer`]: crate::format::Writer
+ /// [`Writer`]: format::Writer
pub fn event_format<E2>(self, e: E2) -> Layer<S, N, E2, W>
where
E2: FormatEvent<S, N> + 'static,
@@ -119,6 +120,37 @@ where
fmt_span: self.fmt_span,
make_writer: self.make_writer,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
+ _inner: self._inner,
+ }
+ }
+
+ /// Updates the event formatter by applying a function to the existing event formatter.
+ ///
+ /// This sets the event formatter that the layer being built will use to record fields.
+ ///
+ /// # Examples
+ ///
+ /// Updating an event formatter:
+ ///
+ /// ```rust
+ /// let layer = tracing_subscriber::fmt::layer()
+ /// .map_event_format(|e| e.compact());
+ /// # // this is necessary for type inference.
+ /// # use tracing_subscriber::Layer as _;
+ /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
+ /// ```
+ pub fn map_event_format<E2>(self, f: impl FnOnce(E) -> E2) -> Layer<S, N, E2, W>
+ where
+ E2: FormatEvent<S, N> + 'static,
+ {
+ Layer {
+ fmt_fields: self.fmt_fields,
+ fmt_event: f(self.fmt_event),
+ fmt_span: self.fmt_span,
+ make_writer: self.make_writer,
+ is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
_inner: self._inner,
}
}
@@ -126,7 +158,7 @@ where
// This needs to be a seperate impl block because they place different bounds on the type parameters.
impl<S, N, E, W> Layer<S, N, E, W> {
- /// Sets the [`MakeWriter`] that the [`Layer`] being built will use to write events.
+ /// Sets the [`MakeWriter`] that the layer being built will use to write events.
///
/// # Examples
///
@@ -142,9 +174,6 @@ impl<S, N, E, W> Layer<S, N, E, W> {
/// # use tracing_subscriber::Layer as _;
/// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
/// ```
- ///
- /// [`MakeWriter`]: ../fmt/trait.MakeWriter.html
- /// [`Layer`]: ../layer/trait.Layer.html
pub fn with_writer<W2>(self, make_writer: W2) -> Layer<S, N, E, W2>
where
W2: for<'writer> MakeWriter<'writer> + 'static,
@@ -154,12 +183,63 @@ impl<S, N, E, W> Layer<S, N, E, W> {
fmt_event: self.fmt_event,
fmt_span: self.fmt_span,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
make_writer,
_inner: self._inner,
}
}
- /// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in
+ /// Borrows the [writer] for this [`Layer`].
+ ///
+ /// [writer]: MakeWriter
+ pub fn writer(&self) -> &W {
+ &self.make_writer
+ }
+
+ /// Mutably borrows the [writer] for this [`Layer`].
+ ///
+ /// This method is primarily expected to be used with the
+ /// [`reload::Handle::modify`](crate::reload::Handle::modify) method.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use tracing::info;
+ /// # use tracing_subscriber::{fmt,reload,Registry,prelude::*};
+ /// # fn non_blocking<T: std::io::Write>(writer: T) -> (fn() -> std::io::Stdout) {
+ /// # std::io::stdout
+ /// # }
+ /// # fn main() {
+ /// let layer = fmt::layer().with_writer(non_blocking(std::io::stderr()));
+ /// let (layer, reload_handle) = reload::Layer::new(layer);
+ /// #
+ /// # // specifying the Registry type is required
+ /// # let _: &reload::Handle<fmt::Layer<Registry, _, _, _>, Registry> = &reload_handle;
+ /// #
+ /// info!("This will be logged to stderr");
+ /// reload_handle.modify(|layer| *layer.writer_mut() = non_blocking(std::io::stdout()));
+ /// info!("This will be logged to stdout");
+ /// # }
+ /// ```
+ ///
+ /// [writer]: MakeWriter
+ pub fn writer_mut(&mut self) -> &mut W {
+ &mut self.make_writer
+ }
+
+ /// Sets whether this layer should use ANSI terminal formatting
+ /// escape codes (such as colors).
+ ///
+ /// This method is primarily expected to be used with the
+ /// [`reload::Handle::modify`](crate::reload::Handle::modify) method when changing
+ /// the writer.
+ #[cfg(feature = "ansi")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
+ pub fn set_ansi(&mut self, ansi: bool) {
+ self.is_ansi = ansi;
+ }
+
+ /// Configures the layer to support [`libtest`'s output capturing][capturing] when used in
/// unit tests.
///
/// See [`TestWriter`] for additional details.
@@ -180,13 +260,14 @@ impl<S, N, E, W> Layer<S, N, E, W> {
/// ```
/// [capturing]:
/// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
- /// [`TestWriter`]: writer/struct.TestWriter.html
+ /// [`TestWriter`]: super::writer::TestWriter
pub fn with_test_writer(self) -> Layer<S, N, E, TestWriter> {
Layer {
fmt_fields: self.fmt_fields,
fmt_event: self.fmt_event,
fmt_span: self.fmt_span,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
make_writer: TestWriter::default(),
_inner: self._inner,
}
@@ -201,6 +282,58 @@ impl<S, N, E, W> Layer<S, N, E, W> {
..self
}
}
+
+ /// Sets whether to write errors from [`FormatEvent`] to the writer.
+ /// Defaults to true.
+ ///
+ /// By default, `fmt::Layer` will write any `FormatEvent`-internal errors to
+ /// the writer. These errors are unlikely and will only occur if there is a
+ /// bug in the `FormatEvent` implementation or its dependencies.
+ ///
+ /// If writing to the writer fails, the error message is printed to stderr
+ /// as a fallback.
+ ///
+ /// [`FormatEvent`]: crate::fmt::FormatEvent
+ pub fn log_internal_errors(self, log_internal_errors: bool) -> Self {
+ Self {
+ log_internal_errors,
+ ..self
+ }
+ }
+
+ /// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
+ ///
+ /// This sets the [`MakeWriter`] that the layer being built will use to write events.
+ ///
+ /// # Examples
+ ///
+ /// Redirect output to stderr if level is <= WARN:
+ ///
+ /// ```rust
+ /// use tracing::Level;
+ /// use tracing_subscriber::fmt::{self, writer::MakeWriterExt};
+ ///
+ /// let stderr = std::io::stderr.with_max_level(Level::WARN);
+ /// let layer = fmt::layer()
+ /// .map_writer(move |w| stderr.or_else(w));
+ /// # // this is necessary for type inference.
+ /// # use tracing_subscriber::Layer as _;
+ /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
+ /// ```
+ pub fn map_writer<W2>(self, f: impl FnOnce(W) -> W2) -> Layer<S, N, E, W2>
+ where
+ W2: for<'writer> MakeWriter<'writer> + 'static,
+ {
+ Layer {
+ fmt_fields: self.fmt_fields,
+ fmt_event: self.fmt_event,
+ fmt_span: self.fmt_span,
+ is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
+ make_writer: f(self.make_writer),
+ _inner: self._inner,
+ }
+ }
}
impl<S, N, L, T, W> Layer<S, N, format::Format<L, T>, W>
@@ -228,6 +361,7 @@ where
fmt_span: self.fmt_span,
make_writer: self.make_writer,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
_inner: self._inner,
}
}
@@ -240,6 +374,7 @@ where
fmt_span: self.fmt_span.without_time(),
make_writer: self.make_writer,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
_inner: self._inner,
}
}
@@ -284,7 +419,7 @@ where
/// `Layer`s added to this subscriber.
///
/// [lifecycle]: https://docs.rs/tracing/latest/tracing/span/index.html#the-span-lifecycle
- /// [time]: #method.without_time
+ /// [time]: Layer::without_time()
pub fn with_span_events(self, kind: FmtSpan) -> Self {
Layer {
fmt_span: self.fmt_span.with_kind(kind),
@@ -299,6 +434,30 @@ where
..self
}
}
+ /// Sets whether or not an event's [source code file path][file] is
+ /// displayed.
+ ///
+ /// [file]: tracing_core::Metadata::file
+ pub fn with_file(self, display_filename: bool) -> Layer<S, N, format::Format<L, T>, W> {
+ Layer {
+ fmt_event: self.fmt_event.with_file(display_filename),
+ ..self
+ }
+ }
+
+ /// Sets whether or not an event's [source code line number][line] is
+ /// displayed.
+ ///
+ /// [line]: tracing_core::Metadata::line
+ pub fn with_line_number(
+ self,
+ display_line_number: bool,
+ ) -> Layer<S, N, format::Format<L, T>, W> {
+ Layer {
+ fmt_event: self.fmt_event.with_line_number(display_line_number),
+ ..self
+ }
+ }
/// Sets whether or not an event's level is displayed.
pub fn with_level(self, display_level: bool) -> Layer<S, N, format::Format<L, T>, W> {
@@ -309,9 +468,9 @@ where
}
/// Sets whether or not the [thread ID] of the current thread is displayed
- /// when formatting events
+ /// when formatting events.
///
- /// [thread ID]: https://doc.rust-lang.org/stable/std/thread/struct.ThreadId.html
+ /// [thread ID]: std::thread::ThreadId
pub fn with_thread_ids(self, display_thread_ids: bool) -> Layer<S, N, format::Format<L, T>, W> {
Layer {
fmt_event: self.fmt_event.with_thread_ids(display_thread_ids),
@@ -320,9 +479,9 @@ where
}
/// Sets whether or not the [name] of the current thread is displayed
- /// when formatting events
+ /// when formatting events.
///
- /// [name]: https://doc.rust-lang.org/stable/std/thread/index.html#naming-threads
+ /// [name]: std::thread#naming-threads
pub fn with_thread_names(
self,
display_thread_names: bool,
@@ -333,7 +492,7 @@ where
}
}
- /// Sets the layer being built to use a [less verbose formatter](../fmt/format/struct.Compact.html).
+ /// Sets the layer being built to use a [less verbose formatter][super::format::Compact].
pub fn compact(self) -> Layer<S, N, format::Format<format::Compact, T>, W>
where
N: for<'writer> FormatFields<'writer> + 'static,
@@ -344,6 +503,7 @@ where
fmt_span: self.fmt_span,
make_writer: self.make_writer,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
_inner: self._inner,
}
}
@@ -358,11 +518,12 @@ where
fmt_span: self.fmt_span,
make_writer: self.make_writer,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
_inner: self._inner,
}
}
- /// Sets the layer being built to use a [JSON formatter](../fmt/format/struct.Json.html).
+ /// Sets the layer being built to use a [JSON formatter][super::format::Json].
///
/// The full format includes fields from all entered spans.
///
@@ -377,7 +538,7 @@ where
/// - [`Layer::flatten_event`] can be used to enable flattening event fields into the root
/// object.
///
- /// [`Layer::flatten_event`]: #method.flatten_event
+ /// [`Layer::flatten_event`]: Layer::flatten_event()
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn json(self) -> Layer<S, format::JsonFields, format::Format<format::Json, T>, W> {
@@ -388,6 +549,7 @@ where
make_writer: self.make_writer,
// always disable ANSI escapes in JSON mode!
is_ansi: false,
+ log_internal_errors: self.log_internal_errors,
_inner: self._inner,
}
}
@@ -398,7 +560,7 @@ where
impl<S, T, W> Layer<S, format::JsonFields, format::Format<format::Json, T>, W> {
/// Sets the JSON layer being built to flatten event metadata.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][super::format::Json]
pub fn flatten_event(
self,
flatten_event: bool,
@@ -413,7 +575,7 @@ impl<S, T, W> Layer<S, format::JsonFields, format::Format<format::Json, T>, W> {
/// Sets whether or not the formatter will include the current span in
/// formatted events.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][super::format::Json]
pub fn with_current_span(
self,
display_current_span: bool,
@@ -428,7 +590,7 @@ impl<S, T, W> Layer<S, format::JsonFields, format::Format<format::Json, T>, W> {
/// Sets whether or not the formatter will include a list (from root to leaf)
/// of all currently entered spans in formatted events.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][super::format::Json]
pub fn with_span_list(
self,
display_span_list: bool,
@@ -454,6 +616,38 @@ impl<S, N, E, W> Layer<S, N, E, W> {
fmt_span: self.fmt_span,
make_writer: self.make_writer,
is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
+ _inner: self._inner,
+ }
+ }
+
+ /// Updates the field formatter by applying a function to the existing field formatter.
+ ///
+ /// This sets the field formatter that the layer being built will use to record fields.
+ ///
+ /// # Examples
+ ///
+ /// Updating a field formatter:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::field::MakeExt;
+ /// let layer = tracing_subscriber::fmt::layer()
+ /// .map_fmt_fields(|f| f.debug_alt());
+ /// # // this is necessary for type inference.
+ /// # use tracing_subscriber::Layer as _;
+ /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
+ /// ```
+ pub fn map_fmt_fields<N2>(self, f: impl FnOnce(N) -> N2) -> Layer<S, N2, E, W>
+ where
+ N2: for<'writer> FormatFields<'writer> + 'static,
+ {
+ Layer {
+ fmt_event: self.fmt_event,
+ fmt_fields: f(self.fmt_fields),
+ fmt_span: self.fmt_span,
+ make_writer: self.make_writer,
+ is_ansi: self.is_ansi,
+ log_internal_errors: self.log_internal_errors,
_inner: self._inner,
}
}
@@ -467,6 +661,7 @@ impl<S> Default for Layer<S> {
fmt_span: format::FmtSpanConfig::default(),
make_writer: io::stdout,
is_ansi: cfg!(feature = "ansi"),
+ log_internal_errors: false,
_inner: PhantomData,
}
}
@@ -497,7 +692,7 @@ where
/// formatters are in use, each can store its own formatted representation
/// without conflicting.
///
-/// [extensions]: ../registry/struct.Extensions.html
+/// [extensions]: crate::registry::Extensions
#[derive(Default)]
pub struct FormattedFields<E: ?Sized> {
_format_fields: PhantomData<fn(E)>,
@@ -586,6 +781,11 @@ where
{
fields.was_ansi = self.is_ansi;
extensions.insert(fields);
+ } else {
+ eprintln!(
+ "[tracing-subscriber] Unable to format the following event, ignoring: {:?}",
+ attrs
+ );
}
}
@@ -732,7 +932,20 @@ where
.is_ok()
{
let mut writer = self.make_writer.make_writer_for(event.metadata());
- let _ = io::Write::write_all(&mut writer, buf.as_bytes());
+ let res = io::Write::write_all(&mut writer, buf.as_bytes());
+ if self.log_internal_errors {
+ if let Err(e) = res {
+ eprintln!("[tracing-subscriber] Unable to write an event to the Writer for this Subscriber! Error: {}\n", e);
+ }
+ }
+ } else if self.log_internal_errors {
+ let err_msg = format!("Unable to format the following event. Name: {}; Fields: {:?}\n",
+ event.metadata().name(), event.fields());
+ let mut writer = self.make_writer.make_writer_for(event.metadata());
+ let res = io::Write::write_all(&mut writer, err_msg.as_bytes());
+ if let Err(e) = res {
+ eprintln!("[tracing-subscriber] Unable to write an \"event formatting error\" to the Writer for this Subscriber! Error: {}\n", e);
+ }
}
buf.clear();
@@ -821,7 +1034,7 @@ where
/// If this returns `None`, then no span exists for that ID (either it has
/// closed or the ID is invalid).
///
- /// [stored data]: ../registry/struct.SpanRef.html
+ /// [stored data]: crate::registry::SpanRef
#[inline]
pub fn span(&self, id: &Id) -> Option<SpanRef<'_, S>>
where
@@ -844,7 +1057,7 @@ where
///
/// If this returns `None`, then we are not currently within a span.
///
- /// [stored data]: ../registry/struct.SpanRef.html
+ /// [stored data]: crate::registry::SpanRef
#[inline]
pub fn lookup_current(&self) -> Option<SpanRef<'_, S>>
where
@@ -1030,6 +1243,60 @@ mod test {
}
#[test]
+ fn format_error_print_to_stderr() {
+ struct AlwaysError;
+
+ impl std::fmt::Debug for AlwaysError {
+ fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ Err(std::fmt::Error)
+ }
+ }
+
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .with_writer(make_writer.clone())
+ .with_level(false)
+ .with_ansi(false)
+ .with_timer(MockTime)
+ .finish();
+
+ with_default(subscriber, || {
+ tracing::info!(?AlwaysError);
+ });
+ let actual = sanitize_timings(make_writer.get_string());
+
+ // Only assert the start because the line number and callsite may change.
+ let expected = concat!("Unable to format the following event. Name: event ", file!(), ":");
+ assert!(actual.as_str().starts_with(expected), "\nactual = {}\nshould start with expected = {}\n", actual, expected);
+ }
+
+ #[test]
+ fn format_error_ignore_if_log_internal_errors_is_false() {
+ struct AlwaysError;
+
+ impl std::fmt::Debug for AlwaysError {
+ fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ Err(std::fmt::Error)
+ }
+ }
+
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .with_writer(make_writer.clone())
+ .with_level(false)
+ .with_ansi(false)
+ .with_timer(MockTime)
+ .log_internal_errors(false)
+ .finish();
+
+ with_default(subscriber, || {
+ tracing::info!(?AlwaysError);
+ });
+ let actual = sanitize_timings(make_writer.get_string());
+ assert_eq!("", actual.as_str());
+ }
+
+ #[test]
fn synthesize_span_none() {
let make_writer = MockMakeWriter::default();
let subscriber = crate::fmt::Subscriber::builder()
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/format/json.rs b/vendor/tracing-subscriber/src/fmt/format/json.rs
index cc86f03c7..c2f4d3755 100644
--- a/vendor/tracing-subscriber-0.3.3/src/fmt/format/json.rs
+++ b/vendor/tracing-subscriber/src/fmt/format/json.rs
@@ -23,25 +23,42 @@ use tracing_serde::AsSerde;
#[cfg(feature = "tracing-log")]
use tracing_log::NormalizeEvent;
-/// Marker for `Format` that indicates that the verbose JSON log format should be used.
+/// Marker for [`Format`] that indicates that the newline-delimited JSON log
+/// format should be used.
///
-/// The full format includes fields from all entered spans.
+/// This formatter is intended for production use with systems where structured
+/// logs are consumed as JSON by analysis and viewing tools. The JSON output is
+/// not optimized for human readability; instead, it should be pretty-printed
+/// using external JSON tools such as `jq`, or using a JSON log viewer.
///
/// # Example Output
///
-/// ```json
-/// {
-/// "timestamp":"Feb 20 11:28:15.096",
-/// "level":"INFO",
-/// "fields":{"message":"some message","key":"value"}
-/// "target":"mycrate",
-/// "span":{name":"leaf"},
-/// "spans":[{"name":"root"},{"name":"leaf"}],
-/// }
-/// ```
+/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-json
+/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
+/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-json`
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821315Z&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;preparing to shave yaks&quot;,&quot;number_of_yaks&quot;:3},&quot;target&quot;:&quot;fmt_json&quot;}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821422Z&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;shaving yaks&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821495Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:1,&quot;name&quot;:&quot;shave&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821546Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaved successfully&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:1,&quot;name&quot;:&quot;shave&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821598Z&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:1,&quot;shaved&quot;:true},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821637Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:1},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821684Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:2,&quot;name&quot;:&quot;shave&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821727Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaved successfully&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:2,&quot;name&quot;:&quot;shave&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821773Z&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:2,&quot;shaved&quot;:true},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821806Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:2},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821909Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:3,&quot;name&quot;:&quot;shave&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821956Z&quot;,&quot;level&quot;:&quot;WARN&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;could not locate yak&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:3,&quot;name&quot;:&quot;shave&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822006Z&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:3,&quot;shaved&quot;:false},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822041Z&quot;,&quot;level&quot;:&quot;ERROR&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;failed to shave yak&quot;,&quot;yak&quot;:3,&quot;error&quot;:&quot;missing yak&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822079Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:2},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
+/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822117Z&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaving completed&quot;,&quot;all_yaks_shaved&quot;:false},&quot;target&quot;:&quot;fmt_json&quot;}
+/// </pre>
///
/// # Options
///
+/// This formatter exposes additional options to configure the structure of the
+/// output JSON objects:
+///
/// - [`Json::flatten_event`] can be used to enable flattening event fields into
/// the root
/// - [`Json::with_current_span`] can be used to control logging of the current
@@ -52,9 +69,23 @@ use tracing_log::NormalizeEvent;
/// By default, event fields are not flattened, and both current span and span
/// list are logged.
///
-/// [`Json::flatten_event`]: #method.flatten_event
-/// [`Json::with_current_span`]: #method.with_current_span
-/// [`Json::with_span_list`]: #method.with_span_list
+/// # Valuable Support
+///
+/// Experimental support is available for using the [`valuable`] crate to record
+/// user-defined values as structured JSON. When the ["valuable" unstable
+/// feature][unstable] is enabled, types implementing [`valuable::Valuable`] will
+/// be recorded as structured JSON, rather than
+/// using their [`std::fmt::Debug`] implementations.
+///
+/// **Note**: This is an experimental feature. [Unstable features][unstable]
+/// must be enabled in order to use `valuable` support.
+///
+/// [`Json::flatten_event`]: Json::flatten_event()
+/// [`Json::with_current_span`]: Json::with_current_span()
+/// [`Json::with_span_list`]: Json::with_span_list()
+/// [`valuable`]: https://crates.io/crates/valuable
+/// [unstable]: crate#unstable-features
+/// [`valuable::Valuable`]: https://docs.rs/valuable/latest/valuable/trait.Valuable.html
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Json {
pub(crate) flatten_event: bool,
@@ -243,6 +274,18 @@ where
serializer.serialize_entry("target", meta.target())?;
}
+ if self.display_filename {
+ if let Some(filename) = meta.file() {
+ serializer.serialize_entry("filename", filename)?;
+ }
+ }
+
+ if self.display_line_number {
+ if let Some(line_number) = meta.line() {
+ serializer.serialize_entry("line_number", &line_number)?;
+ }
+ }
+
if self.format.display_current_span {
if let Some(ref span) = current_span {
serializer
@@ -298,7 +341,6 @@ impl Default for Json {
/// The JSON [`FormatFields`] implementation.
///
-/// [`FormatFields`]: trait.FormatFields.html
#[derive(Debug)]
pub struct JsonFields {
// reserve the ability to add fields to this without causing a breaking
@@ -309,7 +351,6 @@ pub struct JsonFields {
impl JsonFields {
/// Returns a new JSON [`FormatFields`] implementation.
///
- /// [`FormatFields`]: trait.FormatFields.html
pub fn new() -> Self {
Self { _private: () }
}
@@ -378,9 +419,8 @@ impl<'a> FormatFields<'a> for JsonFields {
/// The [visitor] produced by [`JsonFields`]'s [`MakeVisitor`] implementation.
///
-/// [visitor]: ../../field/trait.Visit.html
-/// [`JsonFields`]: struct.JsonFields.html
-/// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html
+/// [visitor]: crate::field::Visit
+/// [`MakeVisitor`]: crate::field::MakeVisitor
pub struct JsonVisitor<'a> {
values: BTreeMap<&'a str, serde_json::Value>,
writer: &'a mut dyn Write,
@@ -435,6 +475,26 @@ impl<'a> crate::field::VisitOutput<fmt::Result> for JsonVisitor<'a> {
}
impl<'a> field::Visit for JsonVisitor<'a> {
+ #[cfg(all(tracing_unstable, feature = "valuable"))]
+ fn record_value(&mut self, field: &Field, value: valuable_crate::Value<'_>) {
+ let value = match serde_json::to_value(valuable_serde::Serializable::new(value)) {
+ Ok(value) => value,
+ Err(_e) => {
+ #[cfg(debug_assertions)]
+ unreachable!(
+ "`valuable::Valuable` implementations should always serialize \
+ successfully, but an error occurred: {}",
+ _e,
+ );
+
+ #[cfg(not(debug_assertions))]
+ return;
+ }
+ };
+
+ self.values.insert(field.name(), value);
+ }
+
/// Visit a double precision floating point value.
fn record_f64(&mut self, field: &Field, value: f64) {
self.values
@@ -488,6 +548,7 @@ mod test {
use tracing::{self, subscriber::with_default};
use std::fmt;
+ use std::path::Path;
struct MockTime;
impl FormatTime for MockTime {
@@ -516,6 +577,50 @@ mod test {
}
#[test]
+ fn json_filename() {
+ let current_path = Path::new("tracing-subscriber")
+ .join("src")
+ .join("fmt")
+ .join("format")
+ .join("json.rs")
+ .to_str()
+ .expect("path must be valid unicode")
+ // escape windows backslashes
+ .replace('\\', "\\\\");
+ let expected =
+ &format!("{}{}{}",
+ "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"filename\":\"",
+ current_path,
+ "\",\"fields\":{\"message\":\"some json test\"}}\n");
+ let subscriber = subscriber()
+ .flatten_event(false)
+ .with_current_span(true)
+ .with_file(true)
+ .with_span_list(true);
+ test_json(expected, subscriber, || {
+ let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
+ let _guard = span.enter();
+ tracing::info!("some json test");
+ });
+ }
+
+ #[test]
+ fn json_line_number() {
+ let expected =
+ "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"line_number\":42,\"fields\":{\"message\":\"some json test\"}}\n";
+ let subscriber = subscriber()
+ .flatten_event(false)
+ .with_current_span(true)
+ .with_line_number(true)
+ .with_span_list(true);
+ test_json_with_line_number(expected, subscriber, || {
+ let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
+ let _guard = span.enter();
+ tracing::info!("some json test");
+ });
+ }
+
+ #[test]
fn json_flattened_event() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"message\":\"some json test\"}\n";
@@ -747,4 +852,34 @@ mod test {
serde_json::from_str(actual).unwrap()
);
}
+
+ fn test_json_with_line_number<T>(
+ expected: &str,
+ builder: crate::fmt::SubscriberBuilder<JsonFields, Format<Json>>,
+ producer: impl FnOnce() -> T,
+ ) {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = builder
+ .with_writer(make_writer.clone())
+ .with_timer(MockTime)
+ .finish();
+
+ with_default(subscriber, producer);
+
+ let buf = make_writer.buf();
+ let actual = std::str::from_utf8(&buf[..]).unwrap();
+ let mut expected =
+ serde_json::from_str::<std::collections::HashMap<&str, serde_json::Value>>(expected)
+ .unwrap();
+ let expect_line_number = expected.remove("line_number").is_some();
+ let mut actual: std::collections::HashMap<&str, serde_json::Value> =
+ serde_json::from_str(actual).unwrap();
+ let line_number = actual.remove("line_number");
+ if expect_line_number {
+ assert_eq!(line_number.map(|x| x.is_number()), Some(true));
+ } else {
+ assert!(line_number.is_none());
+ }
+ assert_eq!(actual, expected);
+ }
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/format/mod.rs b/vendor/tracing-subscriber/src/fmt/format/mod.rs
index 9001e102e..b8a482e55 100644
--- a/vendor/tracing-subscriber-0.3.3/src/fmt/format/mod.rs
+++ b/vendor/tracing-subscriber/src/fmt/format/mod.rs
@@ -1,4 +1,33 @@
//! Formatters for logging `tracing` events.
+//!
+//! This module provides several formatter implementations, as well as utilities
+//! for implementing custom formatters.
+//!
+//! # Formatters
+//! This module provides a number of formatter implementations:
+//!
+//! * [`Full`]: The default formatter. This emits human-readable,
+//! single-line logs for each event that occurs, with the current span context
+//! displayed before the formatted representation of the event. See
+//! [here](Full#example-output) for sample output.
+//!
+//! * [`Compact`]: A variant of the default formatter, optimized for
+//! short line lengths. Fields from the current span context are appended to
+//! the fields of the formatted event, and span names are not shown; the
+//! verbosity level is abbreviated to a single character. See
+//! [here](Compact#example-output) for sample output.
+//!
+//! * [`Pretty`]: Emits excessively pretty, multi-line logs, optimized
+//! for human readability. This is primarily intended to be used in local
+//! development and debugging, or for command-line applications, where
+//! automated analysis and compact storage of logs is less of a priority than
+//! readability and visual appeal. See [here](Pretty#example-output)
+//! for sample output.
+//!
+//! * [`Json`]: Outputs newline-delimited JSON logs. This is intended
+//! for production use with systems where structured logs are consumed as JSON
+//! by analysis and viewing tools. The JSON output is not optimized for human
+//! readability. See [here](Json#example-output) for sample output.
use super::time::{FormatTime, SystemTime};
use crate::{
field::{MakeOutput, MakeVisitor, RecordFields, VisitFmt, VisitOutput},
@@ -17,7 +46,7 @@ use tracing_core::{
use tracing_log::NormalizeEvent;
#[cfg(feature = "ansi")]
-use ansi_term::{Colour, Style};
+use nu_ansi_term::{Color, Style};
#[cfg(feature = "json")]
mod json;
@@ -72,7 +101,7 @@ pub use pretty::*;
/// does not support ANSI escape codes (such as a log file), and they should
/// not be emitted.
///
-/// Crates like [`ansi_term`] and [`owo-colors`] can be used to add ANSI
+/// Crates like [`nu_ansi_term`] and [`owo-colors`] can be used to add ANSI
/// escape codes to formatted output.
///
/// * The actual [`Event`] to be formatted.
@@ -153,13 +182,14 @@ pub use pretty::*;
/// DEBUG yak_shaving::shaver: some-span{field-on-span=foo}: started shaving yak
/// ```
///
+/// [`layer::Context`]: crate::layer::Context
/// [`fmt::Layer`]: super::Layer
/// [`fmt::Subscriber`]: super::Subscriber
/// [`Event`]: tracing::Event
/// [implements `FormatFields`]: super::FmtContext#impl-FormatFields<'writer>
/// [ANSI terminal escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code
/// [`Writer::has_ansi_escapes`]: Writer::has_ansi_escapes
-/// [`ansi_term`]: https://crates.io/crates/ansi_term
+/// [`nu_ansi_term`]: https://crates.io/crates/nu_ansi_term
/// [`owo-colors`]: https://crates.io/crates/owo-colors
/// [default formatter]: Full
pub trait FormatEvent<S, N>
@@ -197,8 +227,8 @@ where
/// time a span or event with fields is recorded, the subscriber will format
/// those fields with its associated `FormatFields` implementation.
///
-/// [set of fields]: ../field/trait.RecordFields.html
-/// [`FmtSubscriber`]: ../fmt/struct.Subscriber.html
+/// [set of fields]: crate::field::RecordFields
+/// [`FmtSubscriber`]: super::Subscriber
pub trait FormatFields<'writer> {
/// Format the provided `fields` to the provided [`Writer`], returning a result.
fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result;
@@ -251,7 +281,6 @@ pub fn json() -> Format<Json> {
/// Returns a [`FormatFields`] implementation that formats fields using the
/// provided function or closure.
///
-/// [`FormatFields`]: trait.FormatFields.html
pub fn debug_fn<F>(f: F) -> FieldFn<F>
where
F: Fn(&mut Writer<'_>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone,
@@ -272,6 +301,8 @@ where
///
/// Additionally, a `Writer` may expose additional `tracing`-specific
/// information to the formatter implementation.
+///
+/// [fields]: tracing_core::field
pub struct Writer<'writer> {
writer: &'writer mut dyn fmt::Write,
// TODO(eliza): add ANSI support
@@ -281,28 +312,76 @@ pub struct Writer<'writer> {
/// A [`FormatFields`] implementation that formats fields by calling a function
/// or closure.
///
-/// [`FormatFields`]: trait.FormatFields.html
#[derive(Debug, Clone)]
pub struct FieldFn<F>(F);
/// The [visitor] produced by [`FieldFn`]'s [`MakeVisitor`] implementation.
///
-/// [visitor]: ../../field/trait.Visit.html
-/// [`FieldFn`]: struct.FieldFn.html
-/// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html
+/// [visitor]: super::super::field::Visit
+/// [`MakeVisitor`]: super::super::field::MakeVisitor
pub struct FieldFnVisitor<'a, F> {
f: F,
writer: Writer<'a>,
result: fmt::Result,
}
-/// Marker for `Format` that indicates that the compact log format should be used.
+/// Marker for [`Format`] that indicates that the compact log format should be used.
+///
+/// The compact format includes fields from all currently entered spans, after
+/// the event's fields. Span names are listed in order before fields are
+/// displayed.
+///
+/// # Example Output
///
-/// The compact format only includes the fields from the most recently entered span.
+/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-compact
+/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
+/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-compact`
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809287Z </font><font color="#4E9A06"> INFO</font> <b>fmt_compact</b><font color="#AAAAAA">: preparing to shave yaks </font><i>number_of_yaks</i><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809367Z </font><font color="#4E9A06"> INFO</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: shaving yaks </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809414Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot; </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=1</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809443Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: yak shaved successfully </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=1</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809477Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks</b>: <b>yak_events</b><font color="#AAAAAA">: </font><i>yak</i><font color="#AAAAAA">=1 </font><i>shaved</i><font color="#AAAAAA">=true </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809500Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: </font><i>yaks_shaved</i><font color="#AAAAAA">=1 </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809531Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot; </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=2</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809554Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: yak shaved successfully </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=2</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809581Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks</b>: <b>yak_events</b><font color="#AAAAAA">: </font><i>yak</i><font color="#AAAAAA">=2 </font><i>shaved</i><font color="#AAAAAA">=true </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809606Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: </font><i>yaks_shaved</i><font color="#AAAAAA">=2 </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809635Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot; </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809664Z </font><font color="#C4A000"> WARN</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: could not locate yak </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809693Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks</b>: <b>yak_events</b><font color="#AAAAAA">: </font><i>yak</i><font color="#AAAAAA">=3 </font><i>shaved</i><font color="#AAAAAA">=false </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809717Z </font><font color="#CC0000">ERROR</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: failed to shave yak </font><i>yak</i><font color="#AAAAAA">=3 </font><i>error</i><font color="#AAAAAA">=missing yak </font><i>error.sources</i><font color="#AAAAAA">=[out of space, out of cash] </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809743Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: </font><i>yaks_shaved</i><font color="#AAAAAA">=2 </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-17T19:51:05.809768Z </font><font color="#4E9A06"> INFO</font> <b>fmt_compact</b><font color="#AAAAAA">: yak shaving completed </font><i>all_yaks_shaved</i><font color="#AAAAAA">=false</font>
+///
+/// </pre>
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct Compact;
-/// Marker for `Format` that indicates that the verbose log format should be used.
+/// Marker for [`Format`] that indicates that the default log format should be used.
+///
+/// This formatter shows the span context before printing event data. Spans are
+/// displayed including their names and fields.
///
-/// The full format includes fields from all entered spans.
+/// # Example Output
+///
+/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt
+/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
+/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt`
+/// <font color="#AAAAAA">2022-02-15T18:40:14.289898Z </font><font color="#4E9A06"> INFO</font> fmt: preparing to shave yaks <i>number_of_yaks</i><font color="#AAAAAA">=3</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.289974Z </font><font color="#4E9A06"> INFO</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: shaving yaks</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290011Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=1</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot;</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290038Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=1</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: yak shaved successfully</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290070Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: yak_events: </font><i>yak</i><font color="#AAAAAA">=1 </font><i>shaved</i><font color="#AAAAAA">=true</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290089Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: </font><i>yaks_shaved</i><font color="#AAAAAA">=1</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290114Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=2</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot;</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290134Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=2</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: yak shaved successfully</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290157Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: yak_events: </font><i>yak</i><font color="#AAAAAA">=2 </font><i>shaved</i><font color="#AAAAAA">=true</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290174Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: </font><i>yaks_shaved</i><font color="#AAAAAA">=2</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290198Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot;</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290222Z </font><font color="#C4A000"> WARN</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: could not locate yak</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290247Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: yak_events: </font><i>yak</i><font color="#AAAAAA">=3 </font><i>shaved</i><font color="#AAAAAA">=false</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290268Z </font><font color="#CC0000">ERROR</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: failed to shave yak </font><i>yak</i><font color="#AAAAAA">=3 </font><i>error</i><font color="#AAAAAA">=missing yak </font><i>error.sources</i><font color="#AAAAAA">=[out of space, out of cash]</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290287Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: </font><i>yaks_shaved</i><font color="#AAAAAA">=2</font>
+/// <font color="#AAAAAA">2022-02-15T18:40:14.290309Z </font><font color="#4E9A06"> INFO</font> fmt: yak shaving completed. <i>all_yaks_shaved</i><font color="#AAAAAA">=false</font>
+/// </pre>
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct Full;
@@ -311,8 +390,11 @@ pub struct Full;
/// You will usually want to use this as the `FormatEvent` for a `FmtSubscriber`.
///
/// The default logging format, [`Full`] includes all fields in each event and its containing
-/// spans. The [`Compact`] logging format includes only the fields from the most-recently-entered
-/// span.
+/// spans. The [`Compact`] logging format is intended to produce shorter log
+/// lines; it displays each event's fields, along with fields from the current
+/// span context, but other information is abbreviated. The [`Pretty`] logging
+/// format is an extra-verbose, multi-line human-readable logging format
+/// intended for use in development.
#[derive(Debug, Clone)]
pub struct Format<F = Full, T = SystemTime> {
format: F,
@@ -323,6 +405,8 @@ pub struct Format<F = Full, T = SystemTime> {
pub(crate) display_level: bool,
pub(crate) display_thread_id: bool,
pub(crate) display_thread_name: bool,
+ pub(crate) display_filename: bool,
+ pub(crate) display_line_number: bool,
}
// === impl Writer ===
@@ -499,6 +583,8 @@ impl Default for Format<Full, SystemTime> {
display_level: true,
display_thread_id: false,
display_thread_name: false,
+ display_filename: false,
+ display_line_number: false,
}
}
}
@@ -517,6 +603,8 @@ impl<F, T> Format<F, T> {
display_level: self.display_level,
display_thread_id: self.display_thread_id,
display_thread_name: self.display_thread_name,
+ display_filename: self.display_filename,
+ display_line_number: self.display_line_number,
}
}
@@ -554,6 +642,8 @@ impl<F, T> Format<F, T> {
display_level: self.display_level,
display_thread_id: self.display_thread_id,
display_thread_name: self.display_thread_name,
+ display_filename: true,
+ display_line_number: true,
}
}
@@ -571,8 +661,6 @@ impl<F, T> Format<F, T> {
///
/// - [`Format::flatten_event`] can be used to enable flattening event fields into the root
/// object.
- ///
- /// [`Format::flatten_event`]: #method.flatten_event
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn json(self) -> Format<Json, T> {
@@ -585,6 +673,8 @@ impl<F, T> Format<F, T> {
display_level: self.display_level,
display_thread_id: self.display_thread_id,
display_thread_name: self.display_thread_name,
+ display_filename: self.display_filename,
+ display_line_number: self.display_line_number,
}
}
@@ -612,6 +702,8 @@ impl<F, T> Format<F, T> {
display_level: self.display_level,
display_thread_id: self.display_thread_id,
display_thread_name: self.display_thread_name,
+ display_filename: self.display_filename,
+ display_line_number: self.display_line_number,
}
}
@@ -626,6 +718,8 @@ impl<F, T> Format<F, T> {
display_level: self.display_level,
display_thread_id: self.display_thread_id,
display_thread_name: self.display_thread_name,
+ display_filename: self.display_filename,
+ display_line_number: self.display_line_number,
}
}
@@ -654,9 +748,9 @@ impl<F, T> Format<F, T> {
}
/// Sets whether or not the [thread ID] of the current thread is displayed
- /// when formatting events
+ /// when formatting events.
///
- /// [thread ID]: https://doc.rust-lang.org/stable/std/thread/struct.ThreadId.html
+ /// [thread ID]: std::thread::ThreadId
pub fn with_thread_ids(self, display_thread_id: bool) -> Format<F, T> {
Format {
display_thread_id,
@@ -665,9 +759,9 @@ impl<F, T> Format<F, T> {
}
/// Sets whether or not the [name] of the current thread is displayed
- /// when formatting events
+ /// when formatting events.
///
- /// [name]: https://doc.rust-lang.org/stable/std/thread/index.html#naming-threads
+ /// [name]: std::thread#naming-threads
pub fn with_thread_names(self, display_thread_name: bool) -> Format<F, T> {
Format {
display_thread_name,
@@ -675,6 +769,38 @@ impl<F, T> Format<F, T> {
}
}
+ /// Sets whether or not an event's [source code file path][file] is
+ /// displayed.
+ ///
+ /// [file]: tracing_core::Metadata::file
+ pub fn with_file(self, display_filename: bool) -> Format<F, T> {
+ Format {
+ display_filename,
+ ..self
+ }
+ }
+
+ /// Sets whether or not an event's [source code line number][line] is
+ /// displayed.
+ ///
+ /// [line]: tracing_core::Metadata::line
+ pub fn with_line_number(self, display_line_number: bool) -> Format<F, T> {
+ Format {
+ display_line_number,
+ ..self
+ }
+ }
+
+ /// Sets whether or not the source code location from which an event
+ /// originated is displayed.
+ ///
+ /// This is equivalent to calling [`Format::with_file`] and
+ /// [`Format::with_line_number`] with the same value.
+ pub fn with_source_location(self, display_location: bool) -> Self {
+ self.with_line_number(display_location)
+ .with_file(display_location)
+ }
+
#[inline]
fn format_timestamp(&self, writer: &mut Writer<'_>) -> fmt::Result
where
@@ -692,14 +818,24 @@ impl<F, T> Format<F, T> {
if writer.has_ansi_escapes() {
let style = Style::new().dimmed();
write!(writer, "{}", style.prefix())?;
- self.timer.format_time(writer)?;
+
+ // If getting the timestamp failed, don't bail --- only bail on
+ // formatting errors.
+ if self.timer.format_time(writer).is_err() {
+ writer.write_str("<unknown time>")?;
+ }
+
write!(writer, "{} ", style.suffix())?;
return Ok(());
}
}
// Otherwise, just format the timestamp without ANSI formatting.
- self.timer.format_time(writer)?;
+ // If getting the timestamp failed, don't bail --- only bail on
+ // formatting errors.
+ if self.timer.format_time(writer).is_err() {
+ writer.write_str("<unknown time>")?;
+ }
writer.write_char(' ')
}
}
@@ -714,7 +850,7 @@ impl<T> Format<Json, T> {
/// ```ignore,json
/// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate", "message":"some message", "key": "value"}
/// ```
- /// See [`Json`](../format/struct.Json.html).
+ /// See [`Json`][super::format::Json].
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn flatten_event(mut self, flatten_event: bool) -> Format<Json, T> {
@@ -725,7 +861,7 @@ impl<T> Format<Json, T> {
/// Sets whether or not the formatter will include the current span in
/// formatted events.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][Json]
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn with_current_span(mut self, display_current_span: bool) -> Format<Json, T> {
@@ -736,7 +872,7 @@ impl<T> Format<Json, T> {
/// Sets whether or not the formatter will include a list (from root to
/// leaf) of all currently entered spans in formatted events.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][Json]
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn with_span_list(mut self, display_span_list: bool) -> Format<Json, T> {
@@ -840,6 +976,34 @@ where
)?;
}
+ let line_number = if self.display_line_number {
+ meta.line()
+ } else {
+ None
+ };
+
+ if self.display_filename {
+ if let Some(filename) = meta.file() {
+ write!(
+ writer,
+ "{}{}{}",
+ dimmed.paint(filename),
+ dimmed.paint(":"),
+ if line_number.is_some() { "" } else { " " }
+ )?;
+ }
+ }
+
+ if let Some(line_number) = line_number {
+ write!(
+ writer,
+ "{}{}:{} ",
+ dimmed.prefix(),
+ line_number,
+ dimmed.suffix()
+ )?;
+ }
+
ctx.format_fields(writer.by_ref(), event)?;
writeln!(writer)
}
@@ -918,23 +1082,49 @@ where
};
write!(writer, "{}", fmt_ctx)?;
+ let bold = writer.bold();
+ let dimmed = writer.dimmed();
+
+ let mut needs_space = false;
if self.display_target {
- write!(
- writer,
- "{}{} ",
- writer.bold().paint(meta.target()),
- writer.dimmed().paint(":")
- )?;
+ write!(writer, "{}{}", bold.paint(meta.target()), dimmed.paint(":"))?;
+ needs_space = true;
+ }
+
+ if self.display_filename {
+ if let Some(filename) = meta.file() {
+ if self.display_target {
+ writer.write_char(' ')?;
+ }
+ write!(writer, "{}{}", bold.paint(filename), dimmed.paint(":"))?;
+ needs_space = true;
+ }
+ }
+
+ if self.display_line_number {
+ if let Some(line_number) = meta.line() {
+ write!(
+ writer,
+ "{}{}{}{}",
+ bold.prefix(),
+ line_number,
+ bold.suffix(),
+ dimmed.paint(":")
+ )?;
+ needs_space = true;
+ }
+ }
+
+ if needs_space {
+ writer.write_char(' ')?;
}
ctx.format_fields(writer.by_ref(), event)?;
- let dimmed = writer.dimmed();
for span in ctx
.event_scope()
.into_iter()
- .map(crate::registry::Scope::from_root)
- .flatten()
+ .flat_map(crate::registry::Scope::from_root)
{
let exts = span.extensions();
if let Some(fields) = exts.get::<FormattedFields<N>>() {
@@ -962,7 +1152,6 @@ where
/// The default [`FormatFields`] implementation.
///
-/// [`FormatFields`]: trait.FormatFields.html
#[derive(Debug)]
pub struct DefaultFields {
// reserve the ability to add fields to this without causing a breaking
@@ -984,7 +1173,6 @@ pub struct DefaultVisitor<'a> {
impl DefaultFields {
/// Returns a new default [`FormatFields`] implementation.
///
- /// [`FormatFields`]: trait.FormatFields.html
pub fn new() -> Self {
Self { _private: () }
}
@@ -1201,6 +1389,14 @@ impl Style {
fn paint(&self, d: impl fmt::Display) -> impl fmt::Display {
d
}
+
+ fn prefix(&self) -> impl fmt::Display {
+ ""
+ }
+
+ fn suffix(&self) -> impl fmt::Display {
+ ""
+ }
}
struct FmtThreadName<'a> {
@@ -1288,11 +1484,11 @@ impl<'a> fmt::Display for FmtLevel<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.ansi {
match *self.level {
- Level::TRACE => write!(f, "{}", Colour::Purple.paint(TRACE_STR)),
- Level::DEBUG => write!(f, "{}", Colour::Blue.paint(DEBUG_STR)),
- Level::INFO => write!(f, "{}", Colour::Green.paint(INFO_STR)),
- Level::WARN => write!(f, "{}", Colour::Yellow.paint(WARN_STR)),
- Level::ERROR => write!(f, "{}", Colour::Red.paint(ERROR_STR)),
+ Level::TRACE => write!(f, "{}", Color::Purple.paint(TRACE_STR)),
+ Level::DEBUG => write!(f, "{}", Color::Blue.paint(DEBUG_STR)),
+ Level::INFO => write!(f, "{}", Color::Green.paint(INFO_STR)),
+ Level::WARN => write!(f, "{}", Color::Yellow.paint(WARN_STR)),
+ Level::ERROR => write!(f, "{}", Color::Red.paint(ERROR_STR)),
}
} else {
match *self.level {
@@ -1366,7 +1562,7 @@ impl<'a, F> fmt::Debug for FieldFnVisitor<'a, F> {
/// Configures what points in the span lifecycle are logged as events.
///
-/// See also [`with_span_events`](../struct.SubscriberBuilder.html#method.with_span_events).
+/// See also [`with_span_events`](super::SubscriberBuilder.html::with_span_events).
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct FmtSpan(u8);
@@ -1528,7 +1724,9 @@ pub(super) mod test {
};
use super::*;
- use std::fmt;
+
+ use regex::Regex;
+ use std::{fmt, path::Path};
pub(crate) struct MockTime;
impl FormatTime for MockTime {
@@ -1550,7 +1748,7 @@ pub(super) mod test {
.with_thread_names(false);
#[cfg(feature = "ansi")]
let subscriber = subscriber.with_ansi(false);
- run_test(subscriber, make_writer, "hello\n")
+ assert_info_hello(subscriber, make_writer, "hello\n")
}
fn test_ansi<T>(
@@ -1598,6 +1796,124 @@ pub(super) mod test {
run_test(subscriber, make_writer, expected);
}
+ #[test]
+ fn with_line_number_and_file_name() {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .with_writer(make_writer.clone())
+ .with_file(true)
+ .with_line_number(true)
+ .with_level(false)
+ .with_ansi(false)
+ .with_timer(MockTime);
+
+ let expected = Regex::new(&format!(
+ "^fake time tracing_subscriber::fmt::format::test: {}:[0-9]+: hello\n$",
+ current_path()
+ // if we're on Windows, the path might contain backslashes, which
+ // have to be escpaed before compiling the regex.
+ .replace('\\', "\\\\")
+ ))
+ .unwrap();
+ let _default = set_default(&subscriber.into());
+ tracing::info!("hello");
+ let res = make_writer.get_string();
+ assert!(expected.is_match(&res));
+ }
+
+ #[test]
+ fn with_line_number() {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .with_writer(make_writer.clone())
+ .with_line_number(true)
+ .with_level(false)
+ .with_ansi(false)
+ .with_timer(MockTime);
+
+ let expected =
+ Regex::new("^fake time tracing_subscriber::fmt::format::test: [0-9]+: hello\n$")
+ .unwrap();
+ let _default = set_default(&subscriber.into());
+ tracing::info!("hello");
+ let res = make_writer.get_string();
+ assert!(expected.is_match(&res));
+ }
+
+ #[test]
+ fn with_filename() {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .with_writer(make_writer.clone())
+ .with_file(true)
+ .with_level(false)
+ .with_ansi(false)
+ .with_timer(MockTime);
+ let expected = &format!(
+ "fake time tracing_subscriber::fmt::format::test: {}: hello\n",
+ current_path(),
+ );
+ assert_info_hello(subscriber, make_writer, expected);
+ }
+
+ #[test]
+ fn with_thread_ids() {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .with_writer(make_writer.clone())
+ .with_thread_ids(true)
+ .with_ansi(false)
+ .with_timer(MockTime);
+ let expected =
+ "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n";
+
+ assert_info_hello_ignore_numeric(subscriber, make_writer, expected);
+ }
+
+ #[test]
+ fn pretty_default() {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .pretty()
+ .with_writer(make_writer.clone())
+ .with_ansi(false)
+ .with_timer(MockTime);
+ let expected = format!(
+ r#" fake time INFO tracing_subscriber::fmt::format::test: hello
+ at {}:NUMERIC
+
+"#,
+ file!()
+ );
+
+ assert_info_hello_ignore_numeric(subscriber, make_writer, &expected)
+ }
+
+ fn assert_info_hello(subscriber: impl Into<Dispatch>, buf: MockMakeWriter, expected: &str) {
+ let _default = set_default(&subscriber.into());
+ tracing::info!("hello");
+ let result = buf.get_string();
+
+ assert_eq!(expected, result)
+ }
+
+ // When numeric characters are used they often form a non-deterministic value as they usually represent things like a thread id or line number.
+ // This assert method should be used when non-deterministic numeric characters are present.
+ fn assert_info_hello_ignore_numeric(
+ subscriber: impl Into<Dispatch>,
+ buf: MockMakeWriter,
+ expected: &str,
+ ) {
+ let _default = set_default(&subscriber.into());
+ tracing::info!("hello");
+
+ let regex = Regex::new("[0-9]+").unwrap();
+ let result = buf.get_string();
+ let result_cleaned = regex.replace_all(&result, "NUMERIC");
+
+ assert_eq!(expected, result_cleaned)
+ }
+
fn test_overridden_parents<T>(
expected: &str,
builder: crate::fmt::SubscriberBuilder<DefaultFields, Format<T>>,
@@ -1606,14 +1922,14 @@ pub(super) mod test {
T: Send + Sync + 'static,
{
let make_writer = MockMakeWriter::default();
- let collector = builder
+ let subscriber = builder
.with_writer(make_writer.clone())
.with_level(false)
.with_ansi(false)
.with_timer(MockTime)
.finish();
- with_default(collector, || {
+ with_default(subscriber, || {
let span1 = tracing::info_span!("span1", span = 1);
let span2 = tracing::info_span!(parent: &span1, "span2", span = 2);
tracing::info!(parent: &span2, "hello");
@@ -1659,6 +1975,21 @@ pub(super) mod test {
mod default {
use super::*;
+
+ #[test]
+ fn with_thread_ids() {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .with_writer(make_writer.clone())
+ .with_thread_ids(true)
+ .with_ansi(false)
+ .with_timer(MockTime);
+ let expected =
+ "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n";
+
+ assert_info_hello_ignore_numeric(subscriber, make_writer, expected);
+ }
+
#[cfg(feature = "ansi")]
#[test]
fn with_ansi_true() {
@@ -1748,6 +2079,26 @@ pub(super) mod test {
}
}
+ mod pretty {
+ use super::*;
+
+ #[test]
+ fn pretty_default() {
+ let make_writer = MockMakeWriter::default();
+ let subscriber = crate::fmt::Subscriber::builder()
+ .pretty()
+ .with_writer(make_writer.clone())
+ .with_ansi(false)
+ .with_timer(MockTime);
+ let expected = format!(
+ " fake time INFO tracing_subscriber::fmt::format::test: hello\n at {}:NUMERIC\n\n",
+ file!()
+ );
+
+ assert_info_hello_ignore_numeric(subscriber, make_writer, &expected)
+ }
+ }
+
#[test]
fn format_nanos() {
fn fmt(t: u64) -> String {
@@ -1795,4 +2146,16 @@ pub(super) mod test {
assert!(!f.contains(FmtSpan::EXIT));
assert!(f.contains(FmtSpan::CLOSE));
}
+
+ /// Returns the test's module path.
+ fn current_path() -> String {
+ Path::new("tracing-subscriber")
+ .join("src")
+ .join("fmt")
+ .join("format")
+ .join("mod.rs")
+ .to_str()
+ .expect("path must not contain invalid unicode")
+ .to_owned()
+ }
}
diff --git a/vendor/tracing-subscriber/src/fmt/format/pretty.rs b/vendor/tracing-subscriber/src/fmt/format/pretty.rs
new file mode 100644
index 000000000..12071de92
--- /dev/null
+++ b/vendor/tracing-subscriber/src/fmt/format/pretty.rs
@@ -0,0 +1,511 @@
+use super::*;
+use crate::{
+ field::{VisitFmt, VisitOutput},
+ fmt::fmt_layer::{FmtContext, FormattedFields},
+ registry::LookupSpan,
+};
+
+use std::fmt;
+use tracing_core::{
+ field::{self, Field},
+ Event, Level, Subscriber,
+};
+
+#[cfg(feature = "tracing-log")]
+use tracing_log::NormalizeEvent;
+
+use nu_ansi_term::{Color, Style};
+
+/// An excessively pretty, human-readable event formatter.
+///
+/// Unlike the [`Full`], [`Compact`], and [`Json`] formatters, this is a
+/// multi-line output format. Each individual event may output multiple lines of
+/// text.
+///
+/// # Example Output
+///
+/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-pretty
+/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
+/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-pretty`
+/// 2022-02-15T18:44:24.535324Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: preparing to shave yaks, </font><font color="#4E9A06"><b>number_of_yaks</b></font><font color="#4E9A06">: 3</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:16 <font color="#AAAAAA"><i>on</i></font> main
+///
+/// 2022-02-15T18:44:24.535403Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty::yak_shave</b></font><font color="#4E9A06">: shaving yaks</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:41 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535442Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535469Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535502Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 1, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535524Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 1</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535551Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535573Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535600Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 2, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535618Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535644Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535670Z <font color="#C4A000"> WARN</font> <font color="#C4A000"><b>fmt_pretty::yak_shave</b></font><font color="#C4A000">: could not locate yak</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:18 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535698Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 3, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: false</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535720Z <font color="#CC0000">ERROR</font> <font color="#CC0000"><b>fmt_pretty::yak_shave</b></font><font color="#CC0000">: failed to shave yak, </font><font color="#CC0000"><b>yak</b></font><font color="#CC0000">: 3, </font><font color="#CC0000"><b>error</b></font><font color="#CC0000">: missing yak, </font><font color="#CC0000"><b>error.sources</b></font><font color="#CC0000">: [out of space, out of cash]</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:51 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535742Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
+/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
+///
+/// 2022-02-15T18:44:24.535765Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: yak shaving completed, </font><font color="#4E9A06"><b>all_yaks_shaved</b></font><font color="#4E9A06">: false</font>
+/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:19 <font color="#AAAAAA"><i>on</i></font> main
+/// </pre>
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Pretty {
+ display_location: bool,
+}
+
+/// The [visitor] produced by [`Pretty`]'s [`MakeVisitor`] implementation.
+///
+/// [visitor]: field::Visit
+/// [`MakeVisitor`]: crate::field::MakeVisitor
+#[derive(Debug)]
+pub struct PrettyVisitor<'a> {
+ writer: Writer<'a>,
+ is_empty: bool,
+ style: Style,
+ result: fmt::Result,
+}
+
+/// An excessively pretty, human-readable [`MakeVisitor`] implementation.
+///
+/// [`MakeVisitor`]: crate::field::MakeVisitor
+#[derive(Debug)]
+pub struct PrettyFields {
+ /// A value to override the provided `Writer`'s ANSI formatting
+ /// configuration.
+ ///
+ /// If this is `Some`, we override the `Writer`'s ANSI setting. This is
+ /// necessary in order to continue supporting the deprecated
+ /// `PrettyFields::with_ansi` method. If it is `None`, we don't override the
+ /// ANSI formatting configuration (because the deprecated method was not
+ /// called).
+ // TODO: when `PrettyFields::with_ansi` is removed, we can get rid
+ // of this entirely.
+ ansi: Option<bool>,
+}
+
+// === impl Pretty ===
+
+impl Default for Pretty {
+ fn default() -> Self {
+ Self {
+ display_location: true,
+ }
+ }
+}
+
+impl Pretty {
+ fn style_for(level: &Level) -> Style {
+ match *level {
+ Level::TRACE => Style::new().fg(Color::Purple),
+ Level::DEBUG => Style::new().fg(Color::Blue),
+ Level::INFO => Style::new().fg(Color::Green),
+ Level::WARN => Style::new().fg(Color::Yellow),
+ Level::ERROR => Style::new().fg(Color::Red),
+ }
+ }
+
+ /// Sets whether the event's source code location is displayed.
+ ///
+ /// This defaults to `true`.
+ #[deprecated(
+ since = "0.3.6",
+ note = "all formatters now support configurable source locations. Use `Format::with_source_location` instead."
+ )]
+ pub fn with_source_location(self, display_location: bool) -> Self {
+ Self {
+ display_location,
+ ..self
+ }
+ }
+}
+
+impl<C, N, T> FormatEvent<C, N> for Format<Pretty, T>
+where
+ C: Subscriber + for<'a> LookupSpan<'a>,
+ N: for<'a> FormatFields<'a> + 'static,
+ T: FormatTime,
+{
+ fn format_event(
+ &self,
+ ctx: &FmtContext<'_, C, N>,
+ mut writer: Writer<'_>,
+ event: &Event<'_>,
+ ) -> fmt::Result {
+ #[cfg(feature = "tracing-log")]
+ let normalized_meta = event.normalized_metadata();
+ #[cfg(feature = "tracing-log")]
+ let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
+ #[cfg(not(feature = "tracing-log"))]
+ let meta = event.metadata();
+ write!(&mut writer, " ")?;
+
+ // if the `Format` struct *also* has an ANSI color configuration,
+ // override the writer...the API for configuring ANSI color codes on the
+ // `Format` struct is deprecated, but we still need to honor those
+ // configurations.
+ if let Some(ansi) = self.ansi {
+ writer = writer.with_ansi(ansi);
+ }
+
+ self.format_timestamp(&mut writer)?;
+
+ let style = if self.display_level && writer.has_ansi_escapes() {
+ Pretty::style_for(meta.level())
+ } else {
+ Style::new()
+ };
+
+ if self.display_level {
+ write!(
+ writer,
+ "{} ",
+ super::FmtLevel::new(meta.level(), writer.has_ansi_escapes())
+ )?;
+ }
+
+ if self.display_target {
+ let target_style = if writer.has_ansi_escapes() {
+ style.bold()
+ } else {
+ style
+ };
+ write!(
+ writer,
+ "{}{}{}:",
+ target_style.prefix(),
+ meta.target(),
+ target_style.infix(style)
+ )?;
+ }
+ let line_number = if self.display_line_number {
+ meta.line()
+ } else {
+ None
+ };
+
+ // If the file name is disabled, format the line number right after the
+ // target. Otherwise, if we also display the file, it'll go on a
+ // separate line.
+ if let (Some(line_number), false, true) = (
+ line_number,
+ self.display_filename,
+ self.format.display_location,
+ ) {
+ write!(
+ writer,
+ "{}{}{}:",
+ style.prefix(),
+ line_number,
+ style.infix(style)
+ )?;
+ }
+
+ writer.write_char(' ')?;
+
+ let mut v = PrettyVisitor::new(writer.by_ref(), true).with_style(style);
+ event.record(&mut v);
+ v.finish()?;
+ writer.write_char('\n')?;
+
+ let dimmed = if writer.has_ansi_escapes() {
+ Style::new().dimmed().italic()
+ } else {
+ Style::new()
+ };
+ let thread = self.display_thread_name || self.display_thread_id;
+
+ if let (Some(file), true, true) = (
+ meta.file(),
+ self.format.display_location,
+ self.display_filename,
+ ) {
+ write!(writer, " {} {}", dimmed.paint("at"), file,)?;
+
+ if let Some(line) = line_number {
+ write!(writer, ":{}", line)?;
+ }
+ writer.write_char(if thread { ' ' } else { '\n' })?;
+ } else if thread {
+ write!(writer, " ")?;
+ };
+
+ if thread {
+ write!(writer, "{} ", dimmed.paint("on"))?;
+ let thread = std::thread::current();
+ if self.display_thread_name {
+ if let Some(name) = thread.name() {
+ write!(writer, "{}", name)?;
+ if self.display_thread_id {
+ writer.write_char(' ')?;
+ }
+ }
+ }
+ if self.display_thread_id {
+ write!(writer, "{:?}", thread.id())?;
+ }
+ writer.write_char('\n')?;
+ }
+
+ let bold = writer.bold();
+ let span = event
+ .parent()
+ .and_then(|id| ctx.span(id))
+ .or_else(|| ctx.lookup_current());
+
+ let scope = span.into_iter().flat_map(|span| span.scope());
+
+ for span in scope {
+ let meta = span.metadata();
+ if self.display_target {
+ write!(
+ writer,
+ " {} {}::{}",
+ dimmed.paint("in"),
+ meta.target(),
+ bold.paint(meta.name()),
+ )?;
+ } else {
+ write!(
+ writer,
+ " {} {}",
+ dimmed.paint("in"),
+ bold.paint(meta.name()),
+ )?;
+ }
+
+ let ext = span.extensions();
+ let fields = &ext
+ .get::<FormattedFields<N>>()
+ .expect("Unable to find FormattedFields in extensions; this is a bug");
+ if !fields.is_empty() {
+ write!(writer, " {} {}", dimmed.paint("with"), fields)?;
+ }
+ writer.write_char('\n')?;
+ }
+
+ writer.write_char('\n')
+ }
+}
+
+impl<'writer> FormatFields<'writer> for Pretty {
+ fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result {
+ let mut v = PrettyVisitor::new(writer, true);
+ fields.record(&mut v);
+ v.finish()
+ }
+
+ fn add_fields(
+ &self,
+ current: &'writer mut FormattedFields<Self>,
+ fields: &span::Record<'_>,
+ ) -> fmt::Result {
+ let empty = current.is_empty();
+ let writer = current.as_writer();
+ let mut v = PrettyVisitor::new(writer, empty);
+ fields.record(&mut v);
+ v.finish()
+ }
+}
+
+// === impl PrettyFields ===
+
+impl Default for PrettyFields {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl PrettyFields {
+ /// Returns a new default [`PrettyFields`] implementation.
+ pub fn new() -> Self {
+ // By default, don't override the `Writer`'s ANSI colors
+ // configuration. We'll only do this if the user calls the
+ // deprecated `PrettyFields::with_ansi` method.
+ Self { ansi: None }
+ }
+
+ /// Enable ANSI encoding for formatted fields.
+ #[deprecated(
+ since = "0.3.3",
+ note = "Use `fmt::Subscriber::with_ansi` or `fmt::Layer::with_ansi` instead."
+ )]
+ pub fn with_ansi(self, ansi: bool) -> Self {
+ Self {
+ ansi: Some(ansi),
+ ..self
+ }
+ }
+}
+
+impl<'a> MakeVisitor<Writer<'a>> for PrettyFields {
+ type Visitor = PrettyVisitor<'a>;
+
+ #[inline]
+ fn make_visitor(&self, mut target: Writer<'a>) -> Self::Visitor {
+ if let Some(ansi) = self.ansi {
+ target = target.with_ansi(ansi);
+ }
+ PrettyVisitor::new(target, true)
+ }
+}
+
+// === impl PrettyVisitor ===
+
+impl<'a> PrettyVisitor<'a> {
+ /// Returns a new default visitor that formats to the provided `writer`.
+ ///
+ /// # Arguments
+ /// - `writer`: the writer to format to.
+ /// - `is_empty`: whether or not any fields have been previously written to
+ /// that writer.
+ pub fn new(writer: Writer<'a>, is_empty: bool) -> Self {
+ Self {
+ writer,
+ is_empty,
+ style: Style::default(),
+ result: Ok(()),
+ }
+ }
+
+ pub(crate) fn with_style(self, style: Style) -> Self {
+ Self { style, ..self }
+ }
+
+ fn write_padded(&mut self, value: &impl fmt::Debug) {
+ let padding = if self.is_empty {
+ self.is_empty = false;
+ ""
+ } else {
+ ", "
+ };
+ self.result = write!(self.writer, "{}{:?}", padding, value);
+ }
+
+ fn bold(&self) -> Style {
+ if self.writer.has_ansi_escapes() {
+ self.style.bold()
+ } else {
+ Style::new()
+ }
+ }
+}
+
+impl<'a> field::Visit for PrettyVisitor<'a> {
+ fn record_str(&mut self, field: &Field, value: &str) {
+ if self.result.is_err() {
+ return;
+ }
+
+ if field.name() == "message" {
+ self.record_debug(field, &format_args!("{}", value))
+ } else {
+ self.record_debug(field, &value)
+ }
+ }
+
+ fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
+ if let Some(source) = value.source() {
+ let bold = self.bold();
+ self.record_debug(
+ field,
+ &format_args!(
+ "{}, {}{}.sources{}: {}",
+ value,
+ bold.prefix(),
+ field,
+ bold.infix(self.style),
+ ErrorSourceList(source),
+ ),
+ )
+ } else {
+ self.record_debug(field, &format_args!("{}", value))
+ }
+ }
+
+ fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
+ if self.result.is_err() {
+ return;
+ }
+ let bold = self.bold();
+ match field.name() {
+ "message" => self.write_padded(&format_args!("{}{:?}", self.style.prefix(), value,)),
+ // Skip fields that are actually log metadata that have already been handled
+ #[cfg(feature = "tracing-log")]
+ name if name.starts_with("log.") => self.result = Ok(()),
+ name if name.starts_with("r#") => self.write_padded(&format_args!(
+ "{}{}{}: {:?}",
+ bold.prefix(),
+ &name[2..],
+ bold.infix(self.style),
+ value
+ )),
+ name => self.write_padded(&format_args!(
+ "{}{}{}: {:?}",
+ bold.prefix(),
+ name,
+ bold.infix(self.style),
+ value
+ )),
+ };
+ }
+}
+
+impl<'a> VisitOutput<fmt::Result> for PrettyVisitor<'a> {
+ fn finish(mut self) -> fmt::Result {
+ write!(&mut self.writer, "{}", self.style.suffix())?;
+ self.result
+ }
+}
+
+impl<'a> VisitFmt for PrettyVisitor<'a> {
+ fn writer(&mut self) -> &mut dyn fmt::Write {
+ &mut self.writer
+ }
+}
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/mod.rs b/vendor/tracing-subscriber/src/fmt/mod.rs
index d5deb8f0c..025e17504 100644
--- a/vendor/tracing-subscriber-0.3.3/src/fmt/mod.rs
+++ b/vendor/tracing-subscriber/src/fmt/mod.rs
@@ -13,10 +13,12 @@
//!
//! ```toml
//! [dependencies]
-//! tracing-subscriber = "0.2"
+//! tracing-subscriber = "0.3"
//! ```
//!
-//! *Compiler support: requires rustc 1.39+*
+//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//!
+//! [msrv]: super#supported-rust-versions
//!
//! Add the following to your executable to initialize the default subscriber:
//! ```rust
@@ -68,132 +70,25 @@
//!
//! * [`format::Full`]: The default formatter. This emits human-readable,
//! single-line logs for each event that occurs, with the current span context
-//! displayed before the formatted representation of the event.
+//! displayed before the formatted representation of the event. See
+//! [here](format::Full#example-output) for sample output.
//!
-//! For example:
-//! <pre><font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 1.59s
-//! <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt`
-//! <font color="#AAAAAA">Oct 24 12:55:47.814 </font><font color="#4E9A06"> INFO</font> fmt: preparing to shave yaks number_of_yaks=3
-//! <font color="#AAAAAA">Oct 24 12:55:47.814 </font><font color="#4E9A06"> INFO</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: fmt::yak_shave: shaving yaks
-//! <font color="#AAAAAA">Oct 24 12:55:47.814 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>:<b>shave{</b>yak=1<b>}</b>: fmt::yak_shave: hello! I&apos;m gonna shave a yak excitement=&quot;yay!&quot;
-//! <font color="#AAAAAA">Oct 24 12:55:47.814 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>:<b>shave{</b>yak=1<b>}</b>: fmt::yak_shave: yak shaved successfully
-//! <font color="#AAAAAA">Oct 24 12:55:47.814 </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: yak_events: yak=1 shaved=true
-//! <font color="#AAAAAA">Oct 24 12:55:47.814 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: fmt::yak_shave: yaks_shaved=1
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>:<b>shave{</b>yak=2<b>}</b>: fmt::yak_shave: hello! I&apos;m gonna shave a yak excitement=&quot;yay!&quot;
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>:<b>shave{</b>yak=2<b>}</b>: fmt::yak_shave: yak shaved successfully
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: yak_events: yak=2 shaved=true
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: fmt::yak_shave: yaks_shaved=2
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>:<b>shave{</b>yak=3<b>}</b>: fmt::yak_shave: hello! I&apos;m gonna shave a yak excitement=&quot;yay!&quot;
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#C4A000"> WARN</font> <b>shaving_yaks{</b>yaks=3<b>}</b>:<b>shave{</b>yak=3<b>}</b>: fmt::yak_shave: could not locate yak
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: yak_events: yak=3 shaved=false
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#CC0000">ERROR</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: fmt::yak_shave: failed to shave yak yak=3 error=missing yak
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b>yaks=3<b>}</b>: fmt::yak_shave: yaks_shaved=2
-//! <font color="#AAAAAA">Oct 24 12:55:47.815 </font><font color="#4E9A06"> INFO</font> fmt: yak shaving completed all_yaks_shaved=false
-//! </pre>
+//! * [`format::Compact`]: A variant of the default formatter, optimized for
+//! short line lengths. Fields from the current span context are appended to
+//! the fields of the formatted event. See
+//! [here](format::Compact#example-output) for sample output.
//!
//! * [`format::Pretty`]: Emits excessively pretty, multi-line logs, optimized
//! for human readability. This is primarily intended to be used in local
//! development and debugging, or for command-line applications, where
//! automated analysis and compact storage of logs is less of a priority than
-//! readability and visual appeal.
-//!
-//! For example:
-//! <pre><font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 1.61s
-//! <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-pretty`
-//! Oct 24 12:57:29.386 <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: preparing to shave yaks, </font><font color="#4E9A06"><b>number_of_yaks</b></font><font color="#4E9A06">: 3</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:16<font color="#AAAAAA"><i> on</i></font> main
-//!
-//! Oct 24 12:57:29.386 <font color="#4E9A06"><b>fmt_pretty::yak_shave</b></font><font color="#4E9A06">: shaving yaks</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:38<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:14<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:22<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 1, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:43<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 1</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:52<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:14<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:22<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 2, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:43<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:52<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:14<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#C4A000"><b>fmt_pretty::yak_shave</b></font><font color="#C4A000">: could not locate yak</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 3, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: false</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:43<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#CC0000"><b>fmt_pretty::yak_shave</b></font><font color="#CC0000">: failed to shave yak, </font><font color="#CC0000"><b>yak</b></font><font color="#CC0000">: 3, </font><font color="#CC0000"><b>error</b></font><font color="#CC0000">: missing yak</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:48<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:52<font color="#AAAAAA"><i> on</i></font> main
-//! <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
-//!
-//! Oct 24 12:57:29.387 <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: yak shaving completed, </font><font color="#4E9A06"><b>all_yaks_shaved</b></font><font color="#4E9A06">: false</font>
-//! <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:19<font color="#AAAAAA"><i> on</i></font> main
-//! </pre>
+//! readability and visual appeal. See [here](format::Pretty#example-output)
+//! for sample output.
//!
//! * [`format::Json`]: Outputs newline-delimited JSON logs. This is intended
//! for production use with systems where structured logs are consumed as JSON
-//! by analysis and viewing tools. The JSON output, as seen below, is *not*
-//! optimized for human readability.
-//!
-//! For example:
-//! <pre><font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 1.58s
-//! <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-json`
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.873&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;preparing to shave yaks&quot;,&quot;number_of_yaks&quot;:3},&quot;target&quot;:&quot;fmt_json&quot;}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;shaving yaks&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:&quot;1&quot;,&quot;name&quot;:&quot;shave&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaved successfully&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:&quot;1&quot;,&quot;name&quot;:&quot;shave&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:1,&quot;shaved&quot;:true},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:1},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:&quot;2&quot;,&quot;name&quot;:&quot;shave&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaved successfully&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:&quot;2&quot;,&quot;name&quot;:&quot;shave&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:2,&quot;shaved&quot;:true},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:2},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.874&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:&quot;3&quot;,&quot;name&quot;:&quot;shave&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.875&quot;,&quot;level&quot;:&quot;WARN&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;could not locate yak&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:&quot;3&quot;,&quot;name&quot;:&quot;shave&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.875&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:3,&quot;shaved&quot;:false},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.875&quot;,&quot;level&quot;:&quot;ERROR&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;failed to shave yak&quot;,&quot;yak&quot;:3,&quot;error&quot;:&quot;missing yak&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.875&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:2},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
-//! {&quot;timestamp&quot;:&quot;Oct 24 13:00:00.875&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaving completed&quot;,&quot;all_yaks_shaved&quot;:false},&quot;target&quot;:&quot;fmt_json&quot;}
-//! </pre>
+//! by analysis and viewing tools. The JSON output is not optimized for human
+//! readability. See [here](format::Json#example-output) for sample output.
//!
//! ### Customizing Formatters
//!
@@ -221,7 +116,7 @@
//! .with_thread_names(true) // include the name of the current thread
//! .compact(); // use the `Compact` formatting style.
//!
-//! // Create a `fmt` collector that uses our custom event format, and set it
+//! // Create a `fmt` subscriber that uses our custom event format, and set it
//! // as the default.
//! tracing_subscriber::fmt()
//! .event_format(format)
@@ -268,7 +163,7 @@
//!
//! ### Composing Layers
//!
-//! Composing an [`EnvFilter`] `Layer` and a [format `Layer`](../fmt/struct.Layer.html):
+//! Composing an [`EnvFilter`] `Layer` and a [format `Layer`][super::fmt::Layer]:
//!
//! ```rust
//! use tracing_subscriber::{fmt, EnvFilter};
@@ -286,11 +181,10 @@
//! .init();
//! ```
//!
-//! [`EnvFilter`]: ../filter/struct.EnvFilter.html
+//! [`EnvFilter`]: super::filter::EnvFilter
//! [`env_logger`]: https://docs.rs/env_logger/
-//! [`filter`]: ../filter/index.html
-//! [`SubscriberBuilder`]: ./struct.SubscriberBuilder.html
-//! [`FmtSubscriber`]: ./struct.Subscriber.html
+//! [`filter`]: super::filter
+//! [`FmtSubscriber`]: Subscriber
//! [`Subscriber`]:
//! https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
//! [`tracing`]: https://crates.io/crates/tracing
@@ -309,6 +203,7 @@ pub mod writer;
pub use fmt_layer::{FmtContext, FormattedFields, Layer};
use crate::layer::Layer as _;
+use crate::util::SubscriberInitExt;
use crate::{
filter::LevelFilter,
layer,
@@ -348,6 +243,7 @@ pub type Formatter<
/// Configures and constructs `Subscriber`s.
#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))]
#[derive(Debug)]
+#[must_use]
pub struct SubscriberBuilder<
N = format::DefaultFields,
E = format::Format<format::Full>,
@@ -417,7 +313,7 @@ pub struct SubscriberBuilder<
/// ```
///
/// [formatting subscriber]: Subscriber
-/// [`SubscriberBuilder::default()`]: SubscriberBuilder::default()
+/// [`SubscriberBuilder::default()`]: SubscriberBuilder::default
/// [`init`]: SubscriberBuilder::init()
/// [`try_init`]: SubscriberBuilder::try_init()
/// [`finish`]: SubscriberBuilder::finish()
@@ -429,10 +325,11 @@ pub fn fmt() -> SubscriberBuilder {
/// Returns a new [formatting layer] that can be [composed] with other layers to
/// construct a [`Subscriber`].
///
-/// This is a shorthand for the equivalent [`Layer::default`] function.
+/// This is a shorthand for the equivalent [`Layer::default()`] function.
///
/// [formatting layer]: Layer
/// [composed]: crate::layer
+/// [`Layer::default()`]: Layer::default
#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))]
pub fn layer<S>() -> Layer<S> {
Layer::default()
@@ -444,8 +341,8 @@ impl Subscriber {
///
/// This can be overridden with the [`SubscriberBuilder::with_max_level`] method.
///
- /// [verbosity level]: https://docs.rs/tracing-core/0.1.5/tracing_core/struct.Level.html
- /// [`SubscriberBuilder::with_max_level`]: struct.SubscriberBuilder.html#method.with_max_level
+ /// [verbosity level]: tracing_core::Level
+ /// [`SubscriberBuilder::with_max_level`]: SubscriberBuilder::with_max_level
pub const DEFAULT_MAX_LEVEL: LevelFilter = LevelFilter::INFO;
/// Returns a new `SubscriberBuilder` for configuring a format subscriber.
@@ -502,6 +399,11 @@ where
}
#[inline]
+ fn event_enabled(&self, event: &Event<'_>) -> bool {
+ self.inner.event_enabled(event)
+ }
+
+ #[inline]
fn event(&self, event: &Event<'_>) {
self.inner.event(event);
}
@@ -565,6 +467,7 @@ impl Default for SubscriberBuilder {
filter: Subscriber::DEFAULT_MAX_LEVEL,
inner: Default::default(),
}
+ .log_internal_errors(true)
}
}
@@ -701,7 +604,7 @@ where
/// `Layer`s added to this subscriber.
///
/// [lifecycle]: https://docs.rs/tracing/latest/tracing/span/index.html#the-span-lifecycle
- /// [time]: #method.without_time
+ /// [time]: SubscriberBuilder::without_time()
pub fn with_span_events(self, kind: format::FmtSpan) -> Self {
SubscriberBuilder {
inner: self.inner.with_span_events(kind),
@@ -719,6 +622,27 @@ where
}
}
+ /// Sets whether to write errors from [`FormatEvent`] to the writer.
+ /// Defaults to true.
+ ///
+ /// By default, `fmt::Layer` will write any `FormatEvent`-internal errors to
+ /// the writer. These errors are unlikely and will only occur if there is a
+ /// bug in the `FormatEvent` implementation or its dependencies.
+ ///
+ /// If writing to the writer fails, the error message is printed to stderr
+ /// as a fallback.
+ ///
+ /// [`FormatEvent`]: crate::fmt::FormatEvent
+ pub fn log_internal_errors(
+ self,
+ log_internal_errors: bool,
+ ) -> SubscriberBuilder<N, format::Format<L, T>, F, W> {
+ SubscriberBuilder {
+ inner: self.inner.log_internal_errors(log_internal_errors),
+ ..self
+ }
+ }
+
/// Sets whether or not an event's target is displayed.
pub fn with_target(
self,
@@ -730,6 +654,34 @@ where
}
}
+ /// Sets whether or not an event's [source code file path][file] is
+ /// displayed.
+ ///
+ /// [file]: tracing_core::Metadata::file
+ pub fn with_file(
+ self,
+ display_filename: bool,
+ ) -> SubscriberBuilder<N, format::Format<L, T>, F, W> {
+ SubscriberBuilder {
+ inner: self.inner.with_file(display_filename),
+ ..self
+ }
+ }
+
+ /// Sets whether or not an event's [source code line number][line] is
+ /// displayed.
+ ///
+ /// [line]: tracing_core::Metadata::line
+ pub fn with_line_number(
+ self,
+ display_line_number: bool,
+ ) -> SubscriberBuilder<N, format::Format<L, T>, F, W> {
+ SubscriberBuilder {
+ inner: self.inner.with_line_number(display_line_number),
+ ..self
+ }
+ }
+
/// Sets whether or not an event's level is displayed.
pub fn with_level(
self,
@@ -742,9 +694,9 @@ where
}
/// Sets whether or not the [name] of the current thread is displayed
- /// when formatting events
+ /// when formatting events.
///
- /// [name]: https://doc.rust-lang.org/stable/std/thread/index.html#naming-threads
+ /// [name]: std::thread#naming-threads
pub fn with_thread_names(
self,
display_thread_names: bool,
@@ -756,9 +708,9 @@ where
}
/// Sets whether or not the [thread ID] of the current thread is displayed
- /// when formatting events
+ /// when formatting events.
///
- /// [thread ID]: https://doc.rust-lang.org/stable/std/thread/struct.ThreadId.html
+ /// [thread ID]: std::thread::ThreadId
pub fn with_thread_ids(
self,
display_thread_ids: bool,
@@ -796,7 +748,7 @@ where
/// Sets the subscriber being built to use a JSON formatter.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][super::fmt::format::Json]
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn json(
@@ -817,7 +769,7 @@ where
impl<T, F, W> SubscriberBuilder<format::JsonFields, format::Format<format::Json, T>, F, W> {
/// Sets the json subscriber being built to flatten event metadata.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][super::fmt::format::Json]
pub fn flatten_event(
self,
flatten_event: bool,
@@ -831,7 +783,7 @@ impl<T, F, W> SubscriberBuilder<format::JsonFields, format::Format<format::Json,
/// Sets whether or not the JSON subscriber being built will include the current span
/// in formatted events.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][super::fmt::format::Json]
pub fn with_current_span(
self,
display_current_span: bool,
@@ -845,7 +797,7 @@ impl<T, F, W> SubscriberBuilder<format::JsonFields, format::Format<format::Json,
/// Sets whether or not the JSON subscriber being built will include a list (from
/// root to leaf) of all currently entered spans in formatted events.
///
- /// See [`format::Json`](../fmt/format/struct.Json.html)
+ /// See [`format::Json`][super::fmt::format::Json]
pub fn with_span_list(
self,
display_span_list: bool,
@@ -891,7 +843,7 @@ where
}
impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
- /// Sets the Visitor that the subscriber being built will use to record
+ /// Sets the field formatter that the subscriber being built will use to record
/// fields.
///
/// For example:
@@ -967,8 +919,8 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// .try_init()?;
/// # Ok(())}
/// ```
- /// [`EnvFilter`]: ../filter/struct.EnvFilter.html
- /// [`with_max_level`]: #method.with_max_level
+ /// [`EnvFilter`]: super::filter::EnvFilter
+ /// [`with_max_level`]: SubscriberBuilder::with_max_level()
#[cfg(feature = "env-filter")]
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
pub fn with_env_filter(
@@ -989,7 +941,7 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// subscriber.
///
/// If the max level has already been set, or a [`EnvFilter`] was added by
- /// [`with_filter`], this replaces that configuration with the new
+ /// [`with_env_filter`], this replaces that configuration with the new
/// maximum level.
///
/// # Examples
@@ -1011,9 +963,9 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// .with_max_level(LevelFilter::OFF)
/// .finish();
/// ```
- /// [verbosity level]: https://docs.rs/tracing-core/0.1.5/tracing_core/struct.Level.html
- /// [`EnvFilter`]: ../filter/struct.EnvFilter.html
- /// [`with_filter`]: #method.with_filter
+ /// [verbosity level]: tracing_core::Level
+ /// [`EnvFilter`]: struct@crate::filter::EnvFilter
+ /// [`with_env_filter`]: fn@Self::with_env_filter
pub fn with_max_level(
self,
filter: impl Into<LevelFilter>,
@@ -1025,8 +977,26 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
}
}
- /// Sets the function that the subscriber being built should use to format
- /// events that occur.
+ /// Sets the [event formatter][`FormatEvent`] that the subscriber being built
+ /// will use to format events that occur.
+ ///
+ /// The event formatter may be any type implementing the [`FormatEvent`]
+ /// trait, which is implemented for all functions taking a [`FmtContext`], a
+ /// [`Writer`], and an [`Event`].
+ ///
+ /// # Examples
+ ///
+ /// Setting a type implementing [`FormatEvent`] as the formatter:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::fmt::format;
+ ///
+ /// let subscriber = tracing_subscriber::fmt()
+ /// .event_format(format().compact())
+ /// .finish();
+ /// ```
+ ///
+ /// [`Writer`]: struct@self::format::Writer
pub fn event_format<E2>(self, fmt_event: E2) -> SubscriberBuilder<N, E2, F, W>
where
E2: FormatEvent<Registry, N> + 'static,
@@ -1053,8 +1023,6 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// .with_writer(io::stderr)
/// .init();
/// ```
- ///
- /// [`MakeWriter`]: trait.MakeWriter.html
pub fn with_writer<W2>(self, make_writer: W2) -> SubscriberBuilder<N, E, F, W2>
where
W2: for<'writer> MakeWriter<'writer> + 'static,
@@ -1088,13 +1056,89 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
///
/// [capturing]:
/// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
- /// [`TestWriter`]: writer/struct.TestWriter.html
+ /// [`TestWriter`]: writer::TestWriter
pub fn with_test_writer(self) -> SubscriberBuilder<N, E, F, TestWriter> {
SubscriberBuilder {
filter: self.filter,
inner: self.inner.with_writer(TestWriter::default()),
}
}
+
+ /// Updates the event formatter by applying a function to the existing event formatter.
+ ///
+ /// This sets the event formatter that the subscriber being built will use to record fields.
+ ///
+ /// # Examples
+ ///
+ /// Updating an event formatter:
+ ///
+ /// ```rust
+ /// let subscriber = tracing_subscriber::fmt()
+ /// .map_event_format(|e| e.compact())
+ /// .finish();
+ /// ```
+ pub fn map_event_format<E2>(self, f: impl FnOnce(E) -> E2) -> SubscriberBuilder<N, E2, F, W>
+ where
+ E2: FormatEvent<Registry, N> + 'static,
+ N: for<'writer> FormatFields<'writer> + 'static,
+ W: for<'writer> MakeWriter<'writer> + 'static,
+ {
+ SubscriberBuilder {
+ filter: self.filter,
+ inner: self.inner.map_event_format(f),
+ }
+ }
+
+ /// Updates the field formatter by applying a function to the existing field formatter.
+ ///
+ /// This sets the field formatter that the subscriber being built will use to record fields.
+ ///
+ /// # Examples
+ ///
+ /// Updating a field formatter:
+ ///
+ /// ```rust
+ /// use tracing_subscriber::field::MakeExt;
+ /// let subscriber = tracing_subscriber::fmt()
+ /// .map_fmt_fields(|f| f.debug_alt())
+ /// .finish();
+ /// ```
+ pub fn map_fmt_fields<N2>(self, f: impl FnOnce(N) -> N2) -> SubscriberBuilder<N2, E, F, W>
+ where
+ N2: for<'writer> FormatFields<'writer> + 'static,
+ {
+ SubscriberBuilder {
+ filter: self.filter,
+ inner: self.inner.map_fmt_fields(f),
+ }
+ }
+
+ /// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
+ ///
+ /// This sets the [`MakeWriter`] that the subscriber being built will use to write events.
+ ///
+ /// # Examples
+ ///
+ /// Redirect output to stderr if level is <= WARN:
+ ///
+ /// ```rust
+ /// use tracing::Level;
+ /// use tracing_subscriber::fmt::{self, writer::MakeWriterExt};
+ ///
+ /// let stderr = std::io::stderr.with_max_level(Level::WARN);
+ /// let layer = tracing_subscriber::fmt()
+ /// .map_writer(move |w| stderr.or_else(w))
+ /// .finish();
+ /// ```
+ pub fn map_writer<W2>(self, f: impl FnOnce(W) -> W2) -> SubscriberBuilder<N, E, F, W2>
+ where
+ W2: for<'writer> MakeWriter<'writer> + 'static,
+ {
+ SubscriberBuilder {
+ filter: self.filter,
+ inner: self.inner.map_writer(f),
+ }
+ }
}
/// Install a global tracing subscriber that listens for events and
@@ -1121,35 +1165,69 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
///
/// [`LogTracer`]:
/// https://docs.rs/tracing-log/0.1.0/tracing_log/struct.LogTracer.html
-/// [`RUST_LOG` environment variable]:
-/// ../filter/struct.EnvFilter.html#associatedconstant.DEFAULT_ENV
+/// [`RUST_LOG` environment variable]: crate::filter::EnvFilter::DEFAULT_ENV
pub fn try_init() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
let builder = Subscriber::builder();
#[cfg(feature = "env-filter")]
let builder = builder.with_env_filter(crate::EnvFilter::from_default_env());
- builder.try_init()
+ // If `env-filter` is disabled, remove the default max level filter from the
+ // subscriber; it will be added to the `Targets` filter instead if no filter
+ // is set in `RUST_LOG`.
+ // Replacing the default `LevelFilter` with an `EnvFilter` would imply this,
+ // but we can't replace the builder's filter with a `Targets` filter yet.
+ #[cfg(not(feature = "env-filter"))]
+ let builder = builder.with_max_level(LevelFilter::TRACE);
+
+ let subscriber = builder.finish();
+ #[cfg(not(feature = "env-filter"))]
+ let subscriber = {
+ use crate::{filter::Targets, layer::SubscriberExt};
+ use std::{env, str::FromStr};
+ let targets = match env::var("RUST_LOG") {
+ Ok(var) => Targets::from_str(&var)
+ .map_err(|e| {
+ eprintln!("Ignoring `RUST_LOG={:?}`: {}", var, e);
+ })
+ .unwrap_or_default(),
+ Err(env::VarError::NotPresent) => {
+ Targets::new().with_default(Subscriber::DEFAULT_MAX_LEVEL)
+ }
+ Err(e) => {
+ eprintln!("Ignoring `RUST_LOG`: {}", e);
+ Targets::new().with_default(Subscriber::DEFAULT_MAX_LEVEL)
+ }
+ };
+ subscriber.with(targets)
+ };
+
+ subscriber.try_init().map_err(Into::into)
}
/// Install a global tracing subscriber that listens for events and
/// filters based on the value of the [`RUST_LOG` environment variable].
///
+/// The configuration of the subscriber initialized by this function
+/// depends on what [feature flags](crate#feature-flags) are enabled.
+///
/// If the `tracing-log` feature is enabled, this will also install
/// the LogTracer to convert `Log` records into `tracing` `Event`s.
///
-/// This is shorthand for
+/// If the `env-filter` feature is enabled, this is shorthand for
///
/// ```rust
-/// tracing_subscriber::fmt().init()
+/// # use tracing_subscriber::EnvFilter;
+/// tracing_subscriber::fmt()
+/// .with_env_filter(EnvFilter::from_default_env())
+/// .init();
/// ```
///
/// # Panics
/// Panics if the initialization was unsuccessful, likely because a
/// global subscriber was already installed by another call to `try_init`.
///
-/// [`RUST_LOG` environment variable]:
-/// ../filter/struct.EnvFilter.html#associatedconstant.DEFAULT_ENV
+/// [`RUST_LOG` environment variable]: crate::filter::EnvFilter::DEFAULT_ENV
pub fn init() {
try_init().expect("Unable to install global subscriber")
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/time/datetime.rs b/vendor/tracing-subscriber/src/fmt/time/datetime.rs
index 531331687..531331687 100644
--- a/vendor/tracing-subscriber-0.3.3/src/fmt/time/datetime.rs
+++ b/vendor/tracing-subscriber/src/fmt/time/datetime.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/time/mod.rs b/vendor/tracing-subscriber/src/fmt/time/mod.rs
index 621df16e4..e5b7c83b0 100644
--- a/vendor/tracing-subscriber-0.3.3/src/fmt/time/mod.rs
+++ b/vendor/tracing-subscriber/src/fmt/time/mod.rs
@@ -12,9 +12,13 @@ mod time_crate;
pub use time_crate::UtcTime;
#[cfg(feature = "local-time")]
-#[cfg_attr(docsrs, doc(cfg(feature = "local-time")))]
+#[cfg_attr(docsrs, doc(cfg(unsound_local_offset, feature = "local-time")))]
pub use time_crate::LocalTime;
+#[cfg(feature = "time")]
+#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
+pub use time_crate::OffsetTime;
+
/// A type that can measure and format the current time.
///
/// This trait is used by `Format` to include a timestamp with each `Event` when it is logged.
@@ -26,7 +30,7 @@ pub use time_crate::LocalTime;
///
/// The full list of provided implementations can be found in [`time`].
///
-/// [`time`]: ./index.html
+/// [`time`]: self
pub trait FormatTime {
/// Measure and write out the current time.
///
diff --git a/vendor/tracing-subscriber/src/fmt/time/time_crate.rs b/vendor/tracing-subscriber/src/fmt/time/time_crate.rs
new file mode 100644
index 000000000..60d57fd0b
--- /dev/null
+++ b/vendor/tracing-subscriber/src/fmt/time/time_crate.rs
@@ -0,0 +1,470 @@
+use crate::fmt::{format::Writer, time::FormatTime, writer::WriteAdaptor};
+use std::fmt;
+use time::{format_description::well_known, formatting::Formattable, OffsetDateTime, UtcOffset};
+
+/// Formats the current [local time] using a [formatter] from the [`time` crate].
+///
+/// To format the current [UTC time] instead, use the [`UtcTime`] type.
+///
+/// <div class="example-wrap" style="display:inline-block">
+/// <pre class="compile_fail" style="white-space:normal;font:inherit;">
+/// <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/"><code>time</code>
+/// crate</a> must be compiled with <code>--cfg unsound_local_offset</code> in order to use
+/// local timestamps. When this cfg is not enabled, local timestamps cannot be recorded, and
+/// events will be logged without timestamps.
+///
+/// Alternatively, [`OffsetTime`] can log with a local offset if it is initialized early.
+///
+/// See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags"><code>time</code>
+/// documentation</a> for more details.
+/// </pre></div>
+///
+/// [local time]: time::OffsetDateTime::now_local
+/// [UTC time]: time::OffsetDateTime::now_utc
+/// [formatter]: time::formatting::Formattable
+/// [`time` crate]: time
+#[derive(Clone, Debug)]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(all(unsound_local_offset, feature = "time", feature = "local-time")))
+)]
+#[cfg(feature = "local-time")]
+pub struct LocalTime<F> {
+ format: F,
+}
+
+/// Formats the current [UTC time] using a [formatter] from the [`time` crate].
+///
+/// To format the current [local time] instead, use the [`LocalTime`] type.
+///
+/// [local time]: time::OffsetDateTime::now_local
+/// [UTC time]: time::OffsetDateTime::now_utc
+/// [formatter]: time::formatting::Formattable
+/// [`time` crate]: time
+#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
+#[derive(Clone, Debug)]
+pub struct UtcTime<F> {
+ format: F,
+}
+
+/// Formats the current time using a fixed offset and a [formatter] from the [`time` crate].
+///
+/// This is typically used as an alternative to [`LocalTime`]. `LocalTime` determines the offset
+/// every time it formats a message, which may be unsound or fail. With `OffsetTime`, the offset is
+/// determined once. This makes it possible to do so while the program is still single-threaded and
+/// handle any errors. However, this also means the offset cannot change while the program is
+/// running (the offset will not change across DST changes).
+///
+/// [formatter]: time::formatting::Formattable
+/// [`time` crate]: time
+#[derive(Clone, Debug)]
+#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
+pub struct OffsetTime<F> {
+ offset: time::UtcOffset,
+ format: F,
+}
+
+// === impl LocalTime ===
+
+#[cfg(feature = "local-time")]
+impl LocalTime<well_known::Rfc3339> {
+ /// Returns a formatter that formats the current [local time] in the
+ /// [RFC 3339] format (a subset of the [ISO 8601] timestamp format).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time};
+ ///
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(time::LocalTime::rfc_3339());
+ /// # drop(collector);
+ /// ```
+ ///
+ /// [local time]: time::OffsetDateTime::now_local
+ /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
+ /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
+ pub fn rfc_3339() -> Self {
+ Self::new(well_known::Rfc3339)
+ }
+}
+
+#[cfg(feature = "local-time")]
+impl<F: Formattable> LocalTime<F> {
+ /// Returns a formatter that formats the current [local time] using the
+ /// [`time` crate] with the provided provided format. The format may be any
+ /// type that implements the [`Formattable`] trait.
+ ///
+ ///
+ /// <div class="example-wrap" style="display:inline-block">
+ /// <pre class="compile_fail" style="white-space:normal;font:inherit;">
+ /// <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/">
+ /// <code>time</code> crate</a> must be compiled with <code>--cfg
+ /// unsound_local_offset</code> in order to use local timestamps. When this
+ /// cfg is not enabled, local timestamps cannot be recorded, and
+ /// events will be logged without timestamps.
+ ///
+ /// See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags">
+ /// <code>time</code> documentation</a> for more details.
+ /// </pre></div>
+ ///
+ /// Typically, the format will be a format description string, or one of the
+ /// `time` crate's [well-known formats].
+ ///
+ /// If the format description is statically known, then the
+ /// [`format_description!`] macro should be used. This is identical to the
+ /// [`time::format_description::parse`] method, but runs at compile-time,
+ /// throwing an error if the format description is invalid. If the desired format
+ /// is not known statically (e.g., a user is providing a format string), then the
+ /// [`time::format_description::parse`] method should be used. Note that this
+ /// method is fallible.
+ ///
+ /// See the [`time` book] for details on the format description syntax.
+ ///
+ /// # Examples
+ ///
+ /// Using the [`format_description!`] macro:
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::LocalTime};
+ /// use time::macros::format_description;
+ ///
+ /// let timer = LocalTime::new(format_description!("[hour]:[minute]:[second]"));
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// Using [`time::format_description::parse`]:
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::LocalTime};
+ ///
+ /// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
+ /// .expect("format string should be valid!");
+ /// let timer = LocalTime::new(time_format);
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// Using the [`format_description!`] macro requires enabling the `time`
+ /// crate's "macros" feature flag.
+ ///
+ /// Using a [well-known format][well-known formats] (this is equivalent to
+ /// [`LocalTime::rfc_3339`]):
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::LocalTime};
+ ///
+ /// let timer = LocalTime::new(time::format_description::well_known::Rfc3339);
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// [local time]: time::OffsetDateTime::now_local()
+ /// [`time` crate]: time
+ /// [`Formattable`]: time::formatting::Formattable
+ /// [well-known formats]: time::format_description::well_known
+ /// [`format_description!`]: time::macros::format_description!
+ /// [`time::format_description::parse`]: time::format_description::parse()
+ /// [`time` book]: https://time-rs.github.io/book/api/format-description.html
+ pub fn new(format: F) -> Self {
+ Self { format }
+ }
+}
+
+#[cfg(feature = "local-time")]
+impl<F> FormatTime for LocalTime<F>
+where
+ F: Formattable,
+{
+ fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
+ let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?;
+ format_datetime(now, w, &self.format)
+ }
+}
+
+#[cfg(feature = "local-time")]
+impl<F> Default for LocalTime<F>
+where
+ F: Formattable + Default,
+{
+ fn default() -> Self {
+ Self::new(F::default())
+ }
+}
+
+// === impl UtcTime ===
+
+impl UtcTime<well_known::Rfc3339> {
+ /// Returns a formatter that formats the current [UTC time] in the
+ /// [RFC 3339] format, which is a subset of the [ISO 8601] timestamp format.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time};
+ ///
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(time::UtcTime::rfc_3339());
+ /// # drop(collector);
+ /// ```
+ ///
+ /// [local time]: time::OffsetDateTime::now_utc
+ /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
+ /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
+ pub fn rfc_3339() -> Self {
+ Self::new(well_known::Rfc3339)
+ }
+}
+
+impl<F: Formattable> UtcTime<F> {
+ /// Returns a formatter that formats the current [UTC time] using the
+ /// [`time` crate], with the provided provided format. The format may be any
+ /// type that implements the [`Formattable`] trait.
+ ///
+ /// Typically, the format will be a format description string, or one of the
+ /// `time` crate's [well-known formats].
+ ///
+ /// If the format description is statically known, then the
+ /// [`format_description!`] macro should be used. This is identical to the
+ /// [`time::format_description::parse`] method, but runs at compile-time,
+ /// failing an error if the format description is invalid. If the desired format
+ /// is not known statically (e.g., a user is providing a format string), then the
+ /// [`time::format_description::parse`] method should be used. Note that this
+ /// method is fallible.
+ ///
+ /// See the [`time` book] for details on the format description syntax.
+ ///
+ /// # Examples
+ ///
+ /// Using the [`format_description!`] macro:
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::UtcTime};
+ /// use time::macros::format_description;
+ ///
+ /// let timer = UtcTime::new(format_description!("[hour]:[minute]:[second]"));
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// Using the [`format_description!`] macro requires enabling the `time`
+ /// crate's "macros" feature flag.
+ ///
+ /// Using [`time::format_description::parse`]:
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::UtcTime};
+ ///
+ /// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
+ /// .expect("format string should be valid!");
+ /// let timer = UtcTime::new(time_format);
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// Using a [well-known format][well-known formats] (this is equivalent to
+ /// [`UtcTime::rfc_3339`]):
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::UtcTime};
+ ///
+ /// let timer = UtcTime::new(time::format_description::well_known::Rfc3339);
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// [UTC time]: time::OffsetDateTime::now_utc()
+ /// [`time` crate]: time
+ /// [`Formattable`]: time::formatting::Formattable
+ /// [well-known formats]: time::format_description::well_known
+ /// [`format_description!`]: time::macros::format_description!
+ /// [`time::format_description::parse`]: time::format_description::parse
+ /// [`time` book]: https://time-rs.github.io/book/api/format-description.html
+ pub fn new(format: F) -> Self {
+ Self { format }
+ }
+}
+
+impl<F> FormatTime for UtcTime<F>
+where
+ F: Formattable,
+{
+ fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
+ format_datetime(OffsetDateTime::now_utc(), w, &self.format)
+ }
+}
+
+impl<F> Default for UtcTime<F>
+where
+ F: Formattable + Default,
+{
+ fn default() -> Self {
+ Self::new(F::default())
+ }
+}
+
+// === impl OffsetTime ===
+
+#[cfg(feature = "local-time")]
+impl OffsetTime<well_known::Rfc3339> {
+ /// Returns a formatter that formats the current time using the [local time offset] in the [RFC
+ /// 3339] format (a subset of the [ISO 8601] timestamp format).
+ ///
+ /// Returns an error if the local time offset cannot be determined. This typically occurs in
+ /// multithreaded programs. To avoid this problem, initialize `OffsetTime` before forking
+ /// threads. When using Tokio, this means initializing `OffsetTime` before the Tokio runtime.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time};
+ ///
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(time::OffsetTime::local_rfc_3339().expect("could not get local offset!"));
+ /// # drop(collector);
+ /// ```
+ ///
+ /// Using `OffsetTime` with Tokio:
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::time::OffsetTime;
+ ///
+ /// #[tokio::main]
+ /// async fn run() {
+ /// tracing::info!("runtime initialized");
+ ///
+ /// // At this point the Tokio runtime is initialized, and we can use both Tokio and Tracing
+ /// // normally.
+ /// }
+ ///
+ /// fn main() {
+ /// // Because we need to get the local offset before Tokio spawns any threads, our `main`
+ /// // function cannot use `tokio::main`.
+ /// tracing_subscriber::fmt()
+ /// .with_timer(OffsetTime::local_rfc_3339().expect("could not get local time offset"))
+ /// .init();
+ ///
+ /// // Even though `run` is written as an `async fn`, because we used `tokio::main` on it
+ /// // we can call it as a synchronous function.
+ /// run();
+ /// }
+ /// ```
+ ///
+ /// [local time offset]: time::UtcOffset::current_local_offset
+ /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
+ /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
+ pub fn local_rfc_3339() -> Result<Self, time::error::IndeterminateOffset> {
+ Ok(Self::new(
+ UtcOffset::current_local_offset()?,
+ well_known::Rfc3339,
+ ))
+ }
+}
+
+impl<F: time::formatting::Formattable> OffsetTime<F> {
+ /// Returns a formatter that formats the current time using the [`time` crate] with the provided
+ /// provided format and [timezone offset]. The format may be any type that implements the
+ /// [`Formattable`] trait.
+ ///
+ ///
+ /// Typically, the offset will be the [local offset], and format will be a format description
+ /// string, or one of the `time` crate's [well-known formats].
+ ///
+ /// If the format description is statically known, then the
+ /// [`format_description!`] macro should be used. This is identical to the
+ /// [`time::format_description::parse`] method, but runs at compile-time,
+ /// throwing an error if the format description is invalid. If the desired format
+ /// is not known statically (e.g., a user is providing a format string), then the
+ /// [`time::format_description::parse`] method should be used. Note that this
+ /// method is fallible.
+ ///
+ /// See the [`time` book] for details on the format description syntax.
+ ///
+ /// # Examples
+ ///
+ /// Using the [`format_description!`] macro:
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::OffsetTime};
+ /// use time::macros::format_description;
+ /// use time::UtcOffset;
+ ///
+ /// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
+ /// let timer = OffsetTime::new(offset, format_description!("[hour]:[minute]:[second]"));
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// Using [`time::format_description::parse`]:
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::OffsetTime};
+ /// use time::UtcOffset;
+ ///
+ /// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
+ /// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
+ /// .expect("format string should be valid!");
+ /// let timer = OffsetTime::new(offset, time_format);
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// Using the [`format_description!`] macro requires enabling the `time`
+ /// crate's "macros" feature flag.
+ ///
+ /// Using a [well-known format][well-known formats] (this is equivalent to
+ /// [`OffsetTime::local_rfc_3339`]):
+ ///
+ /// ```
+ /// use tracing_subscriber::fmt::{self, time::OffsetTime};
+ /// use time::UtcOffset;
+ ///
+ /// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
+ /// let timer = OffsetTime::new(offset, time::format_description::well_known::Rfc3339);
+ /// let collector = tracing_subscriber::fmt()
+ /// .with_timer(timer);
+ /// # drop(collector);
+ /// ```
+ ///
+ /// [`time` crate]: time
+ /// [timezone offset]: time::UtcOffset
+ /// [`Formattable`]: time::formatting::Formattable
+ /// [local offset]: time::UtcOffset::current_local_offset()
+ /// [well-known formats]: time::format_description::well_known
+ /// [`format_description!`]: time::macros::format_description
+ /// [`time::format_description::parse`]: time::format_description::parse
+ /// [`time` book]: https://time-rs.github.io/book/api/format-description.html
+ pub fn new(offset: time::UtcOffset, format: F) -> Self {
+ Self { offset, format }
+ }
+}
+
+impl<F> FormatTime for OffsetTime<F>
+where
+ F: time::formatting::Formattable,
+{
+ fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
+ let now = OffsetDateTime::now_utc().to_offset(self.offset);
+ format_datetime(now, w, &self.format)
+ }
+}
+
+fn format_datetime(
+ now: OffsetDateTime,
+ into: &mut Writer<'_>,
+ fmt: &impl Formattable,
+) -> fmt::Result {
+ let mut into = WriteAdaptor::new(into);
+ now.format_into(&mut into, fmt)
+ .map_err(|_| fmt::Error)
+ .map(|_| ())
+}
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/writer.rs b/vendor/tracing-subscriber/src/fmt/writer.rs
index 0974891f7..3fe945566 100644
--- a/vendor/tracing-subscriber-0.3.3/src/fmt/writer.rs
+++ b/vendor/tracing-subscriber/src/fmt/writer.rs
@@ -1,6 +1,6 @@
//! Abstractions for creating [`io::Write`] instances.
//!
-//! [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
+//! [`io::Write`]: std::io::Write
use std::{
fmt,
io::{self, Write},
@@ -96,8 +96,8 @@ use tracing_core::Metadata;
pub trait MakeWriter<'a> {
/// The concrete [`io::Write`] implementation returned by [`make_writer`].
///
- /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
- /// [`make_writer`]: #tymethod.make_writer
+ /// [`io::Write`]: std::io::Write
+ /// [`make_writer`]: MakeWriter::make_writer
type Writer: io::Write;
/// Returns an instance of [`Writer`].
@@ -109,7 +109,7 @@ pub trait MakeWriter<'a> {
/// creating a [`io::Write`] instance is expensive, be sure to cache it when implementing
/// [`MakeWriter`] to improve performance.
///
- /// [`Writer`]: #associatedtype.Writer
+ /// [`Writer`]: MakeWriter::Writer
/// [`fmt::Layer`]: crate::fmt::Layer
/// [`fmt::Subscriber`]: crate::fmt::Subscriber
/// [`io::Write`]: std::io::Write
@@ -501,13 +501,13 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> {
/// Writing to [`io::stdout`] and [`io::stderr`] produces the same results as using
/// [`libtest`'s `--nocapture` option][nocapture] which may make the results look unreadable.
///
-/// [`fmt::Subscriber`]: ../struct.Subscriber.html
-/// [`fmt::Layer`]: ../struct.Layer.html
+/// [`fmt::Subscriber`]: super::Subscriber
+/// [`fmt::Layer`]: super::Layer
/// [capturing]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
/// [nocapture]: https://doc.rust-lang.org/cargo/commands/cargo-test.html
-/// [`io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html
-/// [`io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
-/// [`print!`]: https://doc.rust-lang.org/std/macro.print.html
+/// [`io::stdout`]: std::io::stdout
+/// [`io::stderr`]: std::io::stderr
+/// [`print!`]: std::print!
#[derive(Default, Debug)]
pub struct TestWriter {
_p: (),
@@ -646,10 +646,9 @@ pub struct Tee<A, B> {
/// requires the `Writer` type to implement [`io::Write`], it's necessary to add
/// a newtype that forwards the trait implementation.
///
-/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
-/// [`MutexGuard`]: https://doc.rust-lang.org/std/sync/struct.MutexGuard.html
-/// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
-/// [`MakeWriter`]: trait.MakeWriter.html
+/// [`io::Write`]: std::io::Write
+/// [`MutexGuard`]: std::sync::MutexGuard
+/// [`Mutex`]: std::sync::Mutex
#[derive(Debug)]
pub struct MutexGuardWriter<'a, W>(MutexGuard<'a, W>);
@@ -689,7 +688,7 @@ where
{
type Writer = &'a W;
fn make_writer(&'a self) -> Self::Writer {
- &*self
+ self
}
}
@@ -734,7 +733,6 @@ impl<'a> MakeWriter<'a> for TestWriter {
impl BoxMakeWriter {
/// Constructs a `BoxMakeWriter` wrapping a type implementing [`MakeWriter`].
///
- /// [`MakeWriter`]: trait.MakeWriter.html
pub fn new<M>(make_writer: M) -> Self
where
M: for<'a> MakeWriter<'a> + Send + Sync + 'static,
@@ -1025,6 +1023,8 @@ impl<A, B> Tee<A, B> {
/// outputs.
///
/// See the documentation for [`MakeWriterExt::and`] for details.
+ ///
+ /// [writers]: std::io::Write
pub fn new(a: A, b: B) -> Self {
Self { a, b }
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/layer/context.rs b/vendor/tracing-subscriber/src/layer/context.rs
index e11959526..46254994f 100644
--- a/vendor/tracing-subscriber-0.3.3/src/layer/context.rs
+++ b/vendor/tracing-subscriber/src/layer/context.rs
@@ -25,10 +25,10 @@ use crate::{filter::FilterId, registry::Registry};
/// }
/// ```
///
-/// [`Layer`]: ../layer/trait.Layer.html
-/// [`Subscriber`]: https://docs.rs/tracing-core/latest/tracing_core/trait.Subscriber.html
-/// [stored data]: ../registry/struct.SpanRef.html
-/// [`LookupSpan`]: "../registry/trait.LookupSpan.html
+/// [`Layer`]: super::Layer
+/// [`Subscriber`]: tracing_core::Subscriber
+/// [stored data]: crate::registry::SpanRef
+/// [`LookupSpan`]: crate::registry::LookupSpan
#[derive(Debug)]
pub struct Context<'a, S> {
subscriber: Option<&'a S>,
@@ -99,9 +99,9 @@ where
/// check whether the event would be enabled. This allows `Layer`s to
/// elide constructing the event if it would not be recorded.
///
- /// [register]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html#method.register_callsite
- /// [`enabled`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html#method.enabled
- /// [`Context::enabled`]: #method.enabled
+ /// [register]: tracing_core::subscriber::Subscriber::register_callsite()
+ /// [`enabled`]: tracing_core::subscriber::Subscriber::enabled()
+ /// [`Context::enabled`]: Context::enabled()
#[inline]
pub fn event(&self, event: &Event<'_>) {
if let Some(subscriber) = self.subscriber {
@@ -206,7 +206,7 @@ where
/// declaration</a> for details.
/// </pre>
///
- /// [stored data]: ../registry/struct.SpanRef.html
+ /// [stored data]: crate::registry::SpanRef
#[inline]
pub fn span(&self, id: &span::Id) -> Option<registry::SpanRef<'_, S>>
where
@@ -251,7 +251,7 @@ where
/// declaration</a> for details.
/// </pre>
///
- /// [stored data]: ../registry/struct.SpanRef.html
+ /// [stored data]: crate::registry::SpanRef
#[inline]
pub fn lookup_current(&self) -> Option<registry::SpanRef<'_, S>>
where
@@ -333,7 +333,7 @@ where
/// declaration</a> for details.
/// </pre>
///
- /// [stored data]: ../registry/struct.SpanRef.html
+ /// [stored data]: crate::registry::SpanRef
pub fn span_scope(&self, id: &span::Id) -> Option<registry::Scope<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
@@ -360,7 +360,7 @@ where
/// declaration</a> for details.
/// </pre>
///
- /// [stored data]: ../registry/struct.SpanRef.html
+ /// [stored data]: crate::registry::SpanRef
pub fn event_scope(&self, event: &Event<'_>) -> Option<registry::Scope<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
diff --git a/vendor/tracing-subscriber-0.3.3/src/layer/layered.rs b/vendor/tracing-subscriber/src/layer/layered.rs
index c690764ad..f09c58c97 100644
--- a/vendor/tracing-subscriber-0.3.3/src/layer/layered.rs
+++ b/vendor/tracing-subscriber/src/layer/layered.rs
@@ -1,9 +1,4 @@
-use tracing_core::{
- metadata::Metadata,
- span,
- subscriber::{Interest, Subscriber},
- Event, LevelFilter,
-};
+use tracing_core::{metadata::Metadata, span, Dispatch, Event, Interest, LevelFilter, Subscriber};
use crate::{
filter,
@@ -12,13 +7,17 @@ use crate::{
};
#[cfg(all(feature = "registry", feature = "std"))]
use crate::{filter::FilterId, registry::Registry};
-use core::{any::TypeId, cmp, fmt, marker::PhantomData};
+use core::{
+ any::{Any, TypeId},
+ cmp, fmt,
+ marker::PhantomData,
+};
/// A [`Subscriber`] composed of a `Subscriber` wrapped by one or more
/// [`Layer`]s.
///
/// [`Layer`]: crate::Layer
-/// [`Subscriber`]: https://docs.rs/tracing-core/latest/tracing_core/trait.Subscriber.html
+/// [`Subscriber`]: tracing_core::Subscriber
#[derive(Clone)]
pub struct Layered<L, I, S = I> {
/// The layer.
@@ -63,6 +62,30 @@ pub struct Layered<L, I, S = I> {
// === impl Layered ===
+impl<L, S> Layered<L, S>
+where
+ L: Layer<S>,
+ S: Subscriber,
+{
+ /// Returns `true` if this [`Subscriber`] is the same type as `T`.
+ pub fn is<T: Any>(&self) -> bool {
+ self.downcast_ref::<T>().is_some()
+ }
+
+ /// Returns some reference to this [`Subscriber`] value if it is of type `T`,
+ /// or `None` if it isn't.
+ pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
+ unsafe {
+ let raw = self.downcast_raw(TypeId::of::<T>())?;
+ if raw.is_null() {
+ None
+ } else {
+ Some(&*(raw as *const T))
+ }
+ }
+ }
+}
+
impl<L, S> Subscriber for Layered<L, S>
where
L: Layer<S>,
@@ -92,7 +115,11 @@ where
}
fn max_level_hint(&self) -> Option<LevelFilter> {
- self.pick_level_hint(self.layer.max_level_hint(), self.inner.max_level_hint())
+ self.pick_level_hint(
+ self.layer.max_level_hint(),
+ self.inner.max_level_hint(),
+ super::subscriber_is_none(&self.inner),
+ )
}
fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
@@ -111,6 +138,16 @@ where
self.layer.on_follows_from(span, follows, self.ctx());
}
+ fn event_enabled(&self, event: &Event<'_>) -> bool {
+ if self.layer.event_enabled(event, self.ctx()) {
+ // if the outer layer enables the event, ask the inner subscriber.
+ self.inner.event_enabled(event)
+ } else {
+ // otherwise, the event is disabled by this layer
+ false
+ }
+ }
+
fn event(&self, event: &Event<'_>) {
self.inner.event(event);
self.layer.on_event(event, self.ctx());
@@ -152,7 +189,7 @@ where
#[cfg(all(feature = "registry", feature = "std"))]
{
if let Some(g) = guard.as_mut() {
- g.is_closing()
+ g.set_closing()
};
}
@@ -207,6 +244,11 @@ where
B: Layer<S>,
S: Subscriber,
{
+ fn on_register_dispatch(&self, subscriber: &Dispatch) {
+ self.layer.on_register_dispatch(subscriber);
+ self.inner.on_register_dispatch(subscriber);
+ }
+
fn on_layer(&mut self, subscriber: &mut S) {
self.layer.on_layer(subscriber);
self.inner.on_layer(subscriber);
@@ -229,7 +271,11 @@ where
}
fn max_level_hint(&self) -> Option<LevelFilter> {
- self.pick_level_hint(self.layer.max_level_hint(), self.inner.max_level_hint())
+ self.pick_level_hint(
+ self.layer.max_level_hint(),
+ self.inner.max_level_hint(),
+ super::layer_is_none(&self.inner),
+ )
}
#[inline]
@@ -251,6 +297,17 @@ where
}
#[inline]
+ fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
+ if self.layer.event_enabled(event, ctx.clone()) {
+ // if the outer layer enables the event, ask the inner subscriber.
+ self.inner.event_enabled(event, ctx)
+ } else {
+ // otherwise, the event is disabled by this layer
+ false
+ }
+ }
+
+ #[inline]
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
self.inner.on_event(event, ctx.clone());
self.layer.on_event(event, ctx);
@@ -386,7 +443,7 @@ where
// (rather than calling into the inner type), clear the current
// per-layer filter interest state.
#[cfg(feature = "registry")]
- drop(filter::FilterState::take_interest());
+ filter::FilterState::take_interest();
return outer;
}
@@ -421,6 +478,7 @@ where
&self,
outer_hint: Option<LevelFilter>,
inner_hint: Option<LevelFilter>,
+ inner_is_none: bool,
) -> Option<LevelFilter> {
if self.inner_is_registry {
return outer_hint;
@@ -438,6 +496,31 @@ where
return None;
}
+ // If the layer is `Option::None`, then we
+ // want to short-circuit the layer underneath, if it
+ // returns `None`, to override the `None` layer returning
+ // `Some(OFF)`, which should ONLY apply when there are
+ // no other layers that return `None`. Note this
+ // `None` does not == `Some(TRACE)`, it means
+ // something more like: "whatever all the other
+ // layers agree on, default to `TRACE` if none
+ // have an opinion". We also choose do this AFTER
+ // we check for per-layer filters, which
+ // have their own logic.
+ //
+ // Also note that this does come at some perf cost, but
+ // this function is only called on initialization and
+ // subscriber reloading.
+ if super::layer_is_none(&self.layer) {
+ return cmp::max(outer_hint, Some(inner_hint?));
+ }
+
+ // Similarly, if the layer on the inside is `None` and it returned an
+ // `Off` hint, we want to override that with the outer hint.
+ if inner_is_none && inner_hint == Some(LevelFilter::OFF) {
+ return outer_hint;
+ }
+
cmp::max(outer_hint, inner_hint)
}
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/layer/mod.rs b/vendor/tracing-subscriber/src/layer/mod.rs
index f3f994490..bdc154301 100644
--- a/vendor/tracing-subscriber-0.3.3/src/layer/mod.rs
+++ b/vendor/tracing-subscriber/src/layer/mod.rs
@@ -19,7 +19,7 @@
//! [`Subscriber`] behavior; it can _observe_ events and spans, but does not
//! assign IDs.
//!
-//! ## Composing Layers
+//! # Composing Layers
//!
//! Since a [`Layer`] does not implement a complete strategy for collecting
//! traces, it must be composed with a `Subscriber` in order to be used. The
@@ -135,9 +135,245 @@
//! [`Layer::with_subscriber`] as an implementation detail, as `with_subscriber`
//! calls must be nested, leading to less clear code for the reader.
//!
+//! ## Runtime Configuration With `Layer`s
+//!
+//! In some cases, a particular [`Layer`] may be enabled or disabled based on
+//! runtime configuration. This can introduce challenges, because the type of a
+//! layered [`Subscriber`] depends on which layers are added to it: if an `if`
+//! or `match` expression adds some [`Layer`] implementation in one branch,
+//! and other layers in another, the [`Subscriber`] values returned by those
+//! branches will have different types. For example, the following _will not_
+//! work:
+//!
+//! ```compile_fail
+//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> {
+//! # struct Config {
+//! # is_prod: bool,
+//! # path: &'static str,
+//! # }
+//! # let cfg = Config { is_prod: false, path: "debug.log" };
+//! use std::fs::File;
+//! use tracing_subscriber::{Registry, prelude::*};
+//!
+//! let stdout_log = tracing_subscriber::fmt::layer().pretty();
+//! let subscriber = Registry::default().with(stdout_log);
+//!
+//! // The compile error will occur here because the if and else
+//! // branches have different (and therefore incompatible) types.
+//! let subscriber = if cfg.is_prod {
+//! let file = File::create(cfg.path)?;
+//! let layer = tracing_subscriber::fmt::layer()
+//! .json()
+//! .with_writer(Arc::new(file));
+//! layer.with(subscriber)
+//! } else {
+//! layer
+//! };
+//!
+//! tracing::subscriber::set_global_default(subscriber)
+//! .expect("Unable to set global subscriber");
+//! # Ok(()) }
+//! ```
+//!
+//! However, a [`Layer`] wrapped in an [`Option`] [also implements the `Layer`
+//! trait][option-impl]. This allows individual layers to be enabled or disabled at
+//! runtime while always producing a [`Subscriber`] of the same type. For
+//! example:
+//!
+//! ```
+//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> {
+//! # struct Config {
+//! # is_prod: bool,
+//! # path: &'static str,
+//! # }
+//! # let cfg = Config { is_prod: false, path: "debug.log" };
+//! use std::fs::File;
+//! use tracing_subscriber::{Registry, prelude::*};
+//!
+//! let stdout_log = tracing_subscriber::fmt::layer().pretty();
+//! let subscriber = Registry::default().with(stdout_log);
+//!
+//! // if `cfg.is_prod` is true, also log JSON-formatted logs to a file.
+//! let json_log = if cfg.is_prod {
+//! let file = File::create(cfg.path)?;
+//! let json_log = tracing_subscriber::fmt::layer()
+//! .json()
+//! .with_writer(file);
+//! Some(json_log)
+//! } else {
+//! None
+//! };
+//!
+//! // If `cfg.is_prod` is false, then `json` will be `None`, and this layer
+//! // will do nothing. However, the subscriber will still have the same type
+//! // regardless of whether the `Option`'s value is `None` or `Some`.
+//! let subscriber = subscriber.with(json_log);
+//!
+//! tracing::subscriber::set_global_default(subscriber)
+//! .expect("Unable to set global subscriber");
+//! # Ok(()) }
+//! ```
+//!
+//! If a [`Layer`] may be one of several different types, note that [`Box<dyn
+//! Layer<S> + Send + Sync>` implements `Layer`][box-impl].
+//! This may be used to erase the type of a [`Layer`].
+//!
+//! For example, a function that configures a [`Layer`] to log to one of
+//! several outputs might return a `Box<dyn Layer<S> + Send + Sync + 'static>`:
+//! ```
+//! use tracing_subscriber::{
+//! Layer,
+//! registry::LookupSpan,
+//! prelude::*,
+//! };
+//! use std::{path::PathBuf, fs::File, io};
+//!
+//! /// Configures whether logs are emitted to a file, to stdout, or to stderr.
+//! pub enum LogConfig {
+//! File(PathBuf),
+//! Stdout,
+//! Stderr,
+//! }
+//!
+//! impl LogConfig {
+//! pub fn layer<S>(self) -> Box<dyn Layer<S> + Send + Sync + 'static>
+//! where
+//! S: tracing_core::Subscriber,
+//! for<'a> S: LookupSpan<'a>,
+//! {
+//! // Shared configuration regardless of where logs are output to.
+//! let fmt = tracing_subscriber::fmt::layer()
+//! .with_target(true)
+//! .with_thread_names(true);
+//!
+//! // Configure the writer based on the desired log target:
+//! match self {
+//! LogConfig::File(path) => {
+//! let file = File::create(path).expect("failed to create log file");
+//! Box::new(fmt.with_writer(file))
+//! },
+//! LogConfig::Stdout => Box::new(fmt.with_writer(io::stdout)),
+//! LogConfig::Stderr => Box::new(fmt.with_writer(io::stderr)),
+//! }
+//! }
+//! }
+//!
+//! let config = LogConfig::Stdout;
+//! tracing_subscriber::registry()
+//! .with(config.layer())
+//! .init();
+//! ```
+//!
+//! The [`Layer::boxed`] method is provided to make boxing a `Layer`
+//! more convenient, but [`Box::new`] may be used as well.
+//!
+//! When the number of `Layer`s varies at runtime, note that a
+//! [`Vec<L> where L: Layer` also implements `Layer`][vec-impl]. This
+//! can be used to add a variable number of `Layer`s to a `Subscriber`:
+//!
+//! ```
+//! use tracing_subscriber::{Layer, prelude::*};
+//! struct MyLayer {
+//! // ...
+//! }
+//! # impl MyLayer { fn new() -> Self { Self {} }}
+//!
+//! impl<S: tracing_core::Subscriber> Layer<S> for MyLayer {
+//! // ...
+//! }
+//!
+//! /// Returns how many layers we need
+//! fn how_many_layers() -> usize {
+//! // ...
+//! # 3
+//! }
+//!
+//! // Create a variable-length `Vec` of layers
+//! let mut layers = Vec::new();
+//! for _ in 0..how_many_layers() {
+//! layers.push(MyLayer::new());
+//! }
+//!
+//! tracing_subscriber::registry()
+//! .with(layers)
+//! .init();
+//! ```
+//!
+//! If a variable number of `Layer` is needed and those `Layer`s have
+//! different types, a `Vec` of [boxed `Layer` trait objects][box-impl] may
+//! be used. For example:
+//!
+//! ```
+//! use tracing_subscriber::{filter::LevelFilter, Layer, prelude::*};
+//! use std::fs::File;
+//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
+//! struct Config {
+//! enable_log_file: bool,
+//! enable_stdout: bool,
+//! enable_stderr: bool,
+//! // ...
+//! }
+//! # impl Config {
+//! # fn from_config_file()-> Result<Self, Box<dyn std::error::Error>> {
+//! # // don't enable the log file so that the example doesn't actually create it
+//! # Ok(Self { enable_log_file: false, enable_stdout: true, enable_stderr: true })
+//! # }
+//! # }
+//!
+//! let cfg = Config::from_config_file()?;
+//!
+//! // Based on our dynamically loaded config file, create any number of layers:
+//! let mut layers = Vec::new();
+//!
+//! if cfg.enable_log_file {
+//! let file = File::create("myapp.log")?;
+//! let layer = tracing_subscriber::fmt::layer()
+//! .with_thread_names(true)
+//! .with_target(true)
+//! .json()
+//! .with_writer(file)
+//! // Box the layer as a type-erased trait object, so that it can
+//! // be pushed to the `Vec`.
+//! .boxed();
+//! layers.push(layer);
+//! }
+//!
+//! if cfg.enable_stdout {
+//! let layer = tracing_subscriber::fmt::layer()
+//! .pretty()
+//! .with_filter(LevelFilter::INFO)
+//! // Box the layer as a type-erased trait object, so that it can
+//! // be pushed to the `Vec`.
+//! .boxed();
+//! layers.push(layer);
+//! }
+//!
+//! if cfg.enable_stdout {
+//! let layer = tracing_subscriber::fmt::layer()
+//! .with_target(false)
+//! .with_filter(LevelFilter::WARN)
+//! // Box the layer as a type-erased trait object, so that it can
+//! // be pushed to the `Vec`.
+//! .boxed();
+//! layers.push(layer);
+//! }
+//!
+//! tracing_subscriber::registry()
+//! .with(layers)
+//! .init();
+//!# Ok(()) }
+//! ```
+//!
+//! Finally, if the number of layers _changes_ at runtime, a `Vec` of
+//! subscribers can be used alongside the [`reload`](crate::reload) module to
+//! add or remove subscribers dynamically at runtime.
+//!
+//! [option-impl]: Layer#impl-Layer<S>-for-Option<L>
+//! [box-impl]: Layer#impl-Layer%3CS%3E-for-Box%3Cdyn%20Layer%3CS%3E%20+%20Send%20+%20Sync%3E
+//! [vec-impl]: Layer#impl-Layer<S>-for-Vec<L>
//! [prelude]: crate::prelude
//!
-//! ## Recording Traces
+//! # Recording Traces
//!
//! The [`Layer`] trait defines a set of methods for consuming notifications from
//! tracing instrumentation, which are generally equivalent to the similarly
@@ -146,7 +382,7 @@
//! information provided by the wrapped subscriber (such as [the current span])
//! to the layer.
//!
-//! ## Filtering with `Layer`s
+//! # Filtering with `Layer`s
//!
//! As well as strategies for handling trace events, the `Layer` trait may also
//! be used to represent composable _filters_. This allows the determination of
@@ -158,7 +394,7 @@
//! combined with _per-layer filters_ that control what spans and events are
//! recorded by those layers.
//!
-//! ### Global Filtering
+//! ## Global Filtering
//!
//! A `Layer` that implements a filtering strategy should override the
//! [`register_callsite`] and/or [`enabled`] methods. It may also choose to implement
@@ -179,7 +415,29 @@
//! [`Interest::never()`] from its [`register_callsite`] method, filter
//! evaluation will short-circuit and the span or event will be disabled.
//!
-//! ### Per-Layer Filtering
+//! ### Enabling Interest
+//!
+//! Whenever an tracing event (or span) is emitted, it goes through a number of
+//! steps to determine how and how much it should be processed. The earlier an
+//! event is disabled, the less work has to be done to process the event, so
+//! `Layer`s that implement filtering should attempt to disable unwanted
+//! events as early as possible. In order, each event checks:
+//!
+//! - [`register_callsite`], once per callsite (roughly: once per time that
+//! `event!` or `span!` is written in the source code; this is cached at the
+//! callsite). See [`Subscriber::register_callsite`] and
+//! [`tracing_core::callsite`] for a summary of how this behaves.
+//! - [`enabled`], once per emitted event (roughly: once per time that `event!`
+//! or `span!` is *executed*), and only if `register_callsite` regesters an
+//! [`Interest::sometimes`]. This is the main customization point to globally
+//! filter events based on their [`Metadata`]. If an event can be disabled
+//! based only on [`Metadata`], it should be, as this allows the construction
+//! of the actual `Event`/`Span` to be skipped.
+//! - For events only (and not spans), [`event_enabled`] is called just before
+//! processing the event. This gives layers one last chance to say that
+//! an event should be filtered out, now that the event's fields are known.
+//!
+//! ## Per-Layer Filtering
//!
//! **Note**: per-layer filtering APIs currently require the [`"registry"` crate
//! feature flag][feat] to be enabled.
@@ -393,94 +651,16 @@
//! # Ok(()) }
//! ```
//!
-//! ## Runtime Configuration With Layers
-//!
-//! In some cases, a particular [`Layer`] may be enabled or disabled based on
-//! runtime configuration. This can introduce challenges, because the type of a
-//! layered [`Subscriber`] depends on which layers are added to it: if an `if`
-//! or `match` expression adds some [`Layer`]s in one branch and other layers
-//! in another, the [`Subscriber`] values returned by those branches will have
-//! different types. For example, the following _will not_ work:
-//!
-//! ```compile_fail
-//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> {
-//! # struct Config {
-//! # is_prod: bool,
-//! # path: &'static str,
-//! # }
-//! # let cfg = Config { is_prod: false, path: "debug.log" };
-//! use std::{fs::File, sync::Arc};
-//! use tracing_subscriber::{Registry, prelude::*};
-//!
-//! let stdout_log = tracing_subscriber::fmt::layer().pretty();
-//! let subscriber = Registry::default().with(stdout_log);
-//!
-//! // The compile error will occur here because the if and else
-//! // branches have different (and therefore incompatible) types.
-//! let subscriber = if cfg.is_prod {
-//! let file = File::create(cfg.path)?;
-//! let layer = tracing_subscriber::fmt::layer()
-//! .json()
-//! .with_writer(Arc::new(file));
-//! subscriber.with(layer)
-//! } else {
-//! subscriber
-//! };
-//!
-//! tracing::subscriber::set_global_default(subscriber)
-//! .expect("Unable to set global subscriber");
-//! # Ok(()) }
-//! ```
-//!
-//! However, a [`Layer`] wrapped in an [`Option`] [also implements the `Layer`
-//! trait][option-impl]. This allows individual layers to be enabled or disabled at
-//! runtime while always producing a [`Subscriber`] of the same type. For
-//! example:
-//!
-//! ```
-//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> {
-//! # struct Config {
-//! # is_prod: bool,
-//! # path: &'static str,
-//! # }
-//! # let cfg = Config { is_prod: false, path: "debug.log" };
-//! use std::{fs::File, sync::Arc};
-//! use tracing_subscriber::{Registry, prelude::*};
-//!
-//! let stdout_log = tracing_subscriber::fmt::layer().pretty();
-//! let subscriber = Registry::default().with(stdout_log);
-//!
-//! // if `cfg.is_prod` is true, also log JSON-formatted logs to a file.
-//! let json_log = if cfg.is_prod {
-//! let file = File::create(cfg.path)?;
-//! let json_log = tracing_subscriber::fmt::layer()
-//! .json()
-//! .with_writer(Arc::new(file));
-//! Some(json_log)
-//! } else {
-//! None
-//! };
-//!
-//! // If `cfg.is_prod` is false, then `json` will be `None`, and this layer
-//! // will do nothing. However, the subscriber will still have the same type
-//! // regardless of whether the `Option`'s value is `None` or `Some`.
-//! let subscriber = subscriber.with(json_log);
-//!
-//! tracing::subscriber::set_global_default(subscriber)
-//! .expect("Unable to set global subscriber");
-//! # Ok(()) }
-//! ```
-//!
-//! [`Subscriber`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html
-//! [span IDs]: https://docs.rs/tracing-core/latest/tracing_core/span/struct.Id.html
+//! [`Subscriber`]: tracing_core::subscriber::Subscriber
+//! [span IDs]: tracing_core::span::Id
//! [the current span]: Context::current_span
//! [`register_callsite`]: Layer::register_callsite
//! [`enabled`]: Layer::enabled
+//! [`event_enabled`]: Layer::event_enabled
//! [`on_enter`]: Layer::on_enter
//! [`Layer::register_callsite`]: Layer::register_callsite
//! [`Layer::enabled`]: Layer::enabled
-//! [`Interest::never()`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/struct.Interest.html#method.never
-//! [option-impl]: crate::layer::Layer#impl-Layer<S>-for-Option<L>
+//! [`Interest::never()`]: tracing_core::subscriber::Interest::never()
//! [`Filtered`]: crate::filter::Filtered
//! [`filter`]: crate::filter
//! [`Targets`]: crate::filter::Targets
@@ -498,7 +678,7 @@ use tracing_core::{
metadata::Metadata,
span,
subscriber::{Interest, Subscriber},
- Event, LevelFilter,
+ Dispatch, Event, LevelFilter,
};
use core::any::TypeId;
@@ -531,6 +711,31 @@ where
S: Subscriber,
Self: 'static,
{
+ /// Performs late initialization when installing this layer as a
+ /// [`Subscriber`].
+ ///
+ /// ## Avoiding Memory Leaks
+ ///
+ /// `Layer`s should not store the [`Dispatch`] pointing to the [`Subscriber`]
+ /// that they are a part of. Because the `Dispatch` owns the `Subscriber`,
+ /// storing the `Dispatch` within the `Subscriber` will create a reference
+ /// count cycle, preventing the `Dispatch` from ever being dropped.
+ ///
+ /// Instead, when it is necessary to store a cyclical reference to the
+ /// `Dispatch` within a `Layer`, use [`Dispatch::downgrade`] to convert a
+ /// `Dispatch` into a [`WeakDispatch`]. This type is analogous to
+ /// [`std::sync::Weak`], and does not create a reference count cycle. A
+ /// [`WeakDispatch`] can be stored within a subscriber without causing a
+ /// memory leak, and can be [upgraded] into a `Dispatch` temporarily when
+ /// the `Dispatch` must be accessed by the subscriber.
+ ///
+ /// [`WeakDispatch`]: tracing_core::dispatcher::WeakDispatch
+ /// [upgraded]: tracing_core::dispatcher::WeakDispatch::upgrade
+ /// [`Subscriber`]: tracing_core::Subscriber
+ fn on_register_dispatch(&self, collector: &Dispatch) {
+ let _ = collector;
+ }
+
/// Performs late initialization when attaching a `Layer` to a
/// [`Subscriber`].
///
@@ -592,15 +797,15 @@ where
/// globally enable or disable those callsites, it should always return
/// [`Interest::always()`].
///
- /// [`Interest`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Interest.html
- /// [`Subscriber::register_callsite`]: https://docs.rs/tracing-core/latest/tracing_core/trait.Subscriber.html#method.register_callsite
- /// [`Interest::never()`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/struct.Interest.html#method.never
- /// [`Interest::always()`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/struct.Interest.html#method.always
- /// [`self.enabled`]: #method.enabled
- /// [`Layer::enabled`]: #method.enabled
- /// [`on_event`]: #method.on_event
- /// [`on_enter`]: #method.on_enter
- /// [`on_exit`]: #method.on_exit
+ /// [`Interest`]: tracing_core::Interest
+ /// [`Subscriber::register_callsite`]: tracing_core::Subscriber::register_callsite()
+ /// [`Interest::never()`]: tracing_core::subscriber::Interest::never()
+ /// [`Interest::always()`]: tracing_core::subscriber::Interest::always()
+ /// [`self.enabled`]: Layer::enabled()
+ /// [`Layer::enabled`]: Layer::enabled()
+ /// [`on_event`]: Layer::on_event()
+ /// [`on_enter`]: Layer::on_enter()
+ /// [`on_exit`]: Layer::on_exit()
/// [the trait-level documentation]: #filtering-with-layers
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
if self.enabled(metadata, Context::none()) {
@@ -635,13 +840,12 @@ where
/// See [the trait-level documentation] for more information on filtering
/// with `Layer`s.
///
- /// [`Interest`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Interest.html
- /// [`Context`]: ../struct.Context.html
- /// [`Subscriber::enabled`]: https://docs.rs/tracing-core/latest/tracing_core/trait.Subscriber.html#method.enabled
- /// [`Layer::register_callsite`]: #method.register_callsite
- /// [`on_event`]: #method.on_event
- /// [`on_enter`]: #method.on_enter
- /// [`on_exit`]: #method.on_exit
+ /// [`Interest`]: tracing_core::Interest
+ /// [`Subscriber::enabled`]: tracing_core::Subscriber::enabled()
+ /// [`Layer::register_callsite`]: Layer::register_callsite()
+ /// [`on_event`]: Layer::on_event()
+ /// [`on_enter`]: Layer::on_enter()
+ /// [`on_exit`]: Layer::on_exit()
/// [the trait-level documentation]: #filtering-with-layers
fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
let _ = (metadata, ctx);
@@ -676,6 +880,31 @@ where
// seems like a good future-proofing measure as it may grow other methods later...
fn on_follows_from(&self, _span: &span::Id, _follows: &span::Id, _ctx: Context<'_, S>) {}
+ /// Called before [`on_event`], to determine if `on_event` should be called.
+ ///
+ /// <div class="example-wrap" style="display:inline-block">
+ /// <pre class="ignore" style="white-space:normal;font:inherit;">
+ ///
+ /// **Note**: This method determines whether an event is globally enabled,
+ /// *not* whether the individual `Layer` will be notified about the
+ /// event. This is intended to be used by `Layer`s that implement
+ /// filtering for the entire stack. `Layer`s which do not wish to be
+ /// notified about certain events but do not wish to globally disable them
+ /// should ignore those events in their [on_event][Self::on_event].
+ ///
+ /// </pre></div>
+ ///
+ /// See [the trait-level documentation] for more information on filtering
+ /// with `Layer`s.
+ ///
+ /// [`on_event`]: Self::on_event
+ /// [`Interest`]: tracing_core::Interest
+ /// [the trait-level documentation]: #filtering-with-layers
+ #[inline] // collapse this to a constant please mrs optimizer
+ fn event_enabled(&self, _event: &Event<'_>, _ctx: Context<'_, S>) -> bool {
+ true
+ }
+
/// Notifies this layer that an event has occurred.
fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {}
@@ -840,7 +1069,7 @@ where
/// .with_subscriber(MySubscriber::new());
///```
///
- /// [`Subscriber`]: https://docs.rs/tracing-core/latest/tracing_core/trait.Subscriber.html
+ /// [`Subscriber`]: tracing_core::Subscriber
fn with_subscriber(mut self, mut inner: S) -> Layered<Self, S>
where
Self: Sized,
@@ -857,7 +1086,7 @@ where
/// per-layer filtering.
///
/// [`Filtered`]: crate::filter::Filtered
- /// [plf]: #per-layer-filtering
+ /// [plf]: crate::layer#per-layer-filtering
#[cfg(all(feature = "registry", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
fn with_filter<F>(self, filter: F) -> filter::Filtered<Self, F, S>
@@ -868,6 +1097,131 @@ where
filter::Filtered::new(self, filter)
}
+ /// Erases the type of this [`Layer`], returning a [`Box`]ed `dyn
+ /// Layer` trait object.
+ ///
+ /// This can be used when a function returns a `Layer` which may be of
+ /// one of several types, or when a `Layer` subscriber has a very long type
+ /// signature.
+ ///
+ /// # Examples
+ ///
+ /// The following example will *not* compile, because the value assigned to
+ /// `log_layer` may have one of several different types:
+ ///
+ /// ```compile_fail
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+ /// use tracing_subscriber::{Layer, filter::LevelFilter, prelude::*};
+ /// use std::{path::PathBuf, fs::File, io};
+ ///
+ /// /// Configures whether logs are emitted to a file, to stdout, or to stderr.
+ /// pub enum LogConfig {
+ /// File(PathBuf),
+ /// Stdout,
+ /// Stderr,
+ /// }
+ ///
+ /// let config = // ...
+ /// # LogConfig::Stdout;
+ ///
+ /// // Depending on the config, construct a layer of one of several types.
+ /// let log_layer = match config {
+ /// // If logging to a file, use a maximally-verbose configuration.
+ /// LogConfig::File(path) => {
+ /// let file = File::create(path)?;
+ /// tracing_subscriber::fmt::layer()
+ /// .with_thread_ids(true)
+ /// .with_thread_names(true)
+ /// // Selecting the JSON logging format changes the layer's
+ /// // type.
+ /// .json()
+ /// .with_span_list(true)
+ /// // Setting the writer to use our log file changes the
+ /// // layer's type again.
+ /// .with_writer(file)
+ /// },
+ ///
+ /// // If logging to stdout, use a pretty, human-readable configuration.
+ /// LogConfig::Stdout => tracing_subscriber::fmt::layer()
+ /// // Selecting the "pretty" logging format changes the
+ /// // layer's type!
+ /// .pretty()
+ /// .with_writer(io::stdout)
+ /// // Add a filter based on the RUST_LOG environment variable;
+ /// // this changes the type too!
+ /// .and_then(tracing_subscriber::EnvFilter::from_default_env()),
+ ///
+ /// // If logging to stdout, only log errors and warnings.
+ /// LogConfig::Stderr => tracing_subscriber::fmt::layer()
+ /// // Changing the writer changes the layer's type
+ /// .with_writer(io::stderr)
+ /// // Only log the `WARN` and `ERROR` levels. Adding a filter
+ /// // changes the layer's type to `Filtered<LevelFilter, ...>`.
+ /// .with_filter(LevelFilter::WARN),
+ /// };
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(log_layer)
+ /// .init();
+ /// # Ok(()) }
+ /// ```
+ ///
+ /// However, adding a call to `.boxed()` after each match arm erases the
+ /// layer's type, so this code *does* compile:
+ ///
+ /// ```
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+ /// # use tracing_subscriber::{Layer, filter::LevelFilter, prelude::*};
+ /// # use std::{path::PathBuf, fs::File, io};
+ /// # pub enum LogConfig {
+ /// # File(PathBuf),
+ /// # Stdout,
+ /// # Stderr,
+ /// # }
+ /// # let config = LogConfig::Stdout;
+ /// let log_layer = match config {
+ /// LogConfig::File(path) => {
+ /// let file = File::create(path)?;
+ /// tracing_subscriber::fmt::layer()
+ /// .with_thread_ids(true)
+ /// .with_thread_names(true)
+ /// .json()
+ /// .with_span_list(true)
+ /// .with_writer(file)
+ /// // Erase the type by boxing the layer
+ /// .boxed()
+ /// },
+ ///
+ /// LogConfig::Stdout => tracing_subscriber::fmt::layer()
+ /// .pretty()
+ /// .with_writer(io::stdout)
+ /// .and_then(tracing_subscriber::EnvFilter::from_default_env())
+ /// // Erase the type by boxing the layer
+ /// .boxed(),
+ ///
+ /// LogConfig::Stderr => tracing_subscriber::fmt::layer()
+ /// .with_writer(io::stderr)
+ /// .with_filter(LevelFilter::WARN)
+ /// // Erase the type by boxing the layer
+ /// .boxed(),
+ /// };
+ ///
+ /// tracing_subscriber::registry()
+ /// .with(log_layer)
+ /// .init();
+ /// # Ok(()) }
+ /// ```
+ #[cfg(any(feature = "alloc", feature = "std"))]
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
+ fn boxed(self) -> Box<dyn Layer<S> + Send + Sync + 'static>
+ where
+ Self: Sized,
+ Self: Layer<S> + Send + Sync + 'static,
+ S: Subscriber,
+ {
+ Box::new(self)
+ }
+
#[doc(hidden)]
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
if id == TypeId::of::<Self>() {
@@ -880,6 +1234,7 @@ where
feature! {
#![all(feature = "registry", feature = "std")]
+
/// A per-[`Layer`] filter that determines whether a span or event is enabled
/// for an individual layer.
///
@@ -1022,6 +1377,26 @@ feature! {
Interest::sometimes()
}
+ /// Called before the filtered [`Layer]'s [`on_event`], to determine if
+ /// `on_event` should be called.
+ ///
+ /// This gives a chance to filter events based on their fields. Note,
+ /// however, that this *does not* override [`enabled`], and is not even
+ /// called if [`enabled`] returns `false`.
+ ///
+ /// ## Default Implementation
+ ///
+ /// By default, this method returns `true`, indicating that no events are
+ /// filtered out based on their fields.
+ ///
+ /// [`enabled`]: crate::layer::Filter::enabled
+ /// [`on_event`]: crate::layer::Layer::on_event
+ #[inline] // collapse this to a constant please mrs optimizer
+ fn event_enabled(&self, event: &Event<'_>, cx: &Context<'_, S>) -> bool {
+ let _ = (event, cx);
+ true
+ }
+
/// Returns an optional hint of the highest [verbosity level][level] that
/// this `Filter` will enable.
///
@@ -1054,6 +1429,51 @@ feature! {
fn max_level_hint(&self) -> Option<LevelFilter> {
None
}
+
+ /// Notifies this filter that a new span was constructed with the given
+ /// `Attributes` and `Id`.
+ ///
+ /// By default, this method does nothing. `Filter` implementations that
+ /// need to be notified when new spans are created can override this
+ /// method.
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+ let _ = (attrs, id, ctx);
+ }
+
+
+ /// Notifies this filter that a span with the given `Id` recorded the given
+ /// `values`.
+ ///
+ /// By default, this method does nothing. `Filter` implementations that
+ /// need to be notified when new spans are created can override this
+ /// method.
+ fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+ let _ = (id, values, ctx);
+ }
+
+ /// Notifies this filter that a span with the given ID was entered.
+ ///
+ /// By default, this method does nothing. `Filter` implementations that
+ /// need to be notified when a span is entered can override this method.
+ fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+ let _ = (id, ctx);
+ }
+
+ /// Notifies this filter that a span with the given ID was exited.
+ ///
+ /// By default, this method does nothing. `Filter` implementations that
+ /// need to be notified when a span is exited can override this method.
+ fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+ let _ = (id, ctx);
+ }
+
+ /// Notifies this filter that a span with the given ID has been closed.
+ ///
+ /// By default, this method does nothing. `Filter` implementations that
+ /// need to be notified when a span is closed can override this method.
+ fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+ let _ = (id, ctx);
+ }
}
}
@@ -1077,6 +1497,47 @@ pub struct Identity {
// === impl Layer ===
+#[derive(Clone, Copy)]
+pub(crate) struct NoneLayerMarker(());
+static NONE_LAYER_MARKER: NoneLayerMarker = NoneLayerMarker(());
+
+/// Is a type implementing `Layer` `Option::<_>::None`?
+pub(crate) fn layer_is_none<L, S>(layer: &L) -> bool
+where
+ L: Layer<S>,
+ S: Subscriber,
+{
+ unsafe {
+ // Safety: we're not actually *doing* anything with this pointer ---
+ // this only care about the `Option`, which is essentially being used
+ // as a bool. We can rely on the pointer being valid, because it is
+ // a crate-private type, and is only returned by the `Layer` impl
+ // for `Option`s. However, even if the layer *does* decide to be
+ // evil and give us an invalid pointer here, that's fine, because we'll
+ // never actually dereference it.
+ layer.downcast_raw(TypeId::of::<NoneLayerMarker>())
+ }
+ .is_some()
+}
+
+/// Is a type implementing `Subscriber` `Option::<_>::None`?
+pub(crate) fn subscriber_is_none<S>(subscriber: &S) -> bool
+where
+ S: Subscriber,
+{
+ unsafe {
+ // Safety: we're not actually *doing* anything with this pointer ---
+ // this only care about the `Option`, which is essentially being used
+ // as a bool. We can rely on the pointer being valid, because it is
+ // a crate-private type, and is only returned by the `Layer` impl
+ // for `Option`s. However, even if the subscriber *does* decide to be
+ // evil and give us an invalid pointer here, that's fine, because we'll
+ // never actually dereference it.
+ subscriber.downcast_raw(TypeId::of::<NoneLayerMarker>())
+ }
+ .is_some()
+}
+
impl<L, S> Layer<S> for Option<L>
where
L: Layer<S>,
@@ -1115,7 +1576,11 @@ where
fn max_level_hint(&self) -> Option<LevelFilter> {
match self {
Some(ref inner) => inner.max_level_hint(),
- None => None,
+ None => {
+ // There is no inner layer, so this layer will
+ // never enable anything.
+ Some(LevelFilter::OFF)
+ }
}
}
@@ -1134,6 +1599,14 @@ where
}
#[inline]
+ fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
+ match self {
+ Some(ref inner) => inner.event_enabled(event, ctx),
+ None => true,
+ }
+ }
+
+ #[inline]
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
if let Some(ref inner) = self {
inner.on_event(event, ctx);
@@ -1173,6 +1646,8 @@ where
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
if id == TypeId::of::<Self>() {
Some(self as *const _ as *const ())
+ } else if id == TypeId::of::<NoneLayerMarker>() && self.is_none() {
+ Some(&NONE_LAYER_MARKER as *const _ as *const ())
} else {
self.as_ref().and_then(|inner| inner.downcast_raw(id))
}
@@ -1181,10 +1656,17 @@ where
feature! {
#![any(feature = "std", feature = "alloc")]
+ #[cfg(not(feature = "std"))]
+ use alloc::vec::Vec;
macro_rules! layer_impl_body {
() => {
#[inline]
+ fn on_register_dispatch(&self, subscriber: &Dispatch) {
+ self.deref().on_register_dispatch(subscriber);
+ }
+
+ #[inline]
fn on_layer(&mut self, subscriber: &mut S) {
self.deref_mut().on_layer(subscriber);
}
@@ -1220,6 +1702,11 @@ feature! {
}
#[inline]
+ fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
+ self.deref().event_enabled(event, ctx)
+ }
+
+ #[inline]
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
self.deref().on_event(event, ctx)
}
@@ -1266,6 +1753,125 @@ feature! {
{
layer_impl_body! {}
}
+
+
+
+ impl<S, L> Layer<S> for Vec<L>
+ where
+ L: Layer<S>,
+ S: Subscriber,
+ {
+
+ fn on_layer(&mut self, subscriber: &mut S) {
+ for l in self {
+ l.on_layer(subscriber);
+ }
+ }
+
+ fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
+ // Return highest level of interest.
+ let mut interest = Interest::never();
+ for l in self {
+ let new_interest = l.register_callsite(metadata);
+ if (interest.is_sometimes() && new_interest.is_always())
+ || (interest.is_never() && !new_interest.is_never())
+ {
+ interest = new_interest;
+ }
+ }
+
+ interest
+ }
+
+ fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
+ self.iter().all(|l| l.enabled(metadata, ctx.clone()))
+ }
+
+ fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
+ self.iter().all(|l| l.event_enabled(event, ctx.clone()))
+ }
+
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+ for l in self {
+ l.on_new_span(attrs, id, ctx.clone());
+ }
+ }
+
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ // Default to `OFF` if there are no inner layers.
+ let mut max_level = LevelFilter::OFF;
+ for l in self {
+ // NOTE(eliza): this is slightly subtle: if *any* layer
+ // returns `None`, we have to return `None`, assuming there is
+ // no max level hint, since that particular layer cannot
+ // provide a hint.
+ let hint = l.max_level_hint()?;
+ max_level = core::cmp::max(hint, max_level);
+ }
+ Some(max_level)
+ }
+
+ fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+ for l in self {
+ l.on_record(span, values, ctx.clone())
+ }
+ }
+
+ fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) {
+ for l in self {
+ l.on_follows_from(span, follows, ctx.clone());
+ }
+ }
+
+ fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
+ for l in self {
+ l.on_event(event, ctx.clone());
+ }
+ }
+
+ fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+ for l in self {
+ l.on_enter(id, ctx.clone());
+ }
+ }
+
+ fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+ for l in self {
+ l.on_exit(id, ctx.clone());
+ }
+ }
+
+ fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+ for l in self {
+ l.on_close(id.clone(), ctx.clone());
+ }
+ }
+
+ #[doc(hidden)]
+ unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
+ // If downcasting to `Self`, return a pointer to `self`.
+ if id == TypeId::of::<Self>() {
+ return Some(self as *const _ as *const ());
+ }
+
+ // Someone is looking for per-layer filters. But, this `Vec`
+ // might contain layers with per-layer filters *and*
+ // layers without filters. It should only be treated as a
+ // per-layer-filtered layer if *all* its layers have
+ // per-layer filters.
+ // XXX(eliza): it's a bummer we have to do this linear search every
+ // time. It would be nice if this could be cached, but that would
+ // require replacing the `Vec` impl with an impl for a newtype...
+ if filter::is_plf_downcast_marker(id) && self.iter().any(|s| s.downcast_raw(id).is_none()) {
+ return None;
+ }
+
+ // Otherwise, return the first child of `self` that downcaasts to
+ // the selected type, if any.
+ // XXX(eliza): hope this is reasonable lol
+ self.iter().find_map(|l| l.downcast_raw(id))
+ }
+ }
}
// === impl SubscriberExt ===
diff --git a/vendor/tracing-subscriber-0.3.3/src/layer/tests.rs b/vendor/tracing-subscriber/src/layer/tests.rs
index d7ad61769..d7ad61769 100644
--- a/vendor/tracing-subscriber-0.3.3/src/layer/tests.rs
+++ b/vendor/tracing-subscriber/src/layer/tests.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/lib.rs b/vendor/tracing-subscriber/src/lib.rs
index 563a86dee..808923007 100644
--- a/vendor/tracing-subscriber-0.3.3/src/lib.rs
+++ b/vendor/tracing-subscriber/src/lib.rs
@@ -10,7 +10,7 @@
//! `tracing-subscriber` is intended for use by both `Subscriber` authors and
//! application authors using `tracing` to instrument their applications.
//!
-//! *Compiler support: [requires `rustc` 1.42+][msrv]*
+//! *Compiler support: [requires `rustc` 1.50+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -60,9 +60,11 @@
//! **Requires "std"**.
//! - `json`: Enables `fmt` support for JSON output. In JSON output, the ANSI
//! feature does nothing. **Requires "fmt" and "std"**.
-//! - [`local-time`]: Enables local time formatting when using the [`time`
+//! - `local-time`: Enables local time formatting when using the [`time`
//! crate]'s timestamp formatters with the `fmt` subscriber.
//!
+//! [`registry`]: mod@registry
+//!
//! ### Optional Dependencies
//!
//! - [`tracing-log`]: Enables better formatting for events emitted by `log`
@@ -80,7 +82,7 @@
//! used without requiring the Rust standard library, although some features are
//! disabled. Although most of the APIs provided by `tracing-subscriber`, such
//! as [`fmt`] and [`EnvFilter`], require the standard library, some
-//! functionality, such as the [`Subscriber`] trait, can still be used in
+//! functionality, such as the [`Layer`] trait, can still be used in
//! `no_std` environments.
//!
//! The dependency on the standard library is controlled by two crate feature
@@ -102,10 +104,41 @@
//! tracing-subscriber = { version = "0.3", default-features = false, features = ["alloc"] }
//! ```
//!
+//! ### Unstable Features
+//!
+//! These feature flags enable **unstable** features. The public API may break in 0.1.x
+//! releases. To enable these features, the `--cfg tracing_unstable` must be passed to
+//! `rustc` when compiling.
+//!
+//! The following unstable feature flags are currently available:
+//!
+//! * `valuable`: Enables support for serializing values recorded using the
+//! [`valuable`] crate as structured JSON in the [`format::Json`] formatter.
+//!
+//! #### Enabling Unstable Features
+//!
+//! The easiest way to set the `tracing_unstable` cfg is to use the `RUSTFLAGS`
+//! env variable when running `cargo` commands:
+//!
+//! ```shell
+//! RUSTFLAGS="--cfg tracing_unstable" cargo build
+//! ```
+//! Alternatively, the following can be added to the `.cargo/config` file in a
+//! project to automatically enable the cfg flag for that project:
+//!
+//! ```toml
+//! [build]
+//! rustflags = ["--cfg", "tracing_unstable"]
+//! ```
+//!
+//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
+//! [`valuable`]: https://crates.io/crates/valuable
+//! [`format::Json`]: crate::fmt::format::Json
+//!
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.42. The current Tracing version is not guaranteed to build on
+//! version is 1.50. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
@@ -116,17 +149,18 @@
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
-//! [`tracing`]: https://docs.rs/tracing/latest/tracing/
-//! [`Subscriber`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html
-//! [`EnvFilter`]: filter/struct.EnvFilter.html
-//! [`fmt`]: fmt/index.html
+//! [`Subscriber`]: tracing_core::subscriber::Subscriber
+//! [`tracing`]: https://docs.rs/tracing/latest/tracing
+//! [`EnvFilter`]: filter::EnvFilter
+//! [`fmt`]: mod@fmt
//! [`tracing-log`]: https://crates.io/crates/tracing-log
//! [`smallvec`]: https://crates.io/crates/smallvec
//! [`env_logger` crate]: https://crates.io/crates/env_logger
//! [`parking_lot`]: https://crates.io/crates/parking_lot
//! [`time` crate]: https://crates.io/crates/time
-//! [`liballoc`]: https://doc.rust-lang.org/alloc/index.html
-#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.1")]
+//! [`libstd`]: std
+//! [`liballoc`]: alloc
+#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.15")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
diff --git a/vendor/tracing-subscriber-0.3.3/src/macros.rs b/vendor/tracing-subscriber/src/macros.rs
index 81351132f..81351132f 100644
--- a/vendor/tracing-subscriber-0.3.3/src/macros.rs
+++ b/vendor/tracing-subscriber/src/macros.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/prelude.rs b/vendor/tracing-subscriber/src/prelude.rs
index c2230907b..c2230907b 100644
--- a/vendor/tracing-subscriber-0.3.3/src/prelude.rs
+++ b/vendor/tracing-subscriber/src/prelude.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/registry/extensions.rs b/vendor/tracing-subscriber/src/registry/extensions.rs
index 899e1549f..ff76fb599 100644
--- a/vendor/tracing-subscriber-0.3.3/src/registry/extensions.rs
+++ b/vendor/tracing-subscriber/src/registry/extensions.rs
@@ -78,7 +78,7 @@ impl<'a> ExtensionsMut<'a> {
/// should be able to reuse timestamp _x_.
///
/// Therefore, extensions should generally be newtypes, rather than common
- /// types like [`String`](https://doc.rust-lang.org/std/string/struct.String.html), to avoid accidental
+ /// types like [`String`](std::string::String), to avoid accidental
/// cross-`Layer` clobbering.
///
/// ## Panics
diff --git a/vendor/tracing-subscriber-0.3.3/src/registry/mod.rs b/vendor/tracing-subscriber/src/registry/mod.rs
index f3b77b6a9..38af53e8a 100644
--- a/vendor/tracing-subscriber-0.3.3/src/registry/mod.rs
+++ b/vendor/tracing-subscriber/src/registry/mod.rs
@@ -55,8 +55,7 @@
//! require the root subscriber to be a registry.
//!
//! [`Layer`]: crate::layer::Layer
-//! [`Subscriber`]:
-//! https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html
+//! [`Subscriber`]: tracing_core::Subscriber
//! [ctx]: crate::layer::Context
//! [lookup]: crate::layer::Context::span()
use tracing_core::{field::FieldSet, span::Id, Metadata};
@@ -87,9 +86,9 @@ feature! {
/// implement this trait; if they do, any [`Layer`]s wrapping them can look up
/// metadata via the [`Context`] type's [`span()`] method.
///
-/// [`Layer`]: ../layer/trait.Layer.html
-/// [`Context`]: ../layer/struct.Context.html
-/// [`span()`]: ../layer/struct.Context.html#method.span
+/// [`Layer`]: super::layer::Layer
+/// [`Context`]: super::layer::Context
+/// [`span()`]: super::layer::Context::span
pub trait LookupSpan<'a> {
/// The type of span data stored in this registry.
type Data: SpanData<'a>;
@@ -104,7 +103,6 @@ pub trait LookupSpan<'a> {
/// capable of performing more sophisiticated queries.
/// </pre>
///
- /// [`SpanData`]: trait.SpanData.html
fn span_data(&'a self, id: &Id) -> Option<Self::Data>;
/// Returns a [`SpanRef`] for the span with the given `Id`, if it exists.
@@ -116,9 +114,7 @@ pub trait LookupSpan<'a> {
/// rather than the [`span_data`] method; while _implementors_ of this trait
/// should only implement `span_data`.
///
- /// [`SpanRef`]: struct.SpanRef.html
- /// [`SpanData`]: trait.SpanData.html
- /// [`span_data`]: #method.span_data
+ /// [`span_data`]: LookupSpan::span_data()
fn span(&'a self, id: &Id) -> Option<SpanRef<'_, Self>>
where
Self: Sized,
@@ -208,8 +204,8 @@ pub trait SpanData<'a> {
/// provides additional methods for querying the registry based on values from
/// the span.
///
-/// [span data]: trait.SpanData.html
-/// [registry]: trait.LookupSpan.html
+/// [span data]: SpanData
+/// [registry]: LookupSpan
#[derive(Debug)]
pub struct SpanRef<'a, R: LookupSpan<'a>> {
registry: &'a R,
@@ -360,7 +356,7 @@ where
/// Returns a list of [fields] defined by the span.
///
- /// [fields]: https://docs.rs/tracing-core/latest/tracing_core/field/index.html
+ /// [fields]: tracing_core::field
pub fn fields(&self) -> &FieldSet {
self.data.metadata().fields()
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/registry/sharded.rs b/vendor/tracing-subscriber/src/registry/sharded.rs
index a6311cb71..797899767 100644
--- a/vendor/tracing-subscriber-0.3.3/src/registry/sharded.rs
+++ b/vendor/tracing-subscriber/src/registry/sharded.rs
@@ -75,16 +75,16 @@ use tracing_core::{
/// the distributed tracing system. These IDs can be associated with
/// `tracing` spans using [fields] and/or [stored span data].
///
-/// [span IDs]: https://docs.rs/tracing-core/latest/tracing_core/span/struct.Id.html
-/// [slab]: https://docs.rs/crate/sharded-slab/
+/// [span IDs]: tracing_core::span::Id
+/// [slab]: sharded_slab
/// [`Layer`]: crate::Layer
/// [added]: crate::layer::Layer#composing-layers
/// [extensions]: super::Extensions
/// [closed]: https://docs.rs/tracing/latest/tracing/span/index.html#closing-spans
-/// [considered closed]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html#method.try_close
+/// [considered closed]: tracing_core::subscriber::Subscriber::try_close()
/// [`Span`]: https://docs.rs/tracing/latest/tracing/span/struct.Span.html
/// [ot]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#spancontext
-/// [fields]: https://docs.rs/tracing-core/latest/tracing-core/field/index.html
+/// [fields]: tracing_core::field
/// [stored span data]: crate::registry::SpanData::extensions_mut
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
@@ -102,9 +102,8 @@ pub struct Registry {
/// [`Layer`s], such as formatted fields, metrics, or distributed traces should
/// be stored in the [extensions] typemap.
///
-/// [`Registry`]: struct.Registry.html
-/// [`Layer`s]: ../layer/trait.Layer.html
-/// [extensions]: struct.Extensions.html
+/// [`Layer`s]: crate::layer::Layer
+/// [extensions]: Extensions
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
#[derive(Debug)]
@@ -115,10 +114,11 @@ pub struct Data<'a> {
/// Stored data associated with a span.
///
-/// This type is pooled using `sharded_slab::Pool`; when a span is dropped, the
-/// `DataInner` entry at that span's slab index is cleared in place and reused
-/// by a future span. Thus, the `Default` and `sharded_slab::Clear`
-/// implementations for this type are load-bearing.
+/// This type is pooled using [`sharded_slab::Pool`]; when a span is
+/// dropped, the `DataInner` entry at that span's slab index is cleared
+/// in place and reused by a future span. Thus, the `Default` and
+/// [`sharded_slab::Clear`] implementations for this type are
+/// load-bearing.
#[derive(Debug)]
struct DataInner {
filter_map: FilterMap,
@@ -173,7 +173,6 @@ fn id_to_idx(id: &Id) -> usize {
/// greater than 0, `CloseGuard` decrements the counter by one and
/// _does not_ remove the span from the [`Registry`].
///
-/// [`Registry`]: ./struct.Registry.html
pub(crate) struct CloseGuard<'a> {
id: Id,
registry: &'a Registry,
@@ -189,7 +188,6 @@ impl Registry {
/// processed an `on_close` notification via the `CLOSE_COUNT` thread-local.
/// For additional details, see [`CloseGuard`].
///
- /// [`CloseGuard`]: ./struct.CloseGuard.html
pub(crate) fn start_close(&self, id: Id) -> CloseGuard<'_> {
CLOSE_COUNT.with(|count| {
let c = count.get();
@@ -216,7 +214,6 @@ thread_local! {
/// track how many layers have processed the close.
/// For additional details, see [`CloseGuard`].
///
- /// [`CloseGuard`]: ./struct.CloseGuard.html
static CLOSE_COUNT: Cell<usize> = Cell::new(0);
}
@@ -278,6 +275,13 @@ impl Subscriber for Registry {
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {}
+ fn event_enabled(&self, _event: &Event<'_>) -> bool {
+ if self.has_per_layer_filters() {
+ return FilterState::event_enabled();
+ }
+ true
+ }
+
/// This is intentionally not implemented, as recording events
/// is the responsibility of layers atop of this registry.
fn event(&self, _: &Event<'_>) {}
@@ -380,7 +384,7 @@ impl<'a> LookupSpan<'a> for Registry {
// === impl CloseGuard ===
impl<'a> CloseGuard<'a> {
- pub(crate) fn is_closing(&mut self) {
+ pub(crate) fn set_closing(&mut self) {
self.is_closing = true;
}
}
diff --git a/vendor/tracing-subscriber-0.3.3/src/registry/stack.rs b/vendor/tracing-subscriber/src/registry/stack.rs
index 4a3f7e59d..4a3f7e59d 100644
--- a/vendor/tracing-subscriber-0.3.3/src/registry/stack.rs
+++ b/vendor/tracing-subscriber/src/registry/stack.rs
diff --git a/vendor/tracing-subscriber/src/reload.rs b/vendor/tracing-subscriber/src/reload.rs
new file mode 100644
index 000000000..096f83d38
--- /dev/null
+++ b/vendor/tracing-subscriber/src/reload.rs
@@ -0,0 +1,384 @@
+//! Wrapper for a `Layer` to allow it to be dynamically reloaded.
+//!
+//! This module provides a [`Layer` type] implementing the [`Layer` trait] or [`Filter` trait]
+//! which wraps another type implementing the corresponding trait. This
+//! allows the wrapped type to be replaced with another
+//! instance of that type at runtime.
+//!
+//! This can be used in cases where a subset of `Layer` or `Filter` functionality
+//! should be dynamically reconfigured, such as when filtering directives may
+//! change at runtime. Note that this layer introduces a (relatively small)
+//! amount of overhead, and should thus only be used as needed.
+//!
+//! # Examples
+//!
+//! Reloading a [global filtering](crate::layer#global-filtering) layer:
+//!
+//! ```rust
+//! # use tracing::info;
+//! use tracing_subscriber::{filter, fmt, reload, prelude::*};
+//! let filter = filter::LevelFilter::WARN;
+//! let (filter, reload_handle) = reload::Layer::new(filter);
+//! tracing_subscriber::registry()
+//! .with(filter)
+//! .with(fmt::Layer::default())
+//! .init();
+//! #
+//! # // specifying the Registry type is required
+//! # let _: &reload::Handle<filter::LevelFilter, tracing_subscriber::Registry> = &reload_handle;
+//! #
+//! info!("This will be ignored");
+//! reload_handle.modify(|filter| *filter = filter::LevelFilter::INFO);
+//! info!("This will be logged");
+//! ```
+//!
+//! Reloading a [`Filtered`](crate::filter::Filtered) layer:
+//!
+//! ```rust
+//! # use tracing::info;
+//! use tracing_subscriber::{filter, fmt, reload, prelude::*};
+//! let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN);
+//! let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer);
+//! #
+//! # // specifying the Registry type is required
+//! # let _: &reload::Handle<filter::Filtered<fmt::Layer<tracing_subscriber::Registry>,
+//! # filter::LevelFilter, tracing_subscriber::Registry>,tracing_subscriber::Registry>
+//! # = &reload_handle;
+//! #
+//! tracing_subscriber::registry()
+//! .with(filtered_layer)
+//! .init();
+//! info!("This will be ignored");
+//! reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO);
+//! info!("This will be logged");
+//! ```
+//!
+//! ## Note
+//!
+//! The [`Layer`] implementation is unable to implement downcasting functionality,
+//! so certain [`Layer`] will fail to downcast if wrapped in a `reload::Layer`.
+//!
+//! If you only want to be able to dynamically change the
+//! `Filter` on a layer, prefer wrapping that `Filter` in the `reload::Layer`.
+//!
+//! [`Filter` trait]: crate::layer::Filter
+//! [`Layer` type]: Layer
+//! [`Layer` trait]: super::layer::Layer
+use crate::layer;
+use crate::sync::RwLock;
+
+use core::any::TypeId;
+use std::{
+ error, fmt,
+ marker::PhantomData,
+ sync::{Arc, Weak},
+};
+use tracing_core::{
+ callsite, span,
+ subscriber::{Interest, Subscriber},
+ Dispatch, Event, LevelFilter, Metadata,
+};
+
+/// Wraps a `Layer` or `Filter`, allowing it to be reloaded dynamically at runtime.
+#[derive(Debug)]
+pub struct Layer<L, S> {
+ // TODO(eliza): this once used a `crossbeam_util::ShardedRwLock`. We may
+ // eventually wish to replace it with a sharded lock implementation on top
+ // of our internal `RwLock` wrapper type. If possible, we should profile
+ // this first to determine if it's necessary.
+ inner: Arc<RwLock<L>>,
+ _s: PhantomData<fn(S)>,
+}
+
+/// Allows reloading the state of an associated [`Layer`](crate::layer::Layer).
+#[derive(Debug)]
+pub struct Handle<L, S> {
+ inner: Weak<RwLock<L>>,
+ _s: PhantomData<fn(S)>,
+}
+
+/// Indicates that an error occurred when reloading a layer.
+#[derive(Debug)]
+pub struct Error {
+ kind: ErrorKind,
+}
+
+#[derive(Debug)]
+enum ErrorKind {
+ SubscriberGone,
+ Poisoned,
+}
+
+// ===== impl Layer =====
+
+impl<L, S> crate::Layer<S> for Layer<L, S>
+where
+ L: crate::Layer<S> + 'static,
+ S: Subscriber,
+{
+ fn on_register_dispatch(&self, subscriber: &Dispatch) {
+ try_lock!(self.inner.read()).on_register_dispatch(subscriber);
+ }
+
+ fn on_layer(&mut self, subscriber: &mut S) {
+ try_lock!(self.inner.write(), else return).on_layer(subscriber);
+ }
+
+ #[inline]
+ fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
+ try_lock!(self.inner.read(), else return Interest::sometimes()).register_callsite(metadata)
+ }
+
+ #[inline]
+ fn enabled(&self, metadata: &Metadata<'_>, ctx: layer::Context<'_, S>) -> bool {
+ try_lock!(self.inner.read(), else return false).enabled(metadata, ctx)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_record(span, values, ctx)
+ }
+
+ #[inline]
+ fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_follows_from(span, follows, ctx)
+ }
+
+ #[inline]
+ fn event_enabled(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) -> bool {
+ try_lock!(self.inner.read(), else return false).event_enabled(event, ctx)
+ }
+
+ #[inline]
+ fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_event(event, ctx)
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_enter(id, ctx)
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_exit(id, ctx)
+ }
+
+ #[inline]
+ fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_close(id, ctx)
+ }
+
+ #[inline]
+ fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_id_change(old, new, ctx)
+ }
+
+ #[inline]
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ try_lock!(self.inner.read(), else return None).max_level_hint()
+ }
+
+ #[doc(hidden)]
+ unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
+ // Safety: it is generally unsafe to downcast through a reload, because
+ // the pointer can be invalidated after the lock is dropped.
+ // `NoneLayerMarker` is a special case because it
+ // is never dereferenced.
+ //
+ // Additionally, even if the marker type *is* dereferenced (which it
+ // never will be), the pointer should be valid even if the subscriber
+ // is reloaded, because all `NoneLayerMarker` pointers that we return
+ // actually point to the global static singleton `NoneLayerMarker`,
+ // rather than to a field inside the lock.
+ if id == TypeId::of::<layer::NoneLayerMarker>() {
+ return try_lock!(self.inner.read(), else return None).downcast_raw(id);
+ }
+
+ None
+ }
+}
+
+// ===== impl Filter =====
+
+#[cfg(all(feature = "registry", feature = "std"))]
+#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
+impl<S, L> crate::layer::Filter<S> for Layer<L, S>
+where
+ L: crate::layer::Filter<S> + 'static,
+ S: Subscriber,
+{
+ #[inline]
+ fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
+ try_lock!(self.inner.read(), else return Interest::sometimes()).callsite_enabled(metadata)
+ }
+
+ #[inline]
+ fn enabled(&self, metadata: &Metadata<'_>, ctx: &layer::Context<'_, S>) -> bool {
+ try_lock!(self.inner.read(), else return false).enabled(metadata, ctx)
+ }
+
+ #[inline]
+ fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_new_span(attrs, id, ctx)
+ }
+
+ #[inline]
+ fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_record(span, values, ctx)
+ }
+
+ #[inline]
+ fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_enter(id, ctx)
+ }
+
+ #[inline]
+ fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_exit(id, ctx)
+ }
+
+ #[inline]
+ fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
+ try_lock!(self.inner.read()).on_close(id, ctx)
+ }
+
+ #[inline]
+ fn max_level_hint(&self) -> Option<LevelFilter> {
+ try_lock!(self.inner.read(), else return None).max_level_hint()
+ }
+}
+
+impl<L, S> Layer<L, S> {
+ /// Wraps the given [`Layer`] or [`Filter`], returning a `reload::Layer`
+ /// and a `Handle` that allows the inner value to be modified at runtime.
+ ///
+ /// [`Layer`]: crate::layer::Layer
+ /// [`Filter`]: crate::layer::Filter
+ pub fn new(inner: L) -> (Self, Handle<L, S>) {
+ let this = Self {
+ inner: Arc::new(RwLock::new(inner)),
+ _s: PhantomData,
+ };
+ let handle = this.handle();
+ (this, handle)
+ }
+
+ /// Returns a `Handle` that can be used to reload the wrapped [`Layer`] or [`Filter`].
+ ///
+ /// [`Layer`]: crate::layer::Layer
+ /// [`Filter`]: crate::filter::Filter
+ pub fn handle(&self) -> Handle<L, S> {
+ Handle {
+ inner: Arc::downgrade(&self.inner),
+ _s: PhantomData,
+ }
+ }
+}
+
+// ===== impl Handle =====
+
+impl<L, S> Handle<L, S> {
+ /// Replace the current [`Layer`] or [`Filter`] with the provided `new_value`.
+ ///
+ /// [`Handle::reload`] cannot be used with the [`Filtered`] layer; use
+ /// [`Handle::modify`] instead (see [this issue] for additional details).
+ ///
+ /// However, if the _only_ the [`Filter`] needs to be modified, use
+ /// `reload::Layer` to wrap the `Filter` directly.
+ ///
+ /// [`Layer`]: crate::layer::Layer
+ /// [`Filter`]: crate::layer::Filter
+ /// [`Filtered`]: crate::filter::Filtered
+ ///
+ /// [this issue]: https://github.com/tokio-rs/tracing/issues/1629
+ pub fn reload(&self, new_value: impl Into<L>) -> Result<(), Error> {
+ self.modify(|layer| {
+ *layer = new_value.into();
+ })
+ }
+
+ /// Invokes a closure with a mutable reference to the current layer or filter,
+ /// allowing it to be modified in place.
+ pub fn modify(&self, f: impl FnOnce(&mut L)) -> Result<(), Error> {
+ let inner = self.inner.upgrade().ok_or(Error {
+ kind: ErrorKind::SubscriberGone,
+ })?;
+
+ let mut lock = try_lock!(inner.write(), else return Err(Error::poisoned()));
+ f(&mut *lock);
+ // Release the lock before rebuilding the interest cache, as that
+ // function will lock the new layer.
+ drop(lock);
+
+ callsite::rebuild_interest_cache();
+ Ok(())
+ }
+
+ /// Returns a clone of the layer or filter's current value if it still exists.
+ /// Otherwise, if the subscriber has been dropped, returns `None`.
+ pub fn clone_current(&self) -> Option<L>
+ where
+ L: Clone,
+ {
+ self.with_current(L::clone).ok()
+ }
+
+ /// Invokes a closure with a borrowed reference to the current layer or filter,
+ /// returning the result (or an error if the subscriber no longer exists).
+ pub fn with_current<T>(&self, f: impl FnOnce(&L) -> T) -> Result<T, Error> {
+ let inner = self.inner.upgrade().ok_or(Error {
+ kind: ErrorKind::SubscriberGone,
+ })?;
+ let inner = try_lock!(inner.read(), else return Err(Error::poisoned()));
+ Ok(f(&*inner))
+ }
+}
+
+impl<L, S> Clone for Handle<L, S> {
+ fn clone(&self) -> Self {
+ Handle {
+ inner: self.inner.clone(),
+ _s: PhantomData,
+ }
+ }
+}
+
+// ===== impl Error =====
+
+impl Error {
+ fn poisoned() -> Self {
+ Self {
+ kind: ErrorKind::Poisoned,
+ }
+ }
+
+ /// Returns `true` if this error occurred because the layer was poisoned by
+ /// a panic on another thread.
+ pub fn is_poisoned(&self) -> bool {
+ matches!(self.kind, ErrorKind::Poisoned)
+ }
+
+ /// Returns `true` if this error occurred because the `Subscriber`
+ /// containing the reloadable layer was dropped.
+ pub fn is_dropped(&self) -> bool {
+ matches!(self.kind, ErrorKind::SubscriberGone)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let msg = match self.kind {
+ ErrorKind::SubscriberGone => "subscriber no longer exists",
+ ErrorKind::Poisoned => "lock poisoned",
+ };
+ f.pad(msg)
+ }
+}
+
+impl error::Error for Error {}
diff --git a/vendor/tracing-subscriber-0.3.3/src/sync.rs b/vendor/tracing-subscriber/src/sync.rs
index ec42b834a..ec42b834a 100644
--- a/vendor/tracing-subscriber-0.3.3/src/sync.rs
+++ b/vendor/tracing-subscriber/src/sync.rs
diff --git a/vendor/tracing-subscriber-0.3.3/src/util.rs b/vendor/tracing-subscriber/src/util.rs
index 1c98aa4d2..1c98aa4d2 100644
--- a/vendor/tracing-subscriber-0.3.3/src/util.rs
+++ b/vendor/tracing-subscriber/src/util.rs