blob: 97453e08bed497e25bcac29bf1010836448ff82b [file] [log] [blame]
//! Metadata describing trace data.
use super::{callsite, field};
use crate::stdlib::{fmt, str::FromStr};
/// 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].
/// - 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.
///
/// **Note**: Although instances of `Metadata` cannot be compared directly, they
/// provide a method [`id`] which returns an opaque [callsite identifier]
/// which uniquely identifies the callsite where the metadata originated.
/// This can be used for determining if two Metadata correspond to
/// the same callsite.
///
/// [span]: ../span/index.html
/// [event]: ../event/index.html
/// [name]: #method.name
/// [target]: #method.target
/// [fields]: #method.fields
/// [verbosity level]: #method.level
/// [file name]: #method.file
/// [line number]: #method.line
/// [module path]: #method.module
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`id`]: struct.Metadata.html#method.id
/// [callsite identifier]: ../callsite/struct.Identifier.html
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, Debug, Eq, PartialEq)]
pub struct Kind(KindInner);
/// Describes the level of verbosity of a span or event.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Level(LevelInner);
// ===== 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()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum KindInner {
Event,
Span,
}
impl Kind {
/// `Event` callsite
pub const EVENT: Kind = Kind(KindInner::Event);
/// `Span` callsite
pub const SPAN: Kind = Kind(KindInner::Span);
/// Return true if the callsite kind is `Span`
pub fn is_span(&self) -> bool {
match self {
Kind(KindInner::Span) => true,
_ => false,
}
}
/// Return true if the callsite kind is `Event`
pub fn is_event(&self) -> bool {
match self {
Kind(KindInner::Event) => true,
_ => false,
}
}
}
// ===== 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);
}
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, Eq, PartialEq, Hash, Ord, PartialOrd)]
enum LevelInner {
/// The "error" level.
///
/// Designates very serious errors.
Error = 1,
/// The "warn" level.
///
/// Designates hazardous situations.
Warn,
/// The "info" level.
///
/// Designates useful information.
Info,
/// The "debug" level.
///
/// Designates lower priority information.
Debug,
/// The "trace" level.
///
/// Designates very low priority, often extremely verbose, information.
Trace,
}
/// 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",
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[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())
}
}