//! 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, /// 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 { /// 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); /// 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, 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 { 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 { s.parse::() .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 for LevelFilter { #[inline] fn from(level: Level) -> Self { Self::from_level(level) } } impl From> for LevelFilter { #[inline] fn from(level: Option) -> Self { Self(level) } } impl From for Option { #[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 { 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 { from.parse::() .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`, 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` 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`) 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 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 { 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 for Level { #[inline(always)] fn partial_cmp(&self, other: &LevelFilter) -> Option { 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) -> usize { match x { Some(Level(f)) => *f as usize, None => LevelFilter::OFF_USIZE, } } impl PartialEq 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 { 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 for LevelFilter { #[inline(always)] fn partial_cmp(&self, other: &Level) -> Option { 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::().unwrap(), Level::ERROR); assert_eq!("4".parse::().unwrap(), Level::DEBUG); assert!("0".parse::().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::(), mem::size_of::(), "`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::(filter) }; assert_eq!(expected, repr, "repr changed for {:?}", filter) } } }