Files
tokio/tokio-trace/tokio-trace-core/src/metadata.rs
T
Eliza Weismanandcsmoe 847fb59b17 trace-core: Introduce callsite classification in metadata (#1046)
## Motivation

To ease the implementation of `Subscriber::register_callsite`, a field
should be added to `Metadata` to indicate if this callsite is an event or
a span.

## Solution

A new struct, `Kind`, is added to the `metadata` module in
`tokio-trace-core`, and a `Kind` field is added to the `Metadata`
struct. Macros which construct `metadata` now require a `Kind`.

`Kind` is represented as a struct with a private inner enum to allow new
`Kind`s to be added without breaking changes. However, the _addition_ of
the kind field _is_ a breaking change. While this could be done in a
backward-compatible way, it would permit the construction of metadata
with unknown kinds, and since the next `tokio-trace-core` release will
be a breaking change, I opted to make the breaking change instead.

New API tests for the `callsite!` and `metadata!` macros have been added
to guard against future API breakage.

Fixes: #986
Closes: #1008

Co-Authored-By: csmoe <[email protected]>
2019-04-11 11:56:42 -07:00

350 lines
12 KiB
Rust

//! Metadata describing trace data.
use super::{
callsite::{self, Callsite},
field,
};
use std::fmt;
/// Metadata describing a [span] or [event].
///
/// This includes the source code location where the span occurred, the names of
/// its fields, et cetera.
///
/// 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 [`Metadata::id()`] which returns an 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
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`Metadata::id()`]: struct.Metadata.html#method.id
/// [callsite identifier]: ../callsite/struct.Identifier.html
// TODO: When `const fn` is stable, make this type's fields private.
pub struct Metadata<'a> {
/// The name of the span described by this metadata.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub name: &'static str,
/// The part of the system that the span that this metadata describes
/// occurred in.
///
/// Typically, this is the module path, but alternate targets may be set
/// when spans or events are constructed.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub target: &'a str,
/// The level of verbosity of the described span.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub level: Level,
/// The name of the Rust module where the span occurred, or `None` if this
/// could not be determined.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub module_path: Option<&'a str>,
/// The name of the source code file where the span occurred, or `None` if
/// this could not be determined.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub file: Option<&'a str>,
/// The line number in the source code file where the span occurred, or
/// `None` if this could not be determined.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub line: Option<u32>,
/// The names of the key-value fields attached to the described span or
/// event.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub fields: field::FieldSet,
/// The kind of the callsite.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub kind: Kind,
}
/// Indicate 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, with a name, target, level, field
/// names, and optional source code location.
pub fn new(
name: &'static str,
target: &'a str,
level: Level,
module_path: Option<&'a str>,
file: Option<&'a str>,
line: Option<u32>,
field_names: &'static [&'static str],
callsite: &'static Callsite,
kind: Kind,
) -> Self {
Metadata {
name,
target,
level,
module_path,
file,
line,
fields: field::FieldSet {
names: field_names,
callsite: callsite::Identifier(callsite),
},
kind,
}
}
/// Returns the set of fields on the described span.
pub fn fields(&self) -> &field::FieldSet {
&self.fields
}
/// Returns the level of verbosity of the described span.
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);
}
#[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,
}