aboutsummaryrefslogtreecommitdiff
path: root/src/metadata.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/metadata.rs')
-rw-r--r--src/metadata.rs1114
1 files changed, 1114 insertions, 0 deletions
diff --git a/src/metadata.rs b/src/metadata.rs
new file mode 100644
index 0000000..a154419
--- /dev/null
+++ b/src/metadata.rs
@@ -0,0 +1,1114 @@
+//! Metadata describing trace data.
+use super::{callsite, field};
+use crate::stdlib::{
+ cmp, fmt,
+ str::FromStr,
+ sync::atomic::{AtomicUsize, Ordering},
+};
+
+/// Metadata describing a [span] or [event].
+///
+/// All spans and events have the following metadata:
+/// - A [name], represented as a static string.
+/// - A [target], a string that categorizes part of the system where the span
+/// or event occurred. The `tracing` macros default to using the module
+/// path where the span or event originated as the target, but it may be
+/// overridden.
+/// - A [verbosity level]. This determines how verbose a given span or event
+/// is, and allows enabling or disabling more verbose diagnostics
+/// situationally. See the documentation for the [`Level`] type for details.
+/// - The names of the [fields] defined by the span or event.
+/// - Whether the metadata corresponds to a span or event.
+///
+/// In addition, the following optional metadata describing the source code
+/// location where the span or event originated _may_ be provided:
+/// - The [file name]
+/// - The [line number]
+/// - The [module path]
+///
+/// Metadata is used by [`Subscriber`]s when filtering spans and events, and it
+/// may also be used as part of their data payload.
+///
+/// When created by the `event!` or `span!` macro, the metadata describing a
+/// particular event or span is constructed statically and exists as a single
+/// static instance. Thus, the overhead of creating the metadata is
+/// _significantly_ lower than that of creating the actual span. Therefore,
+/// filtering is based on metadata, rather than on the constructed span.
+///
+/// ## Equality
+///
+/// In well-behaved applications, two `Metadata` with equal
+/// [callsite identifiers] will be equal in all other ways (i.e., have the same
+/// `name`, `target`, etc.). Consequently, in release builds, [`Metadata::eq`]
+/// *only* checks that its arguments have equal callsites. However, the equality
+/// of `Metadata`'s other fields is checked in debug builds.
+///
+/// [span]: super::span
+/// [event]: super::event
+/// [name]: Self::name
+/// [target]: Self::target
+/// [fields]: Self::fields
+/// [verbosity level]: Self::level
+/// [file name]: Self::file
+/// [line number]: Self::line
+/// [module path]: Self::module_path
+/// [`Subscriber`]: super::subscriber::Subscriber
+/// [callsite identifiers]: Self::callsite
+pub struct Metadata<'a> {
+ /// The name of the span described by this metadata.
+ name: &'static str,
+
+ /// The part of the system that the span that this metadata describes
+ /// occurred in.
+ target: &'a str,
+
+ /// The level of verbosity of the described span.
+ level: Level,
+
+ /// The name of the Rust module where the span occurred, or `None` if this
+ /// could not be determined.
+ module_path: Option<&'a str>,
+
+ /// The name of the source code file where the span occurred, or `None` if
+ /// this could not be determined.
+ file: Option<&'a str>,
+
+ /// The line number in the source code file where the span occurred, or
+ /// `None` if this could not be determined.
+ line: Option<u32>,
+
+ /// The names of the key-value fields attached to the described span or
+ /// event.
+ fields: field::FieldSet,
+
+ /// The kind of the callsite.
+ kind: Kind,
+}
+
+/// Indicates whether the callsite is a span or event.
+#[derive(Clone, Eq, PartialEq)]
+pub struct Kind(u8);
+
+/// Describes the level of verbosity of a span or event.
+///
+/// # Comparing Levels
+///
+/// `Level` implements the [`PartialOrd`] and [`Ord`] traits, allowing two
+/// `Level`s to be compared to determine which is considered more or less
+/// verbose. Levels which are more verbose are considered "greater than" levels
+/// which are less verbose, with [`Level::ERROR`] considered the lowest, and
+/// [`Level::TRACE`] considered the highest.
+///
+/// For example:
+/// ```
+/// use tracing_core::Level;
+///
+/// assert!(Level::TRACE > Level::DEBUG);
+/// assert!(Level::ERROR < Level::WARN);
+/// assert!(Level::INFO <= Level::DEBUG);
+/// assert_eq!(Level::TRACE, Level::TRACE);
+/// ```
+///
+/// # Filtering
+///
+/// `Level`s are typically used to implement filtering that determines which
+/// spans and events are enabled. Depending on the use case, more or less
+/// verbose diagnostics may be desired. For example, when running in
+/// development, [`DEBUG`]-level traces may be enabled by default. When running in
+/// production, only [`INFO`]-level and lower traces might be enabled. Libraries
+/// may include very verbose diagnostics at the [`DEBUG`] and/or [`TRACE`] levels.
+/// Applications using those libraries typically chose to ignore those traces. However, when
+/// debugging an issue involving said libraries, it may be useful to temporarily
+/// enable the more verbose traces.
+///
+/// The [`LevelFilter`] type is provided to enable filtering traces by
+/// verbosity. `Level`s can be compared against [`LevelFilter`]s, and
+/// [`LevelFilter`] has a variant for each `Level`, which compares analogously
+/// to that level. In addition, [`LevelFilter`] adds a [`LevelFilter::OFF`]
+/// variant, which is considered "less verbose" than every other `Level`. This is
+/// intended to allow filters to completely disable tracing in a particular context.
+///
+/// For example:
+/// ```
+/// use tracing_core::{Level, LevelFilter};
+///
+/// assert!(LevelFilter::OFF < Level::TRACE);
+/// assert!(LevelFilter::TRACE > Level::DEBUG);
+/// assert!(LevelFilter::ERROR < Level::WARN);
+/// assert!(LevelFilter::INFO <= Level::DEBUG);
+/// assert!(LevelFilter::INFO >= Level::INFO);
+/// ```
+///
+/// ## Examples
+///
+/// Below is a simple example of how a [`Subscriber`] could implement filtering through
+/// a [`LevelFilter`]. When a span or event is recorded, the [`Subscriber::enabled`] method
+/// compares the span or event's `Level` against the configured [`LevelFilter`].
+/// The optional [`Subscriber::max_level_hint`] method can also be implemented to allow spans
+/// and events above a maximum verbosity level to be skipped more efficiently,
+/// often improving performance in short-lived programs.
+///
+/// ```
+/// use tracing_core::{span, Event, Level, LevelFilter, Subscriber, Metadata};
+/// # use tracing_core::span::{Id, Record, Current};
+///
+/// #[derive(Debug)]
+/// pub struct MySubscriber {
+/// /// The most verbose level that this subscriber will enable.
+/// max_level: LevelFilter,
+///
+/// // ...
+/// }
+///
+/// impl MySubscriber {
+/// /// Returns a new `MySubscriber` which will record spans and events up to
+/// /// `max_level`.
+/// pub fn with_max_level(max_level: LevelFilter) -> Self {
+/// Self {
+/// max_level,
+/// // ...
+/// }
+/// }
+/// }
+/// impl Subscriber for MySubscriber {
+/// fn enabled(&self, meta: &Metadata<'_>) -> bool {
+/// // A span or event is enabled if it is at or below the configured
+/// // maximum level.
+/// meta.level() <= &self.max_level
+/// }
+///
+/// // This optional method returns the most verbose level that this
+/// // subscriber will enable. Although implementing this method is not
+/// // *required*, it permits additional optimizations when it is provided,
+/// // allowing spans and events above the max level to be skipped
+/// // more efficiently.
+/// fn max_level_hint(&self) -> Option<LevelFilter> {
+/// Some(self.max_level)
+/// }
+///
+/// // Implement the rest of the subscriber...
+/// fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
+/// // ...
+/// # drop(span); Id::from_u64(1)
+/// }
+
+/// fn event(&self, event: &Event<'_>) {
+/// // ...
+/// # drop(event);
+/// }
+///
+/// // ...
+/// # fn enter(&self, _: &Id) {}
+/// # fn exit(&self, _: &Id) {}
+/// # fn record(&self, _: &Id, _: &Record<'_>) {}
+/// # fn record_follows_from(&self, _: &Id, _: &Id) {}
+/// }
+/// ```
+///
+/// It is worth noting that the `tracing-subscriber` crate provides [additional
+/// APIs][envfilter] for performing more sophisticated filtering, such as
+/// enabling different levels based on which module or crate a span or event is
+/// recorded in.
+///
+/// [`DEBUG`]: Level::DEBUG
+/// [`INFO`]: Level::INFO
+/// [`TRACE`]: Level::TRACE
+/// [`Subscriber::enabled`]: crate::subscriber::Subscriber::enabled
+/// [`Subscriber::max_level_hint`]: crate::subscriber::Subscriber::max_level_hint
+/// [`Subscriber`]: crate::subscriber::Subscriber
+/// [envfilter]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Level(LevelInner);
+
+/// A filter comparable to a verbosity [`Level`].
+///
+/// If a [`Level`] is considered less than a `LevelFilter`, it should be
+/// considered enabled; if greater than or equal to the `LevelFilter`,
+/// that level is disabled. See [`LevelFilter::current`] for more
+/// details.
+///
+/// Note that this is essentially identical to the `Level` type, but with the
+/// addition of an [`OFF`] level that completely disables all trace
+/// instrumentation.
+///
+/// See the documentation for the [`Level`] type to see how `Level`s
+/// and `LevelFilter`s interact.
+///
+/// [`OFF`]: LevelFilter::OFF
+#[repr(transparent)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
+pub struct LevelFilter(Option<Level>);
+
+/// Indicates that a string could not be parsed to a valid level.
+#[derive(Clone, Debug)]
+pub struct ParseLevelFilterError(());
+
+static MAX_LEVEL: AtomicUsize = AtomicUsize::new(LevelFilter::OFF_USIZE);
+
+// ===== impl Metadata =====
+
+impl<'a> Metadata<'a> {
+ /// Construct new metadata for a span or event, with a name, target, level, field
+ /// names, and optional source code location.
+ pub const fn new(
+ name: &'static str,
+ target: &'a str,
+ level: Level,
+ file: Option<&'a str>,
+ line: Option<u32>,
+ module_path: Option<&'a str>,
+ fields: field::FieldSet,
+ kind: Kind,
+ ) -> Self {
+ Metadata {
+ name,
+ target,
+ level,
+ module_path,
+ file,
+ line,
+ fields,
+ kind,
+ }
+ }
+
+ /// Returns the names of the fields on the described span or event.
+ pub fn fields(&self) -> &field::FieldSet {
+ &self.fields
+ }
+
+ /// Returns the level of verbosity of the described span or event.
+ pub fn level(&self) -> &Level {
+ &self.level
+ }
+
+ /// Returns the name of the span.
+ pub fn name(&self) -> &'static str {
+ self.name
+ }
+
+ /// Returns a string describing the part of the system where the span or
+ /// event that this metadata describes occurred.
+ ///
+ /// Typically, this is the module path, but alternate targets may be set
+ /// when spans or events are constructed.
+ pub fn target(&self) -> &'a str {
+ self.target
+ }
+
+ /// Returns the path to the Rust module where the span occurred, or
+ /// `None` if the module path is unknown.
+ pub fn module_path(&self) -> Option<&'a str> {
+ self.module_path
+ }
+
+ /// Returns the name of the source code file where the span
+ /// occurred, or `None` if the file is unknown
+ pub fn file(&self) -> Option<&'a str> {
+ self.file
+ }
+
+ /// Returns the line number in the source code file where the span
+ /// occurred, or `None` if the line number is unknown.
+ pub fn line(&self) -> Option<u32> {
+ self.line
+ }
+
+ /// Returns an opaque `Identifier` that uniquely identifies the callsite
+ /// this `Metadata` originated from.
+ #[inline]
+ pub fn callsite(&self) -> callsite::Identifier {
+ self.fields.callsite()
+ }
+
+ /// Returns true if the callsite kind is `Event`.
+ pub fn is_event(&self) -> bool {
+ self.kind.is_event()
+ }
+
+ /// Return true if the callsite kind is `Span`.
+ pub fn is_span(&self) -> bool {
+ self.kind.is_span()
+ }
+}
+
+impl<'a> fmt::Debug for Metadata<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut meta = f.debug_struct("Metadata");
+ meta.field("name", &self.name)
+ .field("target", &self.target)
+ .field("level", &self.level);
+
+ if let Some(path) = self.module_path() {
+ meta.field("module_path", &path);
+ }
+
+ match (self.file(), self.line()) {
+ (Some(file), Some(line)) => {
+ meta.field("location", &format_args!("{}:{}", file, line));
+ }
+ (Some(file), None) => {
+ meta.field("file", &format_args!("{}", file));
+ }
+
+ // Note: a line num with no file is a kind of weird case that _probably_ never occurs...
+ (None, Some(line)) => {
+ meta.field("line", &line);
+ }
+ (None, None) => {}
+ };
+
+ meta.field("fields", &format_args!("{}", self.fields))
+ .field("callsite", &self.callsite())
+ .field("kind", &self.kind)
+ .finish()
+ }
+}
+
+impl Kind {
+ const EVENT_BIT: u8 = 1 << 0;
+ const SPAN_BIT: u8 = 1 << 1;
+ const HINT_BIT: u8 = 1 << 2;
+
+ /// `Event` callsite
+ pub const EVENT: Kind = Kind(Self::EVENT_BIT);
+
+ /// `Span` callsite
+ pub const SPAN: Kind = Kind(Self::SPAN_BIT);
+
+ /// `enabled!` callsite. [`Subscriber`][`crate::subscriber::Subscriber`]s can assume
+ /// this `Kind` means they will never recieve a
+ /// full event with this [`Metadata`].
+ pub const HINT: Kind = Kind(Self::HINT_BIT);
+
+ /// Return true if the callsite kind is `Span`
+ pub fn is_span(&self) -> bool {
+ self.0 & Self::SPAN_BIT == Self::SPAN_BIT
+ }
+
+ /// Return true if the callsite kind is `Event`
+ pub fn is_event(&self) -> bool {
+ self.0 & Self::EVENT_BIT == Self::EVENT_BIT
+ }
+
+ /// Return true if the callsite kind is `Hint`
+ pub fn is_hint(&self) -> bool {
+ self.0 & Self::HINT_BIT == Self::HINT_BIT
+ }
+
+ /// Sets that this `Kind` is a [hint](Self::HINT).
+ ///
+ /// This can be called on [`SPAN`](Self::SPAN) and [`EVENT`](Self::EVENT)
+ /// kinds to construct a hint callsite that also counts as a span or event.
+ pub const fn hint(self) -> Self {
+ Self(self.0 | Self::HINT_BIT)
+ }
+}
+
+impl fmt::Debug for Kind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("Kind(")?;
+ let mut has_bits = false;
+ let mut write_bit = |name: &str| {
+ if has_bits {
+ f.write_str(" | ")?;
+ }
+ f.write_str(name)?;
+ has_bits = true;
+ Ok(())
+ };
+
+ if self.is_event() {
+ write_bit("EVENT")?;
+ }
+
+ if self.is_span() {
+ write_bit("SPAN")?;
+ }
+
+ if self.is_hint() {
+ write_bit("HINT")?;
+ }
+
+ // if none of the expected bits were set, something is messed up, so
+ // just print the bits for debugging purposes
+ if !has_bits {
+ write!(f, "{:#b}", self.0)?;
+ }
+
+ f.write_str(")")
+ }
+}
+
+impl<'a> Eq for Metadata<'a> {}
+
+impl<'a> PartialEq for Metadata<'a> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ if core::ptr::eq(&self, &other) {
+ true
+ } else if cfg!(not(debug_assertions)) {
+ // In a well-behaving application, two `Metadata` can be assumed to
+ // be totally equal so long as they share the same callsite.
+ self.callsite() == other.callsite()
+ } else {
+ // However, when debug-assertions are enabled, do not assume that
+ // the application is well-behaving; check every field of `Metadata`
+ // for equality.
+
+ // `Metadata` is destructured here to ensure a compile-error if the
+ // fields of `Metadata` change.
+ let Metadata {
+ name: lhs_name,
+ target: lhs_target,
+ level: lhs_level,
+ module_path: lhs_module_path,
+ file: lhs_file,
+ line: lhs_line,
+ fields: lhs_fields,
+ kind: lhs_kind,
+ } = self;
+
+ let Metadata {
+ name: rhs_name,
+ target: rhs_target,
+ level: rhs_level,
+ module_path: rhs_module_path,
+ file: rhs_file,
+ line: rhs_line,
+ fields: rhs_fields,
+ kind: rhs_kind,
+ } = &other;
+
+ // The initial comparison of callsites is purely an optimization;
+ // it can be removed without affecting the overall semantics of the
+ // expression.
+ self.callsite() == other.callsite()
+ && lhs_name == rhs_name
+ && lhs_target == rhs_target
+ && lhs_level == rhs_level
+ && lhs_module_path == rhs_module_path
+ && lhs_file == rhs_file
+ && lhs_line == rhs_line
+ && lhs_fields == rhs_fields
+ && lhs_kind == rhs_kind
+ }
+ }
+}
+
+// ===== impl Level =====
+
+impl Level {
+ /// The "error" level.
+ ///
+ /// Designates very serious errors.
+ pub const ERROR: Level = Level(LevelInner::Error);
+ /// The "warn" level.
+ ///
+ /// Designates hazardous situations.
+ pub const WARN: Level = Level(LevelInner::Warn);
+ /// The "info" level.
+ ///
+ /// Designates useful information.
+ pub const INFO: Level = Level(LevelInner::Info);
+ /// The "debug" level.
+ ///
+ /// Designates lower priority information.
+ pub const DEBUG: Level = Level(LevelInner::Debug);
+ /// The "trace" level.
+ ///
+ /// Designates very low priority, often extremely verbose, information.
+ pub const TRACE: Level = Level(LevelInner::Trace);
+
+ /// Returns the string representation of the `Level`.
+ ///
+ /// This returns the same string as the `fmt::Display` implementation.
+ pub fn as_str(&self) -> &'static str {
+ match *self {
+ Level::TRACE => "TRACE",
+ Level::DEBUG => "DEBUG",
+ Level::INFO => "INFO",
+ Level::WARN => "WARN",
+ Level::ERROR => "ERROR",
+ }
+ }
+}
+
+impl fmt::Display for Level {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ Level::TRACE => f.pad("TRACE"),
+ Level::DEBUG => f.pad("DEBUG"),
+ Level::INFO => f.pad("INFO"),
+ Level::WARN => f.pad("WARN"),
+ Level::ERROR => f.pad("ERROR"),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl crate::stdlib::error::Error for ParseLevelError {}
+
+impl FromStr for Level {
+ type Err = ParseLevelError;
+ fn from_str(s: &str) -> Result<Self, ParseLevelError> {
+ s.parse::<usize>()
+ .map_err(|_| ParseLevelError { _p: () })
+ .and_then(|num| match num {
+ 1 => Ok(Level::ERROR),
+ 2 => Ok(Level::WARN),
+ 3 => Ok(Level::INFO),
+ 4 => Ok(Level::DEBUG),
+ 5 => Ok(Level::TRACE),
+ _ => Err(ParseLevelError { _p: () }),
+ })
+ .or_else(|_| match s {
+ s if s.eq_ignore_ascii_case("error") => Ok(Level::ERROR),
+ s if s.eq_ignore_ascii_case("warn") => Ok(Level::WARN),
+ s if s.eq_ignore_ascii_case("info") => Ok(Level::INFO),
+ s if s.eq_ignore_ascii_case("debug") => Ok(Level::DEBUG),
+ s if s.eq_ignore_ascii_case("trace") => Ok(Level::TRACE),
+ _ => Err(ParseLevelError { _p: () }),
+ })
+ }
+}
+
+#[repr(usize)]
+#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
+enum LevelInner {
+ /// The "trace" level.
+ ///
+ /// Designates very low priority, often extremely verbose, information.
+ Trace = 0,
+ /// The "debug" level.
+ ///
+ /// Designates lower priority information.
+ Debug = 1,
+ /// The "info" level.
+ ///
+ /// Designates useful information.
+ Info = 2,
+ /// The "warn" level.
+ ///
+ /// Designates hazardous situations.
+ Warn = 3,
+ /// The "error" level.
+ ///
+ /// Designates very serious errors.
+ Error = 4,
+}
+
+// === impl LevelFilter ===
+
+impl From<Level> for LevelFilter {
+ #[inline]
+ fn from(level: Level) -> Self {
+ Self::from_level(level)
+ }
+}
+
+impl From<Option<Level>> for LevelFilter {
+ #[inline]
+ fn from(level: Option<Level>) -> Self {
+ Self(level)
+ }
+}
+
+impl From<LevelFilter> for Option<Level> {
+ #[inline]
+ fn from(filter: LevelFilter) -> Self {
+ filter.into_level()
+ }
+}
+
+impl LevelFilter {
+ /// The "off" level.
+ ///
+ /// Designates that trace instrumentation should be completely disabled.
+ pub const OFF: LevelFilter = LevelFilter(None);
+ /// The "error" level.
+ ///
+ /// Designates very serious errors.
+ pub const ERROR: LevelFilter = LevelFilter::from_level(Level::ERROR);
+ /// The "warn" level.
+ ///
+ /// Designates hazardous situations.
+ pub const WARN: LevelFilter = LevelFilter::from_level(Level::WARN);
+ /// The "info" level.
+ ///
+ /// Designates useful information.
+ pub const INFO: LevelFilter = LevelFilter::from_level(Level::INFO);
+ /// The "debug" level.
+ ///
+ /// Designates lower priority information.
+ pub const DEBUG: LevelFilter = LevelFilter::from_level(Level::DEBUG);
+ /// The "trace" level.
+ ///
+ /// Designates very low priority, often extremely verbose, information.
+ pub const TRACE: LevelFilter = LevelFilter(Some(Level::TRACE));
+
+ /// Returns a `LevelFilter` that enables spans and events with verbosity up
+ /// to and including `level`.
+ pub const fn from_level(level: Level) -> Self {
+ Self(Some(level))
+ }
+
+ /// Returns the most verbose [`Level`] that this filter accepts, or `None`
+ /// if it is [`OFF`].
+ ///
+ /// [`OFF`]: LevelFilter::OFF
+ pub const fn into_level(self) -> Option<Level> {
+ self.0
+ }
+
+ // These consts are necessary because `as` casts are not allowed as
+ // match patterns.
+ const ERROR_USIZE: usize = LevelInner::Error as usize;
+ const WARN_USIZE: usize = LevelInner::Warn as usize;
+ const INFO_USIZE: usize = LevelInner::Info as usize;
+ const DEBUG_USIZE: usize = LevelInner::Debug as usize;
+ const TRACE_USIZE: usize = LevelInner::Trace as usize;
+ // Using the value of the last variant + 1 ensures that we match the value
+ // for `Option::None` as selected by the niche optimization for
+ // `LevelFilter`. If this is the case, converting a `usize` value into a
+ // `LevelFilter` (in `LevelFilter::current`) will be an identity conversion,
+ // rather than generating a lookup table.
+ const OFF_USIZE: usize = LevelInner::Error as usize + 1;
+
+ /// Returns a `LevelFilter` that matches the most verbose [`Level`] that any
+ /// currently active [`Subscriber`] will enable.
+ ///
+ /// User code should treat this as a *hint*. If a given span or event has a
+ /// level *higher* than the returned `LevelFilter`, it will not be enabled.
+ /// However, if the level is less than or equal to this value, the span or
+ /// event is *not* guaranteed to be enabled; the subscriber will still
+ /// filter each callsite individually.
+ ///
+ /// Therefore, comparing a given span or event's level to the returned
+ /// `LevelFilter` **can** be used for determining if something is
+ /// *disabled*, but **should not** be used for determining if something is
+ /// *enabled*.
+ ///
+ /// [`Level`]: super::Level
+ /// [`Subscriber`]: super::Subscriber
+ #[inline(always)]
+ pub fn current() -> Self {
+ match MAX_LEVEL.load(Ordering::Relaxed) {
+ Self::ERROR_USIZE => Self::ERROR,
+ Self::WARN_USIZE => Self::WARN,
+ Self::INFO_USIZE => Self::INFO,
+ Self::DEBUG_USIZE => Self::DEBUG,
+ Self::TRACE_USIZE => Self::TRACE,
+ Self::OFF_USIZE => Self::OFF,
+ #[cfg(debug_assertions)]
+ unknown => unreachable!(
+ "/!\\ `LevelFilter` representation seems to have changed! /!\\ \n\
+ This is a bug (and it's pretty bad). Please contact the `tracing` \
+ maintainers. Thank you and I'm sorry.\n \
+ The offending repr was: {:?}",
+ unknown,
+ ),
+ #[cfg(not(debug_assertions))]
+ _ => unsafe {
+ // Using `unreachable_unchecked` here (rather than
+ // `unreachable!()`) is necessary to ensure that rustc generates
+ // an identity conversion from integer -> discriminant, rather
+ // than generating a lookup table. We want to ensure this
+ // function is a single `mov` instruction (on x86) if at all
+ // possible, because it is called *every* time a span/event
+ // callsite is hit; and it is (potentially) the only code in the
+ // hottest path for skipping a majority of callsites when level
+ // filtering is in use.
+ //
+ // safety: This branch is only truly unreachable if we guarantee
+ // that no values other than the possible enum discriminants
+ // will *ever* be present. The `AtomicUsize` is initialized to
+ // the `OFF` value. It is only set by the `set_max` function,
+ // which takes a `LevelFilter` as a parameter. This restricts
+ // the inputs to `set_max` to the set of valid discriminants.
+ // Therefore, **as long as `MAX_VALUE` is only ever set by
+ // `set_max`**, this is safe.
+ crate::stdlib::hint::unreachable_unchecked()
+ },
+ }
+ }
+
+ pub(crate) fn set_max(LevelFilter(level): LevelFilter) {
+ let val = match level {
+ Some(Level(level)) => level as usize,
+ None => Self::OFF_USIZE,
+ };
+
+ // using an AcqRel swap ensures an ordered relationship of writes to the
+ // max level.
+ MAX_LEVEL.swap(val, Ordering::AcqRel);
+ }
+}
+
+impl fmt::Display for LevelFilter {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ LevelFilter::OFF => f.pad("off"),
+ LevelFilter::ERROR => f.pad("error"),
+ LevelFilter::WARN => f.pad("warn"),
+ LevelFilter::INFO => f.pad("info"),
+ LevelFilter::DEBUG => f.pad("debug"),
+ LevelFilter::TRACE => f.pad("trace"),
+ }
+ }
+}
+
+impl fmt::Debug for LevelFilter {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ LevelFilter::OFF => f.pad("LevelFilter::OFF"),
+ LevelFilter::ERROR => f.pad("LevelFilter::ERROR"),
+ LevelFilter::WARN => f.pad("LevelFilter::WARN"),
+ LevelFilter::INFO => f.pad("LevelFilter::INFO"),
+ LevelFilter::DEBUG => f.pad("LevelFilter::DEBUG"),
+ LevelFilter::TRACE => f.pad("LevelFilter::TRACE"),
+ }
+ }
+}
+
+impl FromStr for LevelFilter {
+ type Err = ParseLevelFilterError;
+ fn from_str(from: &str) -> Result<Self, Self::Err> {
+ from.parse::<usize>()
+ .ok()
+ .and_then(|num| match num {
+ 0 => Some(LevelFilter::OFF),
+ 1 => Some(LevelFilter::ERROR),
+ 2 => Some(LevelFilter::WARN),
+ 3 => Some(LevelFilter::INFO),
+ 4 => Some(LevelFilter::DEBUG),
+ 5 => Some(LevelFilter::TRACE),
+ _ => None,
+ })
+ .or_else(|| match from {
+ "" => Some(LevelFilter::ERROR),
+ s if s.eq_ignore_ascii_case("error") => Some(LevelFilter::ERROR),
+ s if s.eq_ignore_ascii_case("warn") => Some(LevelFilter::WARN),
+ s if s.eq_ignore_ascii_case("info") => Some(LevelFilter::INFO),
+ s if s.eq_ignore_ascii_case("debug") => Some(LevelFilter::DEBUG),
+ s if s.eq_ignore_ascii_case("trace") => Some(LevelFilter::TRACE),
+ s if s.eq_ignore_ascii_case("off") => Some(LevelFilter::OFF),
+ _ => None,
+ })
+ .ok_or(ParseLevelFilterError(()))
+ }
+}
+
+/// Returned if parsing a `Level` fails.
+#[derive(Debug)]
+pub struct ParseLevelError {
+ _p: (),
+}
+
+impl fmt::Display for ParseLevelError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(
+ "error parsing level: expected one of \"error\", \"warn\", \
+ \"info\", \"debug\", \"trace\", or a number 1-5",
+ )
+ }
+}
+
+impl fmt::Display for ParseLevelFilterError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(
+ "error parsing level filter: expected one of \"off\", \"error\", \
+ \"warn\", \"info\", \"debug\", \"trace\", or a number 0-5",
+ )
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for ParseLevelFilterError {}
+
+// ==== Level and LevelFilter comparisons ====
+
+// /!\ BIG, IMPORTANT WARNING /!\
+// Do NOT mess with these implementations! They are hand-written for a reason!
+//
+// Since comparing `Level`s and `LevelFilter`s happens in a *very* hot path
+// (potentially, every time a span or event macro is hit, regardless of whether
+// or not is enabled), we *need* to ensure that these comparisons are as fast as
+// possible. Therefore, we have some requirements:
+//
+// 1. We want to do our best to ensure that rustc will generate integer-integer
+// comparisons wherever possible.
+//
+// The derived `Ord`/`PartialOrd` impls for `LevelFilter` will not do this,
+// because `LevelFilter`s are represented by `Option<Level>`, rather than as
+// a separate `#[repr(usize)]` enum. This was (unfortunately) necessary for
+// backwards-compatibility reasons, as the `tracing` crate's original
+// version of `LevelFilter` defined `const fn` conversions between `Level`s
+// and `LevelFilter`, so we're stuck with the `Option<Level>` repr.
+// Therefore, we need hand-written `PartialOrd` impls that cast both sides of
+// the comparison to `usize`s, to force the compiler to generate integer
+// compares.
+//
+// 2. The hottest `Level`/`LevelFilter` comparison, the one that happens every
+// time a callsite is hit, occurs *within the `tracing` crate's macros*.
+// This means that the comparison is happening *inside* a crate that
+// *depends* on `tracing-core`, not in `tracing-core` itself. The compiler
+// will only inline function calls across crate boundaries if the called
+// function is annotated with an `#[inline]` attribute, and we *definitely*
+// want the comparison functions to be inlined: as previously mentioned, they
+// should compile down to a single integer comparison on release builds, and
+// it seems really sad to push an entire stack frame to call a function
+// consisting of one `cmp` instruction!
+//
+// Therefore, we need to ensure that all the comparison methods have
+// `#[inline]` or `#[inline(always)]` attributes. It's not sufficient to just
+// add the attribute to `partial_cmp` in a manual implementation of the
+// trait, since it's the comparison operators (`lt`, `le`, `gt`, and `ge`)
+// that will actually be *used*, and the default implementation of *those*
+// methods, which calls `partial_cmp`, does not have an inline annotation.
+//
+// 3. We need the comparisons to be inverted. The discriminants for the
+// `LevelInner` enum are assigned in "backwards" order, with `TRACE` having
+// the *lowest* value. However, we want `TRACE` to compare greater-than all
+// other levels.
+//
+// Why are the numeric values inverted? In order to ensure that `LevelFilter`
+// (which, as previously mentioned, *has* to be internally represented by an
+// `Option<Level>`) compiles down to a single integer value. This is
+// necessary for storing the global max in an `AtomicUsize`, and for ensuring
+// that we use fast integer-integer comparisons, as mentioned previously. In
+// order to ensure this, we exploit the niche optimization. The niche
+// optimization for `Option<{enum with a numeric repr}>` will choose
+// `(HIGHEST_DISCRIMINANT_VALUE + 1)` as the representation for `None`.
+// Therefore, the integer representation of `LevelFilter::OFF` (which is
+// `None`) will be the number 5. `OFF` must compare higher than every other
+// level in order for it to filter as expected. Since we want to use a single
+// `cmp` instruction, we can't special-case the integer value of `OFF` to
+// compare higher, as that will generate more code. Instead, we need it to be
+// on one end of the enum, with `ERROR` on the opposite end, so we assign the
+// value 0 to `ERROR`.
+//
+// This *does* mean that when parsing `LevelFilter`s or `Level`s from
+// `String`s, the integer values are inverted, but that doesn't happen in a
+// hot path.
+//
+// Note that we manually invert the comparisons by swapping the left-hand and
+// right-hand side. Using `Ordering::reverse` generates significantly worse
+// code (per Matt Godbolt's Compiler Explorer).
+//
+// Anyway, that's a brief history of why this code is the way it is. Don't
+// change it unless you know what you're doing.
+
+impl PartialEq<LevelFilter> for Level {
+ #[inline(always)]
+ fn eq(&self, other: &LevelFilter) -> bool {
+ self.0 as usize == filter_as_usize(&other.0)
+ }
+}
+
+impl PartialOrd for Level {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+
+ #[inline(always)]
+ fn lt(&self, other: &Level) -> bool {
+ (other.0 as usize) < (self.0 as usize)
+ }
+
+ #[inline(always)]
+ fn le(&self, other: &Level) -> bool {
+ (other.0 as usize) <= (self.0 as usize)
+ }
+
+ #[inline(always)]
+ fn gt(&self, other: &Level) -> bool {
+ (other.0 as usize) > (self.0 as usize)
+ }
+
+ #[inline(always)]
+ fn ge(&self, other: &Level) -> bool {
+ (other.0 as usize) >= (self.0 as usize)
+ }
+}
+
+impl Ord for Level {
+ #[inline(always)]
+ fn cmp(&self, other: &Self) -> cmp::Ordering {
+ (other.0 as usize).cmp(&(self.0 as usize))
+ }
+}
+
+impl PartialOrd<LevelFilter> for Level {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
+ Some(filter_as_usize(&other.0).cmp(&(self.0 as usize)))
+ }
+
+ #[inline(always)]
+ fn lt(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) < (self.0 as usize)
+ }
+
+ #[inline(always)]
+ fn le(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) <= (self.0 as usize)
+ }
+
+ #[inline(always)]
+ fn gt(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) > (self.0 as usize)
+ }
+
+ #[inline(always)]
+ fn ge(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) >= (self.0 as usize)
+ }
+}
+
+#[inline(always)]
+fn filter_as_usize(x: &Option<Level>) -> usize {
+ match x {
+ Some(Level(f)) => *f as usize,
+ None => LevelFilter::OFF_USIZE,
+ }
+}
+
+impl PartialEq<Level> for LevelFilter {
+ #[inline(always)]
+ fn eq(&self, other: &Level) -> bool {
+ filter_as_usize(&self.0) == other.0 as usize
+ }
+}
+
+impl PartialOrd for LevelFilter {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+
+ #[inline(always)]
+ fn lt(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) < filter_as_usize(&self.0)
+ }
+
+ #[inline(always)]
+ fn le(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) <= filter_as_usize(&self.0)
+ }
+
+ #[inline(always)]
+ fn gt(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) > filter_as_usize(&self.0)
+ }
+
+ #[inline(always)]
+ fn ge(&self, other: &LevelFilter) -> bool {
+ filter_as_usize(&other.0) >= filter_as_usize(&self.0)
+ }
+}
+
+impl Ord for LevelFilter {
+ #[inline(always)]
+ fn cmp(&self, other: &Self) -> cmp::Ordering {
+ filter_as_usize(&other.0).cmp(&filter_as_usize(&self.0))
+ }
+}
+
+impl PartialOrd<Level> for LevelFilter {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
+ Some((other.0 as usize).cmp(&filter_as_usize(&self.0)))
+ }
+
+ #[inline(always)]
+ fn lt(&self, other: &Level) -> bool {
+ (other.0 as usize) < filter_as_usize(&self.0)
+ }
+
+ #[inline(always)]
+ fn le(&self, other: &Level) -> bool {
+ (other.0 as usize) <= filter_as_usize(&self.0)
+ }
+
+ #[inline(always)]
+ fn gt(&self, other: &Level) -> bool {
+ (other.0 as usize) > filter_as_usize(&self.0)
+ }
+
+ #[inline(always)]
+ fn ge(&self, other: &Level) -> bool {
+ (other.0 as usize) >= filter_as_usize(&self.0)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::stdlib::mem;
+
+ #[test]
+ fn level_from_str() {
+ assert_eq!("error".parse::<Level>().unwrap(), Level::ERROR);
+ assert_eq!("4".parse::<Level>().unwrap(), Level::DEBUG);
+ assert!("0".parse::<Level>().is_err())
+ }
+
+ #[test]
+ fn filter_level_conversion() {
+ let mapping = [
+ (LevelFilter::OFF, None),
+ (LevelFilter::ERROR, Some(Level::ERROR)),
+ (LevelFilter::WARN, Some(Level::WARN)),
+ (LevelFilter::INFO, Some(Level::INFO)),
+ (LevelFilter::DEBUG, Some(Level::DEBUG)),
+ (LevelFilter::TRACE, Some(Level::TRACE)),
+ ];
+ for (filter, level) in mapping.iter() {
+ assert_eq!(filter.into_level(), *level);
+ match level {
+ Some(level) => {
+ let actual: LevelFilter = (*level).into();
+ assert_eq!(actual, *filter);
+ }
+ None => {
+ let actual: LevelFilter = None.into();
+ assert_eq!(actual, *filter);
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn level_filter_is_usize_sized() {
+ assert_eq!(
+ mem::size_of::<LevelFilter>(),
+ mem::size_of::<usize>(),
+ "`LevelFilter` is no longer `usize`-sized! global MAX_LEVEL may now be invalid!"
+ )
+ }
+
+ #[test]
+ fn level_filter_reprs() {
+ let mapping = [
+ (LevelFilter::OFF, LevelInner::Error as usize + 1),
+ (LevelFilter::ERROR, LevelInner::Error as usize),
+ (LevelFilter::WARN, LevelInner::Warn as usize),
+ (LevelFilter::INFO, LevelInner::Info as usize),
+ (LevelFilter::DEBUG, LevelInner::Debug as usize),
+ (LevelFilter::TRACE, LevelInner::Trace as usize),
+ ];
+ for &(filter, expected) in &mapping {
+ let repr = unsafe {
+ // safety: The entire purpose of this test is to assert that the
+ // actual repr matches what we expect it to be --- we're testing
+ // that *other* unsafe code is sound using the transmuted value.
+ // We're not going to do anything with it that might be unsound.
+ mem::transmute::<LevelFilter, usize>(filter)
+ };
+ assert_eq!(expected, repr, "repr changed for {:?}", filter)
+ }
+ }
+}