mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-06 00:00:10 +02:00
Introduce tokio-trace (#827)
<!-- Thank you for your Pull Request. Please provide a description above and review the requirements below. Bug fixes and new features should include tests. Contributors guide: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md --> ## Motivation In asynchronous systems like Tokio, interpreting traditional log messages can often be quite challenging. Since individual tasks are multiplexed on the same thread, associated events and log lines are intermixed making it difficult to trace the logic flow. Currently, none of the available logging frameworks or libraries in Rust offer the ability to trace logical paths through a futures-based program. There also are complementary goals that can be accomplished with such a system. For example, metrics / instrumentation can be tracked by observing emitted events, or trace data can be exported to a distributed tracing or event processing system. In addition, it can often be useful to generate this diagnostic data in a structured manner that can be consumed programmatically. While prior art for structured logging in Rust exists, it is not currently standardized, and is not "Tokio-friendly". ## Solution This branch adds a new library to the tokio project, `tokio-trace`. `tokio-trace` expands upon logging-style diagnostics by allowing libraries and applications to record structured events with additional information about *temporality* and *causality* --- unlike a log message, a span in `tokio-trace` has a beginning and end time, may be entered and exited by the flow of execution, and may exist within a nested tree of similar spans. In addition, `tokio-trace` spans are *structured*, with the ability to record typed data as well as textual messages. The `tokio-trace-core` crate contains the core primitives for this system, which are expected to remain stable, while `tokio-trace` crate provides a more "batteries-included" API. In particular, it provides macros which are a superset of the `log` crate's `error!`, `warn!`, `info!`, `debug!`, and `trace!` macros, allowing users to begin the process of adopting `tokio-trace` by performing a drop-in replacement. ## Notes Work on this project had previously been carried out in the [tokio-trace-prototype] repository. In addition to the `tokio-trace` and `tokio-trace-core` crates, the `tokio-trace-prototype` repo also contains prototypes or sketches of adapter, compatibility, and utility crates which provide useful functionality for `tokio-trace`, but these crates are not yet ready for a release. When this branch is merged, that repository will be archived, and the remaining unstable crates will be moved to a new `tokio-trace-nursery` repository. Remaining issues on the `tokio-trace-prototype` repo will be moved to the appropriate new repo. The crates added in this branch are not _identical_ to the current head of the `tokio-trace-prototype` repo, as I did some final clean-up and docs polish in this branch prior to merging this PR. [tokio-trace-prototype]: https://github.com/hawkw/tokio-trace-prototype Closes: #561 Signed-off-by: Eliza Weisman <[email protected]>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
//! Callsites represent the source locations from which spans or events
|
||||
//! originate.
|
||||
use std::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ptr,
|
||||
sync::Mutex,
|
||||
};
|
||||
use {
|
||||
dispatcher::{self, Dispatch},
|
||||
subscriber::Interest,
|
||||
Metadata,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref REGISTRY: Mutex<Registry> = Mutex::new(Registry {
|
||||
callsites: Vec::new(),
|
||||
dispatchers: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
struct Registry {
|
||||
callsites: Vec<&'static Callsite>,
|
||||
dispatchers: Vec<dispatcher::Registrar>,
|
||||
}
|
||||
|
||||
/// Trait implemented by callsites.
|
||||
pub trait Callsite: Sync {
|
||||
/// Adds the [`Interest`] returned by [registering] the callsite with a
|
||||
/// [dispatcher].
|
||||
///
|
||||
/// If the interest is greater than or equal to the callsite's current
|
||||
/// interest, this should change whether or not the callsite is enabled.
|
||||
///
|
||||
/// [`Interest`]: ::subscriber::Interest
|
||||
/// [registering]: ::subscriber::Subscriber::register_callsite
|
||||
/// [dispatcher]: ::Dispatch
|
||||
fn add_interest(&self, interest: Interest);
|
||||
|
||||
/// Remove _all_ [`Interest`] from the callsite, disabling it.
|
||||
///
|
||||
/// [`Interest`]: ::subscriber::Interest
|
||||
fn clear_interest(&self);
|
||||
|
||||
/// Returns the [metadata] associated with the callsite.
|
||||
///
|
||||
/// [metadata]: ::Metadata
|
||||
fn metadata(&self) -> &Metadata;
|
||||
}
|
||||
|
||||
/// Uniquely identifies a [`Callsite`](::callsite::Callsite).
|
||||
///
|
||||
/// Two `Identifier`s are equal if they both refer to the same callsite.
|
||||
#[derive(Clone)]
|
||||
pub struct Identifier(
|
||||
/// **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 `Identifier`. When
|
||||
/// constructing new `Identifier`s, use the `identify_callsite!` macro or
|
||||
/// the `Callsite::id` function instead.
|
||||
// TODO: When `Callsite::id` is a const fn, this need no longer be `pub`.
|
||||
#[doc(hidden)]
|
||||
pub &'static Callsite,
|
||||
);
|
||||
|
||||
/// Register a new `Callsite` with the global registry.
|
||||
///
|
||||
/// This should be called once per callsite after the callsite has been
|
||||
/// constructed.
|
||||
pub fn register(callsite: &'static Callsite) {
|
||||
let mut registry = REGISTRY.lock().unwrap();
|
||||
let meta = callsite.metadata();
|
||||
registry.dispatchers.retain(|registrar| {
|
||||
match registrar.try_register(meta) {
|
||||
Some(interest) => {
|
||||
callsite.add_interest(interest);
|
||||
true
|
||||
}
|
||||
// TODO: if the dispatcher has been dropped, should we invalidate
|
||||
// any callsites that it previously enabled?
|
||||
None => false,
|
||||
}
|
||||
});
|
||||
registry.callsites.push(callsite);
|
||||
}
|
||||
|
||||
pub(crate) fn register_dispatch(dispatch: &Dispatch) {
|
||||
let mut registry = REGISTRY.lock().unwrap();
|
||||
registry.dispatchers.push(dispatch.registrar());
|
||||
for callsite in ®istry.callsites {
|
||||
let interest = dispatch.register_callsite(callsite.metadata());
|
||||
callsite.add_interest(interest);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Identifier =====
|
||||
|
||||
impl PartialEq for Identifier {
|
||||
fn eq(&self, other: &Identifier) -> bool {
|
||||
ptr::eq(self.0, other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Identifier {}
|
||||
|
||||
impl fmt::Debug for Identifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Identifier({:p})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Identifier {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
(self.0 as *const Callsite).hash(state)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
//! Dispatches trace events to `Subscriber`s.
|
||||
use {
|
||||
callsite, field,
|
||||
subscriber::{self, Subscriber},
|
||||
Event, Metadata, Span,
|
||||
};
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fmt,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
/// `Dispatch` trace data to a [`Subscriber`](::Subscriber).
|
||||
#[derive(Clone)]
|
||||
pub struct Dispatch {
|
||||
subscriber: Arc<Subscriber + Send + Sync>,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static CURRENT_DISPATCH: RefCell<Dispatch> = RefCell::new(Dispatch::none());
|
||||
}
|
||||
|
||||
/// Sets this dispatch as the default for the duration of a closure.
|
||||
///
|
||||
/// The default dispatcher is used when creating a new [`Span`] or
|
||||
/// [`Event`], _if no span is currently executing_. If a span is currently
|
||||
/// executing, new spans or events are dispatched to the subscriber that
|
||||
/// tagged that span, instead.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
/// [`Subscriber`]: ::Subscriber
|
||||
/// [`Event`]: ::Event
|
||||
pub fn with_default<T>(dispatcher: Dispatch, f: impl FnOnce() -> T) -> T {
|
||||
// A drop guard that resets CURRENT_DISPATCH to the prior dispatcher.
|
||||
// Using this (rather than simply resetting after calling `f`) ensures
|
||||
// that we always reset to the prior dispatcher even if `f` panics.
|
||||
struct ResetGuard(Option<Dispatch>);
|
||||
impl Drop for ResetGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(dispatch) = self.0.take() {
|
||||
let _ = CURRENT_DISPATCH.try_with(|current| {
|
||||
*current.borrow_mut() = dispatch;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let prior = CURRENT_DISPATCH.try_with(|current| current.replace(dispatcher));
|
||||
let _guard = ResetGuard(prior.ok());
|
||||
f()
|
||||
}
|
||||
|
||||
/// Executes a closure with a reference to this thread's current dispatcher.
|
||||
pub fn with<T, F>(mut f: F) -> T
|
||||
where
|
||||
F: FnMut(&Dispatch) -> T,
|
||||
{
|
||||
CURRENT_DISPATCH
|
||||
.try_with(|current| f(&*current.borrow()))
|
||||
.unwrap_or_else(|_| f(&Dispatch::none()))
|
||||
}
|
||||
|
||||
pub(crate) struct Registrar(Weak<Subscriber + Send + Sync>);
|
||||
|
||||
impl Dispatch {
|
||||
/// Returns a new `Dispatch` that discards events and spans.
|
||||
pub fn none() -> Self {
|
||||
Dispatch {
|
||||
subscriber: Arc::new(NoSubscriber),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `Dispatch` to the given [`Subscriber`](::Subscriber).
|
||||
pub fn new<S>(subscriber: S) -> Self
|
||||
where
|
||||
S: Subscriber + Send + Sync + 'static,
|
||||
{
|
||||
let me = Dispatch {
|
||||
subscriber: Arc::new(subscriber),
|
||||
};
|
||||
callsite::register_dispatch(&me);
|
||||
me
|
||||
}
|
||||
|
||||
pub(crate) fn registrar(&self) -> Registrar {
|
||||
Registrar(Arc::downgrade(&self.subscriber))
|
||||
}
|
||||
|
||||
/// Registers a new callsite with this subscriber, returning whether or not
|
||||
/// the subscriber is interested in being notified about the callsite.
|
||||
///
|
||||
/// This calls the [`register_callsite`](::Subscriber::register_callsite)
|
||||
/// function on the `Subscriber` that this `Dispatch` forwards to.
|
||||
#[inline]
|
||||
pub fn register_callsite(&self, metadata: &Metadata) -> subscriber::Interest {
|
||||
self.subscriber.register_callsite(metadata)
|
||||
}
|
||||
|
||||
/// Record the construction of a new [`Span`], returning a new ID for the
|
||||
/// span being constructed.
|
||||
///
|
||||
/// This calls the [`new_span`](::Subscriber::new_span)
|
||||
/// function on the `Subscriber` that this `Dispatch` forwards to.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
#[inline]
|
||||
pub fn new_span(&self, metadata: &Metadata, values: &field::ValueSet) -> Span {
|
||||
self.subscriber.new_span(metadata, values)
|
||||
}
|
||||
|
||||
/// Record a set of values on a span.
|
||||
///
|
||||
/// This calls the [`record`](::Subscriber::record)
|
||||
/// function on the `Subscriber` that this `Dispatch` forwards to.
|
||||
#[inline]
|
||||
pub fn record(&self, span: &Span, values: &field::ValueSet) {
|
||||
self.subscriber.record(span, &values)
|
||||
}
|
||||
|
||||
/// Adds an indication that `span` follows from the span with the id
|
||||
/// `follows`.
|
||||
///
|
||||
/// This calls the [`record_follows_from`](::Subscriber::record_follows_from)
|
||||
/// function on the `Subscriber` that this `Dispatch` forwards to.
|
||||
#[inline]
|
||||
pub fn record_follows_from(&self, span: &Span, follows: &Span) {
|
||||
self.subscriber.record_follows_from(span, follows)
|
||||
}
|
||||
|
||||
/// Returns true if a span with the specified [metadata] would be
|
||||
/// recorded.
|
||||
///
|
||||
/// This calls the [`enabled`](::Subscriber::enabled) function on
|
||||
/// the `Subscriber` that this `Dispatch` forwards to.
|
||||
///
|
||||
/// [metadata]: ::Metadata
|
||||
#[inline]
|
||||
pub fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
self.subscriber.enabled(metadata)
|
||||
}
|
||||
|
||||
/// Records that an [`Event`] has occurred.
|
||||
///
|
||||
/// This calls the [`event`](::Subscriber::event) function on
|
||||
/// the `Subscriber` that this `Dispatch` forwards to.
|
||||
///
|
||||
/// [`Event`]: ::event::Event
|
||||
#[inline]
|
||||
pub fn event(&self, event: &Event) {
|
||||
self.subscriber.event(event)
|
||||
}
|
||||
|
||||
/// Records that a [`Span`] has been entered.
|
||||
///
|
||||
/// This calls the [`enter`](::Subscriber::enter) function on the
|
||||
/// `Subscriber` that this `Dispatch` forwards to.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
#[inline]
|
||||
pub fn enter(&self, span: &Span) {
|
||||
self.subscriber.enter(span)
|
||||
}
|
||||
|
||||
/// Records that a [`Span`] has been exited.
|
||||
///
|
||||
/// This calls the [`exit`](::Subscriber::exit) function on the `Subscriber`
|
||||
/// that this `Dispatch` forwards to.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
#[inline]
|
||||
pub fn exit(&self, span: &Span) {
|
||||
self.subscriber.exit(span)
|
||||
}
|
||||
|
||||
/// Notifies the subscriber that a [`Span`] has been cloned.
|
||||
///
|
||||
/// This function is guaranteed to only be called with span IDs that were
|
||||
/// returned by this `Dispatch`'s `new_span` function.
|
||||
///
|
||||
/// This calls the [`clone_span`](::Subscriber::clone_span) function on
|
||||
/// the `Subscriber` that this `Dispatch` forwards to.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
#[inline]
|
||||
pub fn clone_span(&self, id: &Span) -> Span {
|
||||
self.subscriber.clone_span(&id)
|
||||
}
|
||||
|
||||
/// Notifies the subscriber that a [`Span`] handle with the given [`Id`] has
|
||||
/// been dropped.
|
||||
///
|
||||
/// This function is guaranteed to only be called with span IDs that were
|
||||
/// returned by this `Dispatch`'s `new_span` function.
|
||||
///
|
||||
/// This calls the [`drop_span`](::Subscriber::drop_span) function on
|
||||
/// the `Subscriber` that this `Dispatch` forwards to.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
#[inline]
|
||||
pub fn drop_span(&self, id: Span) {
|
||||
self.subscriber.drop_span(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Dispatch {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.pad("Dispatch(...)")
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<S> for Dispatch
|
||||
where
|
||||
S: Subscriber + Send + Sync + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn from(subscriber: S) -> Self {
|
||||
Dispatch::new(subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
struct NoSubscriber;
|
||||
impl Subscriber for NoSubscriber {
|
||||
#[inline]
|
||||
fn register_callsite(&self, _: &Metadata) -> subscriber::Interest {
|
||||
subscriber::Interest::never()
|
||||
}
|
||||
|
||||
fn new_span(&self, _meta: &Metadata, _vals: &field::ValueSet) -> Span {
|
||||
Span::from_u64(0)
|
||||
}
|
||||
|
||||
fn event(&self, _event: &Event) {}
|
||||
|
||||
fn record(&self, _span: &Span, _values: &field::ValueSet) {}
|
||||
|
||||
fn record_follows_from(&self, _span: &Span, _follows: &Span) {}
|
||||
|
||||
#[inline]
|
||||
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn enter(&self, _span: &Span) {}
|
||||
fn exit(&self, _span: &Span) {}
|
||||
}
|
||||
|
||||
impl Registrar {
|
||||
pub(crate) fn try_register(&self, metadata: &Metadata) -> Option<subscriber::Interest> {
|
||||
self.0.upgrade().map(|s| s.register_callsite(metadata))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//! Events represent single points in time during the execution of a program.
|
||||
use {field, Metadata};
|
||||
|
||||
/// `Event`s represent single points in time where something occurred during the
|
||||
/// execution of a program.
|
||||
///
|
||||
/// An `Event` can be compared to a log record in unstructured logging, but with
|
||||
/// two key differences:
|
||||
/// - `Event`s exist _within the context of a [`Span`]_. Unlike log lines, they
|
||||
/// may be located within the trace tree, allowing visibility into the
|
||||
/// _temporal_ context in which the event occurred, as well as the source
|
||||
/// code location.
|
||||
/// - Like spans, `Event`s have structured key-value data known as _fields_,
|
||||
/// which may include textual message. In general, a majority of the data
|
||||
/// associated with an event should be in the event's fields rather than in
|
||||
/// the textual message, as the fields are more structed.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
#[derive(Debug)]
|
||||
pub struct Event<'a> {
|
||||
fields: &'a field::ValueSet<'a>,
|
||||
metadata: &'a Metadata<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Event<'a> {
|
||||
/// Constructs a new `Event` with the specified metadata and set of values,
|
||||
/// and observes it with the current subscriber.
|
||||
#[inline]
|
||||
pub fn observe(metadata: &'a Metadata<'a>, fields: &'a field::ValueSet) {
|
||||
let event = Event { metadata, fields };
|
||||
::dispatcher::with(|current| {
|
||||
current.event(&event);
|
||||
});
|
||||
}
|
||||
|
||||
/// Records all the fields on this `Event` with the specified [recorder].
|
||||
///
|
||||
/// [recorder]: ::field::Record
|
||||
#[inline]
|
||||
pub fn record(&self, recorder: &mut field::Record) {
|
||||
self.fields.record(recorder);
|
||||
}
|
||||
|
||||
/// Returns a reference to the set of values on this `Event`.
|
||||
pub fn fields(&self) -> field::Iter {
|
||||
self.fields.field_set().iter()
|
||||
}
|
||||
|
||||
/// Returns [metadata] describing this `Event`.
|
||||
///
|
||||
/// [metadata]: ::metadata::Metadata
|
||||
pub fn metadata(&self) -> &Metadata {
|
||||
self.metadata
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,740 @@
|
||||
//! `Span` and `Event` key-value data.
|
||||
//!
|
||||
//! Spans and events may be annotated with key-value data, referred to as known
|
||||
//! as _fields_. These fields consist of a mapping from a key (corresponding to
|
||||
//! a `&str` but represented internally as an array index) to a `Value`.
|
||||
//!
|
||||
//! # `Value`s and `Subscriber`s
|
||||
//!
|
||||
//! `Subscriber`s consume `Value`s as fields attached to `Span`s or `Event`s.
|
||||
//! The set of field keys on a given `Span` or is defined on its `Metadata`.
|
||||
//! When a `Span` is created, it provides a `ValueSet` to the `Subscriber`'s
|
||||
//! [`new_span`] method, containing any fields whose values were provided when
|
||||
//! the span was created; and may call the `Subscriber`'s [`record`] method
|
||||
//! with additional `ValueSet`s if values are added for more of its fields.
|
||||
//! Similarly, the [`Event`] type passed to the subscriber's [`event`] method
|
||||
//! will contain any fields attached to each event.
|
||||
//!
|
||||
//! `tokio_trace` represents values as either one of a set of Rust primitives
|
||||
//! (`i64`, `u64`, `bool`, and `&str`) or using a `fmt::Display` or `fmt::Debug`
|
||||
//! implementation. The `record_` trait functions on the `Subscriber` trait
|
||||
//! allow `Subscriber` implementations to provide type-specific behaviour for
|
||||
//! consuming values of each type.
|
||||
//!
|
||||
//! Instances of the `Record` trait are provided by `Subscriber`s to record the
|
||||
//! values attached to `Span`s and `Event`. This trait represents the behavior
|
||||
//! used to record values of various types. For example, we might record
|
||||
//! integers by incrementing counters for their field names, rather than printing
|
||||
//! them.
|
||||
//!
|
||||
//! [`new_span`]: ::subscriber::Subscriber::new_span
|
||||
//! [`record`]: ::subscriber::Subscriber::record
|
||||
//! [`Event`]: ::event::Event
|
||||
//! [`event`]: ::subscriber::Subscriber::event
|
||||
use callsite;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
/// An opaque key allowing _O_(1) access to a field in a `Span`'s key-value
|
||||
/// data.
|
||||
///
|
||||
/// As keys are defined by the _metadata_ of a span, rather than by an
|
||||
/// individual instance of a span, a key may be used to access the same field
|
||||
/// across all instances of a given span with the same metadata. Thus, when a
|
||||
/// subscriber observes a new span, it need only access a field by name _once_,
|
||||
/// and use the key for that name for all other accesses.
|
||||
#[derive(Debug)]
|
||||
pub struct Field {
|
||||
i: usize,
|
||||
fields: FieldSet,
|
||||
}
|
||||
|
||||
/// Describes the fields present on a span.
|
||||
// TODO: When `const fn` is stable, make this type's fields private.
|
||||
pub struct FieldSet {
|
||||
/// The names of each field on 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 `FieldSet`!
|
||||
#[doc(hidden)]
|
||||
pub names: &'static [&'static str],
|
||||
/// The callsite where the described span originates.
|
||||
///
|
||||
/// **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 `FieldSet`!
|
||||
#[doc(hidden)]
|
||||
pub callsite: callsite::Identifier,
|
||||
}
|
||||
|
||||
/// A set of fields and values for a span.
|
||||
pub struct ValueSet<'a> {
|
||||
values: &'a [(&'a Field, Option<&'a (Value + 'a)>)],
|
||||
fields: &'a FieldSet,
|
||||
}
|
||||
|
||||
/// An iterator over a set of fields.
|
||||
#[derive(Debug)]
|
||||
pub struct Iter {
|
||||
idxs: Range<usize>,
|
||||
fields: FieldSet,
|
||||
}
|
||||
|
||||
/// Records typed values.
|
||||
///
|
||||
/// An instance of `Record` ("a recorder") represents the logic necessary to
|
||||
/// record field values of various types. When an implementor of [`Value`] is
|
||||
/// [recorded], it calls the appropriate method on the provided recorder to
|
||||
/// indicate the type that value should be recorded as.
|
||||
///
|
||||
/// When a [`Subscriber`] implementation [records an `Event`] or a
|
||||
/// [set of `Value`s added to a `Span`], it can pass an `&mut Record` to the
|
||||
/// `record` method on the provided [`ValueSet`] or [`Event`]. This recorder
|
||||
/// will then be used to record all the field-value pairs present on that
|
||||
/// `Event` or `ValueSet`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A simple recorder that writes to a string might be implemented like so:
|
||||
/// ```
|
||||
/// # extern crate tokio_trace_core as tokio_trace;
|
||||
/// use std::fmt::{self, Write};
|
||||
/// use tokio_trace::field::{Value, Record, Field};
|
||||
/// # fn main() {
|
||||
/// pub struct StringRecorder<'a> {
|
||||
/// string: &'a mut String,
|
||||
/// }
|
||||
///
|
||||
/// impl<'a> Record for StringRecorder<'a> {
|
||||
/// fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
|
||||
/// write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
|
||||
/// }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
/// This recorder will format each recorded value using `fmt::Debug`, and
|
||||
/// append the field name and formatted value to the provided string,
|
||||
/// regardless of the type of the recorded value. When all the values have
|
||||
/// been recorded, the `StringRecorder` may be dropped, allowing the string
|
||||
/// to be printed or stored in some other data structure.
|
||||
///
|
||||
/// The `Record` trait provides default implementations for `record_i64`,
|
||||
/// `record_u64`, `record_bool`, and `record_str` which simply forward the
|
||||
/// recorded value to `record_debug`. Thus, `record_debug` is the only method
|
||||
/// which a `Record` implementation *must* implement. However, recorders may
|
||||
/// override the default implementations of these functions in order to
|
||||
/// implement type-specific behavior.
|
||||
///
|
||||
/// Additionally, when a recorder recieves a value of a type it does not care
|
||||
/// about, it is free to ignore those values completely. For example, a
|
||||
/// recorder which only records numeric data might look like this:
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio_trace_core as tokio_trace;
|
||||
/// # use std::fmt::{self, Write};
|
||||
/// # use tokio_trace::field::{Value, Record, Field};
|
||||
/// # fn main() {
|
||||
/// pub struct SumRecorder {
|
||||
/// sum: i64,
|
||||
/// }
|
||||
///
|
||||
/// impl Record for SumRecorder {
|
||||
/// fn record_i64(&mut self, _field: &Field, value: i64) {
|
||||
/// self.sum += value;
|
||||
/// }
|
||||
///
|
||||
/// fn record_u64(&mut self, _field: &Field, value: u64) {
|
||||
/// self.sum += value as i64;
|
||||
/// }
|
||||
///
|
||||
/// fn record_debug(&mut self, _field: &Field, _value: &fmt::Debug) {
|
||||
/// // Do nothing
|
||||
/// }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// This recorder (which is probably not particularly useful) keeps a running
|
||||
/// sum of all the numeric values it records, and ignores all other values. A
|
||||
/// more practical example of recording typed values is presented in
|
||||
/// `examples/counters.rs`, which demonstrates a very simple metrics system
|
||||
/// implemented using `tokio-trace`.
|
||||
///
|
||||
/// [`Value`]: ::field::Value
|
||||
/// [recorded]: ::field::Value::record
|
||||
/// [`Subscriber`]: ::subscriber::Subscriber
|
||||
/// [records an `Event`]: ::subscriber::Subscriber::event
|
||||
/// [set of `Value`s added to a `Span`]: ::subscriber::Subscriber::record
|
||||
/// [`Event`]: ::event::Event
|
||||
/// [`ValueSet`]: ::field::ValueSet
|
||||
pub trait Record {
|
||||
/// Record a signed 64-bit integer value.
|
||||
fn record_i64(&mut self, field: &Field, value: i64) {
|
||||
self.record_debug(field, &value)
|
||||
}
|
||||
|
||||
/// Record an umsigned 64-bit integer value.
|
||||
fn record_u64(&mut self, field: &Field, value: u64) {
|
||||
self.record_debug(field, &value)
|
||||
}
|
||||
|
||||
/// Record a boolean value.
|
||||
fn record_bool(&mut self, field: &Field, value: bool) {
|
||||
self.record_debug(field, &value)
|
||||
}
|
||||
|
||||
/// Record a string value.
|
||||
fn record_str(&mut self, field: &Field, value: &str) {
|
||||
self.record_debug(field, &value)
|
||||
}
|
||||
|
||||
/// Record a value implementing `fmt::Debug`.
|
||||
fn record_debug(&mut self, field: &Field, value: &fmt::Debug);
|
||||
}
|
||||
|
||||
/// A field value of an erased type.
|
||||
///
|
||||
/// Implementors of `Value` may call the appropriate typed recording methods on
|
||||
/// the [recorder] passed to their `record` method in order to indicate how
|
||||
/// their data should be recorded.
|
||||
///
|
||||
/// [recorder]: ::field::Record
|
||||
pub trait Value: ::sealed::Sealed {
|
||||
/// Records this value with the given `Recorder`.
|
||||
fn record(&self, key: &Field, recorder: &mut Record);
|
||||
}
|
||||
|
||||
/// A `Value` which serializes as a string using `fmt::Display`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DisplayValue<T: fmt::Display>(T);
|
||||
|
||||
/// A `Value` which serializes as a string using `fmt::Debug`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugValue<T: fmt::Debug>(T);
|
||||
|
||||
/// Marker trait implemented by arrays which are of valid length to
|
||||
/// construct a `ValueSet`.
|
||||
///
|
||||
/// `ValueSet`s may only be constructed from arrays containing 32 or fewer
|
||||
/// elements, to ensure the array is small enough to always be allocated on the
|
||||
/// stack. This trait is only implemented by arrays of an appropriate length,
|
||||
/// ensuring that the correct size arrays are used at compile-time.
|
||||
pub trait ValidLen<'a>: ::sealed::Sealed + Borrow<[(&'a Field, Option<&'a (Value + 'a)>)]> {}
|
||||
|
||||
/// Wraps a type implementing `fmt::Display` as a `Value` that can be
|
||||
/// recorded using its `Display` implementation.
|
||||
pub fn display<T>(t: T) -> DisplayValue<T>
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
DisplayValue(t)
|
||||
}
|
||||
|
||||
/// Wraps a type implementing `fmt::Debug` as a `Value` that can be
|
||||
/// recorded using its `Debug` implementation.
|
||||
pub fn debug<T>(t: T) -> DebugValue<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
DebugValue(t)
|
||||
}
|
||||
|
||||
// ===== impl Record =====
|
||||
|
||||
impl<'a, 'b> Record for fmt::DebugStruct<'a, 'b> {
|
||||
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
|
||||
self.field(field.name(), value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Record for fmt::DebugMap<'a, 'b> {
|
||||
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
|
||||
self.entry(&format_args!("{}", field), value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Record for F
|
||||
where
|
||||
F: FnMut(&Field, &fmt::Debug),
|
||||
{
|
||||
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
|
||||
(self)(field, value)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Value =====
|
||||
|
||||
macro_rules! impl_values {
|
||||
( $( $record:ident( $( $whatever:tt)+ ) ),+ ) => {
|
||||
$(
|
||||
impl_value!{ $record( $( $whatever )+ ) }
|
||||
)+
|
||||
}
|
||||
}
|
||||
macro_rules! impl_value {
|
||||
( $record:ident( $( $value_ty:ty ),+ ) ) => {
|
||||
$(
|
||||
impl $crate::sealed::Sealed for $value_ty {}
|
||||
impl $crate::field::Value for $value_ty {
|
||||
fn record(
|
||||
&self,
|
||||
key: &$crate::field::Field,
|
||||
recorder: &mut $crate::field::Record,
|
||||
) {
|
||||
recorder.$record(key, *self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
( $record:ident( $( $value_ty:ty ),+ as $as_ty:ty) ) => {
|
||||
$(
|
||||
impl $crate::sealed::Sealed for $value_ty {}
|
||||
impl Value for $value_ty {
|
||||
fn record(
|
||||
&self,
|
||||
key: &$crate::field::Field,
|
||||
recorder: &mut $crate::field::Record,
|
||||
) {
|
||||
recorder.$record(key, *self as $as_ty)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// ===== impl Value =====
|
||||
|
||||
impl_values! {
|
||||
record_u64(u64),
|
||||
record_u64(usize, u32, u16 as u64),
|
||||
record_i64(i64),
|
||||
record_i64(isize, i32, i16, i8 as i64),
|
||||
record_bool(bool)
|
||||
}
|
||||
|
||||
impl ::sealed::Sealed for str {}
|
||||
|
||||
impl Value for str {
|
||||
fn record(&self, key: &Field, recorder: &mut Record) {
|
||||
recorder.record_str(key, &self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> ::sealed::Sealed for &'a T where T: Value + ::sealed::Sealed + 'a {}
|
||||
|
||||
impl<'a, T: ?Sized> Value for &'a T
|
||||
where
|
||||
T: Value + 'a,
|
||||
{
|
||||
fn record(&self, key: &Field, recorder: &mut Record) {
|
||||
(*self).record(key, recorder)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::sealed::Sealed for fmt::Arguments<'a> {}
|
||||
|
||||
impl<'a> Value for fmt::Arguments<'a> {
|
||||
fn record(&self, key: &Field, recorder: &mut Record) {
|
||||
recorder.record_debug(key, self)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl DisplayValue =====
|
||||
|
||||
impl<T: fmt::Display> ::sealed::Sealed for DisplayValue<T> {}
|
||||
|
||||
impl<T> Value for DisplayValue<T>
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
fn record(&self, key: &Field, recorder: &mut Record) {
|
||||
recorder.record_debug(key, &format_args!("{}", self.0))
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl DebugValue =====
|
||||
|
||||
impl<T: fmt::Debug> ::sealed::Sealed for DebugValue<T> {}
|
||||
|
||||
impl<T: fmt::Debug> Value for DebugValue<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
fn record(&self, key: &Field, recorder: &mut Record) {
|
||||
recorder.record_debug(key, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Field =====
|
||||
|
||||
impl Field {
|
||||
/// Returns an [`Identifier`](::metadata::Identifier) that uniquely
|
||||
/// identifies the callsite that defines the field this key refers to.
|
||||
#[inline]
|
||||
pub fn callsite(&self) -> callsite::Identifier {
|
||||
self.fields.callsite()
|
||||
}
|
||||
|
||||
/// Returns a string representing the name of the field.
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.fields.names[self.i]
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Field {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.pad(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Field {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Field {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.callsite() == other.callsite() && self.i == other.i
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Field {}
|
||||
|
||||
impl Hash for Field {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.callsite().hash(state);
|
||||
self.i.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Field {
|
||||
fn clone(&self) -> Self {
|
||||
Field {
|
||||
i: self.i,
|
||||
fields: FieldSet {
|
||||
names: self.fields.names,
|
||||
callsite: self.fields.callsite(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FieldSet =====
|
||||
|
||||
impl FieldSet {
|
||||
pub(crate) fn callsite(&self) -> callsite::Identifier {
|
||||
callsite::Identifier(self.callsite.0)
|
||||
}
|
||||
|
||||
/// Returns the [`Field`](::field::Field) named `name`, or `None` if no such
|
||||
/// field exists.
|
||||
pub fn field<Q: ?Sized>(&self, name: &Q) -> Option<Field>
|
||||
where
|
||||
Q: Borrow<str>,
|
||||
{
|
||||
let name = &name.borrow();
|
||||
self.names.iter().position(|f| f == name).map(|i| Field {
|
||||
i,
|
||||
fields: FieldSet {
|
||||
names: self.names,
|
||||
callsite: self.callsite(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` contains the given `field`.
|
||||
///
|
||||
/// **Note**: If `field` shares a name with a field in this `FieldSet`, but
|
||||
/// was created by a `FieldSet` with a different callsite, this `FieldSet`
|
||||
/// does _not_ contain it. This is so that if two separate span callsites
|
||||
/// define a field named "foo", the `Field` corresponding to "foo" for each
|
||||
/// of those callsites are not equivalent.
|
||||
pub fn contains(&self, field: &Field) -> bool {
|
||||
field.callsite() == self.callsite() && field.i <= self.len()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the `Field`s in this `FieldSet`.
|
||||
pub fn iter(&self) -> Iter {
|
||||
let idxs = 0..self.len();
|
||||
Iter {
|
||||
idxs,
|
||||
fields: FieldSet {
|
||||
names: self.names,
|
||||
callsite: self.callsite(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `ValueSet` with entries for this `FieldSet`'s values.
|
||||
///
|
||||
/// Note that a `ValueSet` may not be constructed with arrays of over 32
|
||||
/// elements.
|
||||
#[doc(hidden)]
|
||||
pub fn value_set<'v, V>(&'v self, values: &'v V) -> ValueSet<'v>
|
||||
where
|
||||
V: ValidLen<'v>,
|
||||
{
|
||||
ValueSet {
|
||||
fields: self,
|
||||
values: &values.borrow()[..],
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of fields in this `FieldSet`.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.names.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a FieldSet {
|
||||
type IntoIter = Iter;
|
||||
type Item = Field;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FieldSet {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FieldSet")
|
||||
.field("names", &self.names)
|
||||
.field("callsite", &self.callsite)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Iter =====
|
||||
|
||||
impl Iterator for Iter {
|
||||
type Item = Field;
|
||||
fn next(&mut self) -> Option<Field> {
|
||||
let i = self.idxs.next()?;
|
||||
Some(Field {
|
||||
i,
|
||||
fields: FieldSet {
|
||||
names: self.fields.names,
|
||||
callsite: self.fields.callsite(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl ValueSet =====
|
||||
|
||||
impl<'a> ValueSet<'a> {
|
||||
/// Returns an [`Identifier`](::metadata::Identifier) that uniquely
|
||||
/// identifies the callsite that defines the fields this `ValueSet` refers to.
|
||||
#[inline]
|
||||
pub fn callsite(&self) -> callsite::Identifier {
|
||||
self.fields.callsite()
|
||||
}
|
||||
|
||||
/// Records all the fields in this `ValueSet` with the provided [recorder].
|
||||
///
|
||||
/// [recorder]: ::field::Record
|
||||
pub fn record(&self, recorder: &mut Record) {
|
||||
let my_callsite = self.callsite();
|
||||
for (field, value) in self.values {
|
||||
if field.callsite() != my_callsite {
|
||||
continue;
|
||||
}
|
||||
if let Some(value) = value {
|
||||
value.record(field, recorder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this `ValueSet` contains a value for the given `Field`.
|
||||
pub fn contains(&self, field: &Field) -> bool {
|
||||
field.callsite() == self.callsite()
|
||||
&& self
|
||||
.values
|
||||
.iter()
|
||||
.any(|(key, val)| *key == field && val.is_some())
|
||||
}
|
||||
|
||||
/// Returns true if this `ValueSet` contains _no_ values.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let my_callsite = self.callsite();
|
||||
self.values
|
||||
.iter()
|
||||
.all(|(key, val)| val.is_none() || key.callsite() != my_callsite)
|
||||
}
|
||||
|
||||
pub(crate) fn field_set(&self) -> &FieldSet {
|
||||
self.fields
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for ValueSet<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.values
|
||||
.iter()
|
||||
.fold(&mut f.debug_struct("ValueSet"), |dbg, (key, v)| {
|
||||
if let Some(val) = v {
|
||||
val.record(key, dbg);
|
||||
}
|
||||
dbg
|
||||
})
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl ValidLen =====
|
||||
|
||||
macro_rules! impl_valid_len {
|
||||
( $( $len:tt ),+ ) => {
|
||||
$(
|
||||
impl<'a> ::sealed::Sealed for
|
||||
[(&'a Field, Option<&'a (Value + 'a)>); $len] {}
|
||||
impl<'a> ValidLen<'a> for
|
||||
[(&'a Field, Option<&'a (Value + 'a)>); $len] {}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
impl_valid_len! {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use {Level, Metadata};
|
||||
|
||||
struct TestCallsite1;
|
||||
static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1;
|
||||
static TEST_META_1: Metadata<'static> = metadata! {
|
||||
name: "field_test1",
|
||||
target: module_path!(),
|
||||
level: Level::INFO,
|
||||
fields: &["foo", "bar", "baz"],
|
||||
callsite: &TEST_CALLSITE_1,
|
||||
};
|
||||
|
||||
impl ::callsite::Callsite for TestCallsite1 {
|
||||
fn add_interest(&self, _: ::subscriber::Interest) {}
|
||||
fn clear_interest(&self) {}
|
||||
|
||||
fn metadata(&self) -> &Metadata {
|
||||
&TEST_META_1
|
||||
}
|
||||
}
|
||||
|
||||
struct TestCallsite2;
|
||||
static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2;
|
||||
static TEST_META_2: Metadata<'static> = metadata! {
|
||||
name: "field_test2",
|
||||
target: module_path!(),
|
||||
level: Level::INFO,
|
||||
fields: &["foo", "bar", "baz"],
|
||||
callsite: &TEST_CALLSITE_2,
|
||||
};
|
||||
|
||||
impl ::callsite::Callsite for TestCallsite2 {
|
||||
fn add_interest(&self, _: ::subscriber::Interest) {}
|
||||
fn clear_interest(&self) {}
|
||||
|
||||
fn metadata(&self) -> &Metadata {
|
||||
&TEST_META_2
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_set_with_no_values_is_empty() {
|
||||
let fields = TEST_META_1.fields();
|
||||
let values = &[
|
||||
(&fields.field("foo").unwrap(), None),
|
||||
(&fields.field("bar").unwrap(), None),
|
||||
(&fields.field("baz").unwrap(), None),
|
||||
];
|
||||
let valueset = fields.value_set(values);
|
||||
assert!(valueset.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_value_set_is_empty() {
|
||||
let fields = TEST_META_1.fields();
|
||||
let valueset = fields.value_set(&[]);
|
||||
assert!(valueset.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_sets_with_fields_from_other_callsites_are_empty() {
|
||||
let fields = TEST_META_1.fields();
|
||||
let values = &[
|
||||
(&fields.field("foo").unwrap(), Some(&1 as &Value)),
|
||||
(&fields.field("bar").unwrap(), Some(&2 as &Value)),
|
||||
(&fields.field("baz").unwrap(), Some(&3 as &Value)),
|
||||
];
|
||||
let valueset = TEST_META_2.fields().value_set(values);
|
||||
assert!(valueset.is_empty())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sparse_value_sets_are_not_empty() {
|
||||
let fields = TEST_META_1.fields();
|
||||
let values = &[
|
||||
(&fields.field("foo").unwrap(), None),
|
||||
(&fields.field("bar").unwrap(), Some(&57 as &Value)),
|
||||
(&fields.field("baz").unwrap(), None),
|
||||
];
|
||||
let valueset = fields.value_set(values);
|
||||
assert!(!valueset.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fields_from_other_callsets_are_skipped() {
|
||||
let fields = TEST_META_1.fields();
|
||||
let values = &[
|
||||
(&fields.field("foo").unwrap(), None),
|
||||
(
|
||||
&TEST_META_2.fields().field("bar").unwrap(),
|
||||
Some(&57 as &Value),
|
||||
),
|
||||
(&fields.field("baz").unwrap(), None),
|
||||
];
|
||||
|
||||
struct MyRecorder;
|
||||
impl Record for MyRecorder {
|
||||
fn record_debug(&mut self, field: &Field, _: &::std::fmt::Debug) {
|
||||
assert_eq!(field.callsite(), TEST_META_1.callsite())
|
||||
}
|
||||
}
|
||||
let valueset = fields.value_set(values);
|
||||
valueset.record(&mut MyRecorder);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_debug_fn() {
|
||||
let fields = TEST_META_1.fields();
|
||||
let values = &[
|
||||
(&fields.field("foo").unwrap(), Some(&1 as &Value)),
|
||||
(&fields.field("bar").unwrap(), Some(&2 as &Value)),
|
||||
(&fields.field("baz").unwrap(), Some(&3 as &Value)),
|
||||
];
|
||||
let valueset = fields.value_set(values);
|
||||
let mut result = String::new();
|
||||
valueset.record(&mut |_: &Field, value: &fmt::Debug| {
|
||||
use std::fmt::Write;
|
||||
write!(&mut result, "{:?}", value).unwrap();
|
||||
});
|
||||
assert_eq!(result, "123".to_owned());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Core primitives for `tokio-trace`.
|
||||
//!
|
||||
//! `tokio-trace` is a framework for instrumenting Rust programs to collect
|
||||
//! structured, event-based diagnostic information. This crate defines the core
|
||||
//! primitives of `tokio-trace`.
|
||||
//!
|
||||
//! The crate provides:
|
||||
//!
|
||||
//! * [`Span`] identifies a span within the execution of a program.
|
||||
//!
|
||||
//! * [`Subscriber`], the trait implemented to collect trace data.
|
||||
//!
|
||||
//! * [`Metadata`] and [`Callsite`] provide information describing `Span`s.
|
||||
//!
|
||||
//! * [`Field`] and [`FieldSet`] describe and access the structured data attached to
|
||||
//! a `Span`.
|
||||
//!
|
||||
//! * [`Dispatch`] allows span events to be dispatched to `Subscriber`s.
|
||||
//!
|
||||
//! In addition, it defines the global callsite registry and per-thread current
|
||||
//! dispatcher which other components of the tracing system rely on.
|
||||
//!
|
||||
//! Application authors will typically not use this crate directly. Instead,
|
||||
//! they will use the `tokio-trace` crate, which provides a much more
|
||||
//! fully-featured API. However, this crate's API will change very infrequently,
|
||||
//! so it may be used when dependencies must be very stable.
|
||||
//!
|
||||
//! [`Span`]: ::span::Span
|
||||
//! [`Subscriber`]: ::subscriber::Subscriber
|
||||
//! [`Metadata`]: ::metadata::Metadata
|
||||
//! [`Callsite`]: ::callsite::Callsite
|
||||
//! [`Field`]: ::field::Field
|
||||
//! [`FieldSet`]: ::field::FieldSet
|
||||
//! [`Dispatch`]: ::dispatcher::Dispatch
|
||||
//!
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
/// Statically constructs an [`Identifier`] for the provided [`Callsite`].
|
||||
///
|
||||
/// This may be used in contexts, such as static initializers, where the
|
||||
/// [`Callsite::id`] function is not currently usable.
|
||||
///
|
||||
/// For example:
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate tokio_trace_core;
|
||||
/// use tokio_trace_core::callsite;
|
||||
/// # use tokio_trace_core::{Metadata, subscriber::Interest};
|
||||
/// # fn main() {
|
||||
/// pub struct MyCallsite {
|
||||
/// // ...
|
||||
/// }
|
||||
/// impl callsite::Callsite for MyCallsite {
|
||||
/// # fn add_interest(&self, _: Interest) { unimplemented!() }
|
||||
/// # fn clear_interest(&self) {}
|
||||
/// # fn metadata(&self) -> &Metadata { unimplemented!() }
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// static CALLSITE: MyCallsite = MyCallsite {
|
||||
/// // ...
|
||||
/// };
|
||||
///
|
||||
/// static CALLSITE_ID: callsite::Identifier = identify_callsite!(&CALLSITE);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`Identifier`]: ::callsite::Identifier
|
||||
/// [`Callsite`]: ::callsite::Callsite
|
||||
/// [`Callsite::id`]: ::callsite::Callsite::id
|
||||
#[macro_export]
|
||||
macro_rules! identify_callsite {
|
||||
($callsite:expr) => {
|
||||
$crate::callsite::Identifier($callsite)
|
||||
};
|
||||
}
|
||||
|
||||
/// Statically constructs new span [metadata].
|
||||
///
|
||||
/// This may be used in contexts, such as static initializers, where the
|
||||
/// [`Metadata::new`] function is not currently usable.
|
||||
///
|
||||
/// /// For example:
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate tokio_trace_core;
|
||||
/// # use tokio_trace_core::{callsite::Callsite, subscriber::Interest};
|
||||
/// use tokio_trace_core::{Metadata, Level};
|
||||
/// # fn main() {
|
||||
/// # pub struct MyCallsite { }
|
||||
/// # impl Callsite for MyCallsite {
|
||||
/// # fn add_interest(&self, _: Interest) { unimplemented!() }
|
||||
/// # fn clear_interest(&self) {}
|
||||
/// # fn metadata(&self) -> &Metadata { unimplemented!() }
|
||||
/// # }
|
||||
/// #
|
||||
/// static FOO_CALLSITE: MyCallsite = MyCallsite {
|
||||
/// // ...
|
||||
/// };
|
||||
///
|
||||
/// static FOO_METADATA: Metadata = metadata!{
|
||||
/// name: "foo",
|
||||
/// target: module_path!(),
|
||||
/// level: Level::DEBUG,
|
||||
/// fields: &["bar", "baz"],
|
||||
/// callsite: &FOO_CALLSITE,
|
||||
/// };
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [metadata]: ::metadata::Metadata
|
||||
/// [`Metadata::new`]: ::metadata::Metadata::new
|
||||
#[macro_export]
|
||||
macro_rules! metadata {
|
||||
(
|
||||
name: $name:expr,
|
||||
target: $target:expr,
|
||||
level: $level:expr,
|
||||
fields: $fields:expr,
|
||||
callsite: $callsite:expr
|
||||
) => {
|
||||
metadata! {
|
||||
name: $name,
|
||||
target: $target,
|
||||
level: $level,
|
||||
fields: $fields,
|
||||
callsite: $callsite,
|
||||
}
|
||||
};
|
||||
(
|
||||
name: $name:expr,
|
||||
target: $target:expr,
|
||||
level: $level:expr,
|
||||
fields: $fields:expr,
|
||||
callsite: $callsite:expr,
|
||||
) => {
|
||||
$crate::metadata::Metadata {
|
||||
name: $name,
|
||||
target: $target,
|
||||
level: $level,
|
||||
file: Some(file!()),
|
||||
line: Some(line!()),
|
||||
module_path: Some(module_path!()),
|
||||
fields: $crate::field::FieldSet {
|
||||
names: $fields,
|
||||
callsite: identify_callsite!($callsite),
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod callsite;
|
||||
pub mod dispatcher;
|
||||
pub mod event;
|
||||
pub mod field;
|
||||
pub mod metadata;
|
||||
pub mod span;
|
||||
pub mod subscriber;
|
||||
|
||||
pub use self::{
|
||||
callsite::Callsite,
|
||||
dispatcher::Dispatch,
|
||||
event::Event,
|
||||
field::Field,
|
||||
metadata::{Level, Metadata},
|
||||
span::Span,
|
||||
subscriber::{Interest, Subscriber},
|
||||
};
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
//! Metadata describing trace data.
|
||||
use super::{
|
||||
callsite::{self, Callsite},
|
||||
field,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
/// Metadata describing a [`Span`].
|
||||
///
|
||||
/// 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::Span
|
||||
/// [`Subscriber`]: ::Subscriber
|
||||
/// [`Metadata::id()`]: ::metadata::Metadata::id
|
||||
/// [callsite identifier]: ::callsite::Identifier
|
||||
// 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,
|
||||
}
|
||||
|
||||
/// Describes the level of verbosity of a `Span`.
|
||||
#[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,
|
||||
) -> Self {
|
||||
Metadata {
|
||||
name,
|
||||
target,
|
||||
level,
|
||||
module_path,
|
||||
file,
|
||||
line,
|
||||
fields: field::FieldSet {
|
||||
names: field_names,
|
||||
callsite: callsite::Identifier(callsite),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for Metadata<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Metadata")
|
||||
.field("name", &self.name)
|
||||
.field("target", &self.target)
|
||||
.field("level", &self.level)
|
||||
.field("module_path", &self.module_path)
|
||||
.field("file", &self.file)
|
||||
.field("line", &self.line)
|
||||
.field("field_names", &self.fields)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 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,
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//! Spans represent periods of time in the execution of a program.
|
||||
|
||||
/// Identifies a span within the context of a process.
|
||||
///
|
||||
/// Span IDs are used primarily to determine of two handles refer to the same
|
||||
/// span, without requiring the comparison of the span's fields.
|
||||
///
|
||||
/// They are generated by [`Subscriber`](::Subscriber)s for each span as it is
|
||||
/// created, through the [`new_id`](::Subscriber::new_span_id) trait
|
||||
/// method. See the documentation for that method for more information on span
|
||||
/// ID generation.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Span(u64);
|
||||
|
||||
// ===== impl Id =====
|
||||
|
||||
impl Span {
|
||||
/// Constructs a new span ID from the given `u64`.
|
||||
pub fn from_u64(u: u64) -> Self {
|
||||
Span(u)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
//! Subscribers collect and record trace data.
|
||||
use {field, Event, Metadata, Span};
|
||||
|
||||
/// Trait representing the functions required to collect trace data.
|
||||
///
|
||||
/// Crates that provide implementations of methods for collecting or recording
|
||||
/// trace data should implement the `Subscriber` interface. This trait is
|
||||
/// intended to represent fundamental primitives for collecting trace events and
|
||||
/// spans — other libraries may offer utility functions and types to make
|
||||
/// subscriber implementations more modular or improve the ergonomics of writing
|
||||
/// subscribers.
|
||||
///
|
||||
/// A subscriber is responsible for the following:
|
||||
/// - Registering new spans as they are created, and providing them with span
|
||||
/// IDs. Implicitly, this means the subscriber may determine the strategy for
|
||||
/// determining span equality.
|
||||
/// - Recording the attachment of field values and follows-from annotations to
|
||||
/// spans.
|
||||
/// - Filtering spans and events, and determining when those filters must be
|
||||
/// invalidated.
|
||||
/// - Observing spans as they are entered, exited, and closed, and events as
|
||||
/// they occur.
|
||||
///
|
||||
/// When a span is entered or exited, the subscriber is provided only with the
|
||||
/// [ID] with which it tagged that span when it was created. This means
|
||||
/// that it is up to the subscriber to determine whether and how span _data_ —
|
||||
/// the fields and metadata describing the span — should be stored. The
|
||||
/// [`new_span`] function is called when a new span is created, and at that
|
||||
/// point, the subscriber _may_ choose to store the associated data if it will
|
||||
/// be referenced again. However, if the data has already been recorded and will
|
||||
/// not be needed by the implementations of `enter` and `exit`, the subscriber
|
||||
/// may freely discard that data without allocating space to store it.
|
||||
///
|
||||
/// [ID]: ::span::Span
|
||||
/// [`new_span`]: ::Span::new_span
|
||||
pub trait Subscriber {
|
||||
// === Span registry methods ==============================================
|
||||
|
||||
/// Registers a new callsite with this subscriber, returning whether or not
|
||||
/// the subscriber is interested in being notified about the callsite.
|
||||
///
|
||||
/// By default, this function assumes that the subscriber's filter
|
||||
/// represents an unchanging view of its interest in the callsite. However,
|
||||
/// if this is not the case, subscribers may override this function to
|
||||
/// indicate different interests, or to implement behaviour that should run
|
||||
/// once for every callsite.
|
||||
///
|
||||
/// This function is guaranteed to be called exactly once per callsite on
|
||||
/// every active subscriber. The subscriber may store the keys to fields it
|
||||
/// cares in order to reduce the cost of accessing fields by name,
|
||||
/// preallocate storage for that callsite, or perform any other actions it
|
||||
/// wishes to perform once for each callsite.
|
||||
///
|
||||
/// The subscriber should then return an [`Interest`](Interest), indicating
|
||||
/// whether it is interested in being notified about that callsite in the
|
||||
/// future. This may be `Always` indicating that the subscriber always
|
||||
/// wishes to be notified about the callsite, and its filter need not be
|
||||
/// re-evaluated; `Sometimes`, indicating that the subscriber may sometimes
|
||||
/// care about the callsite but not always (such as when sampling), or
|
||||
/// `Never`, indicating that the subscriber never wishes to be notified about
|
||||
/// that callsite. If all active subscribers return `Never`, a callsite will
|
||||
/// never be enabled unless a new subscriber expresses interest in it.
|
||||
///
|
||||
/// `Subscriber`s which require their filters to be run every time an event
|
||||
/// occurs or a span is entered/exited should return `Interest::Sometimes`.
|
||||
///
|
||||
/// For example, suppose a sampling subscriber is implemented by
|
||||
/// incrementing a counter every time `enabled` is called and only returning
|
||||
/// `true` when the counter is divisible by a specified sampling rate. If
|
||||
/// that subscriber returns `Interest::Always` from `register_callsite`, then
|
||||
/// the filter will not be re-evaluated once it has been applied to a given
|
||||
/// set of metadata. Thus, the counter will not be incremented, and the span
|
||||
/// or event that correspands to the metadata will never be `enabled`.
|
||||
///
|
||||
/// Similarly, if a `Subscriber` has a filtering strategy that can be
|
||||
/// changed dynamically at runtime, it would need to re-evaluate that filter
|
||||
/// if the cached results have changed.
|
||||
///
|
||||
/// A subscriber which manages fanout to multiple other subscribers
|
||||
/// should proxy this decision to all of its child subscribers,
|
||||
/// returning `Interest::Never` only if _all_ such children return
|
||||
/// `Interest::Never`. If the set of subscribers to which spans are
|
||||
/// broadcast may change dynamically, the subscriber should also never
|
||||
/// return `Interest::Never`, as a new subscriber may be added that _is_
|
||||
/// interested.
|
||||
///
|
||||
/// **Note**: If a subscriber returns `Interest::Never` for a particular
|
||||
/// callsite, it _may_ still see spans and events originating from that
|
||||
/// callsite, if another subscriber expressed interest in it.
|
||||
///
|
||||
/// [metadata]: ::Metadata
|
||||
/// [`enabled`]: ::Subscriber::enabled
|
||||
fn register_callsite(&self, metadata: &Metadata) -> Interest {
|
||||
match self.enabled(metadata) {
|
||||
true => Interest::always(),
|
||||
false => Interest::never(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if a span with the specified [metadata] would be
|
||||
/// recorded.
|
||||
///
|
||||
/// This is used by the dispatcher to avoid allocating for span construction
|
||||
/// if the span would be discarded anyway.
|
||||
///
|
||||
/// [metadata]: ::Metadata
|
||||
fn enabled(&self, metadata: &Metadata) -> bool;
|
||||
|
||||
/// Record the construction of a new [`Span`], returning a new ID for the
|
||||
/// span being constructed.
|
||||
///
|
||||
/// The provided `ValueSet` contains any field values that were provided
|
||||
/// when the span was created. The subscriber may pass a [recorder] to the
|
||||
/// `ValueSet`'s [`record` method] to record these values.
|
||||
///
|
||||
/// IDs are used to uniquely identify spans and events within the context of a
|
||||
/// subscriber, so span equality will be based on the returned ID. Thus, if
|
||||
/// the subscriber wishes for all spans with the same metadata to be
|
||||
/// considered equal, it should return the same ID every time it is given a
|
||||
/// particular set of metadata. Similarly, if it wishes for two separate
|
||||
/// instances of a span with the same metadata to *not* be equal, it should
|
||||
/// return a distinct ID every time this function is called, regardless of
|
||||
/// the metadata.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
/// [recorder]: ::field::Record
|
||||
/// [`record` method]: ::field::ValueSet::record
|
||||
fn new_span(&self, metadata: &Metadata, values: &field::ValueSet) -> Span;
|
||||
|
||||
// === Notification methods ===============================================
|
||||
|
||||
/// Record a set of values on a span.
|
||||
///
|
||||
/// The subscriber is expected to provide a [recorder] to the `ValueSet`'s
|
||||
/// [`record` method] in order to record the added values.
|
||||
///
|
||||
/// [recorder]: ::field::Record
|
||||
/// [`record` method]: ::field::ValueSet::record
|
||||
fn record(&self, span: &Span, values: &field::ValueSet);
|
||||
|
||||
/// Adds an indication that `span` follows from the span with the id
|
||||
/// `follows`.
|
||||
///
|
||||
/// This relationship differs somewhat from the parent-child relationship: a
|
||||
/// span may have any number of prior spans, rather than a single one; and
|
||||
/// spans are not considered to be executing _inside_ of the spans they
|
||||
/// follow from. This means that a span may close even if subsequent spans
|
||||
/// that follow from it are still open, and time spent inside of a
|
||||
/// subsequent span should not be included in the time its precedents were
|
||||
/// executing. This is used to model causal relationships such as when a
|
||||
/// single future spawns several related background tasks, et cetera.
|
||||
///
|
||||
/// If the subscriber has spans corresponding to the given IDs, it should
|
||||
/// record this relationship in whatever way it deems necessary. Otherwise,
|
||||
/// if one or both of the given span IDs do not correspond to spans that the
|
||||
/// subscriber knows about, or if a cyclical relationship would be created
|
||||
/// (i.e., some span _a_ which proceeds some other span _b_ may not also
|
||||
/// follow from _b_), it may silently do nothing.
|
||||
fn record_follows_from(&self, span: &Span, follows: &Span);
|
||||
|
||||
/// Records that an [`Event`] has occurred.
|
||||
///
|
||||
/// The provided `Event` struct contains any field values attached to the
|
||||
/// event. The subscriber may pass a [recorder] to the `Event`'s
|
||||
/// [`record` method] to record these values.
|
||||
///
|
||||
/// [`Event`]: ::event::Event
|
||||
/// [recorder]: ::field::Record
|
||||
/// [`record` method]: ::event::Event::record
|
||||
fn event(&self, event: &Event);
|
||||
|
||||
/// Records that a [`Span`] has been entered.
|
||||
///
|
||||
/// When entering a span, this method is called to notify the subscriber
|
||||
/// that the span has been entered. The subscriber is provided with the ID
|
||||
/// of the entered span, and should update any internal state tracking the
|
||||
/// current span accordingly.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
fn enter(&self, span: &Span);
|
||||
|
||||
/// Records that a [`Span`] has been exited.
|
||||
///
|
||||
/// When entering a span, this method is called to notify the subscriber
|
||||
/// that the span has been exited. The subscriber is provided with the ID
|
||||
/// of the exited span, and should update any internal state tracking the
|
||||
/// current span accordingly.
|
||||
///
|
||||
/// Exiting a span does not imply that the span will not be re-entered.
|
||||
///
|
||||
/// [`Span`]: ::span::Span
|
||||
fn exit(&self, span: &Span);
|
||||
|
||||
/// Notifies the subscriber that a [`Span`] has been cloned.
|
||||
///
|
||||
/// This function is guaranteed to only be called with span IDs that were
|
||||
/// returned by this subscriber's `new_span` function.
|
||||
///
|
||||
/// Note that the default implementation of this function this is just the
|
||||
/// identity function, passing through the identifier. However, it can be
|
||||
/// used in conjunction with [`drop_span`] to track the number of handles
|
||||
/// capable of `enter`ing a span. When all the handles have been dropped
|
||||
/// (i.e., `drop_span` has been called one more time than `clone_span` for a
|
||||
/// given ID), the subscriber may assume that the span will not be entered
|
||||
/// again. It is then free to deallocate storage for data associated with
|
||||
/// that span, write data from that span to IO, and so on.
|
||||
///
|
||||
/// For more unsafe situations, however, if `id` is itself a pointer of some
|
||||
/// kind this can be used as a hook to "clone" the pointer, depending on
|
||||
/// what that means for the specified pointer.
|
||||
///
|
||||
/// [`Span`]: ::span::Span,
|
||||
/// [`drop_span`]: ::subscriber::Subscriber::drop_span
|
||||
fn clone_span(&self, id: &Span) -> Span {
|
||||
id.clone()
|
||||
}
|
||||
|
||||
/// Notifies the subscriber that a [`Span`] has been dropped.
|
||||
///
|
||||
/// This function is guaranteed to only be called with span IDs that were
|
||||
/// returned by this subscriber's `new_span` function.
|
||||
///
|
||||
/// It's guaranteed that if this function has been called once more than the
|
||||
/// number of times `clone_span` was called with the same `id`, then no more
|
||||
/// `Span`s using that `id` exist. This means that it can be used in
|
||||
/// conjunction with [`clone_span`] to track the number of handles
|
||||
/// capable of `enter`ing a span. When all the handles have been dropped
|
||||
/// (i.e., `drop_span` has been called one more time than `clone_span` for a
|
||||
/// given ID), the subscriber may assume that the span will not be entered
|
||||
/// again. It is then free to deallocate storage for data associated with
|
||||
/// that span, write data from that span to IO, and so on.
|
||||
///
|
||||
/// **Note**: since this function is called when spans are dropped,
|
||||
/// implementations should ensure that they are unwind-safe. Panicking from
|
||||
/// inside of a `drop_span` function may cause a double panic, if the span
|
||||
/// was dropped due to a thread unwinding.
|
||||
///
|
||||
/// [`Span`]: ::span::Span,
|
||||
/// [`drop_span`]: ::subscriber::Subscriber::drop_span
|
||||
fn drop_span(&self, id: Span) {
|
||||
let _ = id;
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates a `Subscriber`'s interest in a particular callsite.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Interest(InterestKind);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
enum InterestKind {
|
||||
Never = 0,
|
||||
Sometimes = 1,
|
||||
Always = 2,
|
||||
}
|
||||
|
||||
impl Interest {
|
||||
/// Returns an `Interest` indicating that the subscriber is never interested
|
||||
/// in being notified about a callsite.
|
||||
///
|
||||
/// If all active subscribers are `never()` interested in a callsite, it will
|
||||
/// be completely disabled unless a new subscriber becomes active.
|
||||
#[inline]
|
||||
pub fn never() -> Self {
|
||||
Interest(InterestKind::Never)
|
||||
}
|
||||
|
||||
/// Returns an `Interest` indicating the subscriber is sometimes interested
|
||||
/// in being notified about a callsite.
|
||||
///
|
||||
/// If all active subscribers are `sometimes` or `never` interested in a
|
||||
/// callsite, the currently active subscriber will be asked to filter that
|
||||
/// callsite every time it creates a span. This will be the case until a
|
||||
/// subscriber expresses that it is `always` interested in the callsite.
|
||||
#[inline]
|
||||
pub fn sometimes() -> Self {
|
||||
Interest(InterestKind::Sometimes)
|
||||
}
|
||||
|
||||
/// Returns an `Interest` indicating the subscriber is always interested in
|
||||
/// being notified about a callsite.
|
||||
///
|
||||
/// If any subscriber expresses that it is `always()` interested in a given
|
||||
/// callsite, then the callsite will always be enabled.
|
||||
#[inline]
|
||||
pub fn always() -> Self {
|
||||
Interest(InterestKind::Always)
|
||||
}
|
||||
|
||||
/// Returns `true` if the subscriber is never interested in being notified
|
||||
/// about this callsite.
|
||||
#[inline]
|
||||
pub fn is_never(&self) -> bool {
|
||||
match self.0 {
|
||||
InterestKind::Never => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the subscriber is sometimes interested in being notified
|
||||
/// about this callsite.
|
||||
#[inline]
|
||||
pub fn is_sometimes(&self) -> bool {
|
||||
match self.0 {
|
||||
InterestKind::Sometimes => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the subscriber is always interested in being notified
|
||||
/// about this callsite.
|
||||
#[inline]
|
||||
pub fn is_always(&self) -> bool {
|
||||
match self.0 {
|
||||
InterestKind::Always => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user