897 lines
30 KiB
Rust
897 lines
30 KiB
Rust
//! Dispatches trace events to [`Subscriber`]s.
|
|
//!
|
|
//! The _dispatcher_ is the component of the tracing system which is responsible
|
|
//! for forwarding trace data from the instrumentation points that generate it
|
|
//! to the subscriber that collects it.
|
|
//!
|
|
//! # Using the Trace Dispatcher
|
|
//!
|
|
//! Every thread in a program using `tracing` has a _default subscriber_. When
|
|
//! events occur, or spans are created, they are dispatched to the thread's
|
|
//! current subscriber.
|
|
//!
|
|
//! ## Setting the Default Subscriber
|
|
//!
|
|
//! By default, the current subscriber is an empty implementation that does
|
|
//! nothing. To use a subscriber implementation, it must be set as the default.
|
|
//! There are two methods for doing so: [`with_default`] and
|
|
//! [`set_global_default`]. `with_default` sets the default subscriber for the
|
|
//! duration of a scope, while `set_global_default` sets a default subscriber
|
|
//! for the entire process.
|
|
//!
|
|
//! To use either of these functions, we must first wrap our subscriber in a
|
|
//! [`Dispatch`], a cloneable, type-erased reference to a subscriber. For
|
|
//! example:
|
|
//! ```rust
|
|
//! # pub struct FooSubscriber;
|
|
//! # use tracing_core::{
|
|
//! # dispatcher, Event, Metadata,
|
|
//! # span::{Attributes, Id, Record}
|
|
//! # };
|
|
//! # impl tracing_core::Subscriber for FooSubscriber {
|
|
//! # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) }
|
|
//! # fn record(&self, _: &Id, _: &Record) {}
|
|
//! # fn event(&self, _: &Event) {}
|
|
//! # fn record_follows_from(&self, _: &Id, _: &Id) {}
|
|
//! # fn enabled(&self, _: &Metadata) -> bool { false }
|
|
//! # fn enter(&self, _: &Id) {}
|
|
//! # fn exit(&self, _: &Id) {}
|
|
//! # }
|
|
//! # impl FooSubscriber { fn new() -> Self { FooSubscriber } }
|
|
//! use dispatcher::Dispatch;
|
|
//!
|
|
//! let my_subscriber = FooSubscriber::new();
|
|
//! let my_dispatch = Dispatch::new(my_subscriber);
|
|
//! ```
|
|
//! Then, we can use [`with_default`] to set our `Dispatch` as the default for
|
|
//! the duration of a block:
|
|
//! ```rust
|
|
//! # pub struct FooSubscriber;
|
|
//! # use tracing_core::{
|
|
//! # dispatcher, Event, Metadata,
|
|
//! # span::{Attributes, Id, Record}
|
|
//! # };
|
|
//! # impl tracing_core::Subscriber for FooSubscriber {
|
|
//! # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) }
|
|
//! # fn record(&self, _: &Id, _: &Record) {}
|
|
//! # fn event(&self, _: &Event) {}
|
|
//! # fn record_follows_from(&self, _: &Id, _: &Id) {}
|
|
//! # fn enabled(&self, _: &Metadata) -> bool { false }
|
|
//! # fn enter(&self, _: &Id) {}
|
|
//! # fn exit(&self, _: &Id) {}
|
|
//! # }
|
|
//! # impl FooSubscriber { fn new() -> Self { FooSubscriber } }
|
|
//! # let my_subscriber = FooSubscriber::new();
|
|
//! # let my_dispatch = dispatcher::Dispatch::new(my_subscriber);
|
|
//! // no default subscriber
|
|
//!
|
|
//! # #[cfg(feature = "std")]
|
|
//! dispatcher::with_default(&my_dispatch, || {
|
|
//! // my_subscriber is the default
|
|
//! });
|
|
//!
|
|
//! // no default subscriber again
|
|
//! ```
|
|
//! It's important to note that `with_default` will not propagate the current
|
|
//! thread's default subscriber to any threads spawned within the `with_default`
|
|
//! block. To propagate the default subscriber to new threads, either use
|
|
//! `with_default` from the new thread, or use `set_global_default`.
|
|
//!
|
|
//! As an alternative to `with_default`, we can use [`set_global_default`] to
|
|
//! set a `Dispatch` as the default for all threads, for the lifetime of the
|
|
//! program. For example:
|
|
//! ```rust
|
|
//! # pub struct FooSubscriber;
|
|
//! # use tracing_core::{
|
|
//! # dispatcher, Event, Metadata,
|
|
//! # span::{Attributes, Id, Record}
|
|
//! # };
|
|
//! # impl tracing_core::Subscriber for FooSubscriber {
|
|
//! # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) }
|
|
//! # fn record(&self, _: &Id, _: &Record) {}
|
|
//! # fn event(&self, _: &Event) {}
|
|
//! # fn record_follows_from(&self, _: &Id, _: &Id) {}
|
|
//! # fn enabled(&self, _: &Metadata) -> bool { false }
|
|
//! # fn enter(&self, _: &Id) {}
|
|
//! # fn exit(&self, _: &Id) {}
|
|
//! # }
|
|
//! # impl FooSubscriber { fn new() -> Self { FooSubscriber } }
|
|
//! # let my_subscriber = FooSubscriber::new();
|
|
//! # let my_dispatch = dispatcher::Dispatch::new(my_subscriber);
|
|
//! // no default subscriber
|
|
//!
|
|
//! dispatcher::set_global_default(my_dispatch)
|
|
//! // `set_global_default` will return an error if the global default
|
|
//! // subscriber has already been set.
|
|
//! .expect("global default was already set!");
|
|
//!
|
|
//! // `my_subscriber` is now the default
|
|
//! ```
|
|
//!
|
|
//! <pre class="ignore" style="white-space:normal;font:inherit;">
|
|
//! <strong>Note</strong>:the thread-local scoped dispatcher
|
|
//! (<a href="#fn.with_default"><code>with_default</code></a>) requires the
|
|
//! Rust standard library. <code>no_std</code> users should use
|
|
//! <a href="#fn.set_global_default"><code>set_global_default</code></a>
|
|
//! instead.
|
|
//! </pre>
|
|
//!
|
|
//! ## Accessing the Default Subscriber
|
|
//!
|
|
//! A thread's current default subscriber can be accessed using the
|
|
//! [`get_default`] function, which executes a closure with a reference to the
|
|
//! currently default `Dispatch`. This is used primarily by `tracing`
|
|
//! instrumentation.
|
|
//!
|
|
use crate::{
|
|
callsite, span,
|
|
subscriber::{self, NoSubscriber, Subscriber},
|
|
Event, LevelFilter, Metadata,
|
|
};
|
|
|
|
use crate::stdlib::{
|
|
any::Any,
|
|
fmt,
|
|
sync::{
|
|
atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
Arc,
|
|
},
|
|
};
|
|
|
|
#[cfg(feature = "std")]
|
|
use crate::stdlib::{
|
|
cell::{Cell, RefCell, RefMut},
|
|
error,
|
|
sync::Weak,
|
|
};
|
|
|
|
/// `Dispatch` trace data to a [`Subscriber`].
|
|
///
|
|
#[derive(Clone)]
|
|
pub struct Dispatch {
|
|
subscriber: Arc<dyn Subscriber + Send + Sync>,
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
thread_local! {
|
|
static CURRENT_STATE: State = State {
|
|
default: RefCell::new(None),
|
|
can_enter: Cell::new(true),
|
|
};
|
|
}
|
|
|
|
static EXISTS: AtomicBool = AtomicBool::new(false);
|
|
static GLOBAL_INIT: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
|
|
|
|
const UNINITIALIZED: usize = 0;
|
|
const INITIALIZING: usize = 1;
|
|
const INITIALIZED: usize = 2;
|
|
|
|
static mut GLOBAL_DISPATCH: Option<Dispatch> = None;
|
|
|
|
/// The dispatch state of a thread.
|
|
#[cfg(feature = "std")]
|
|
struct State {
|
|
/// This thread's current default dispatcher.
|
|
default: RefCell<Option<Dispatch>>,
|
|
/// Whether or not we can currently begin dispatching a trace event.
|
|
///
|
|
/// This is set to `false` when functions such as `enter`, `exit`, `event`,
|
|
/// and `new_span` are called on this thread's default dispatcher, to
|
|
/// prevent further trace events triggered inside those functions from
|
|
/// creating an infinite recursion. When we finish handling a dispatch, this
|
|
/// is set back to `true`.
|
|
can_enter: Cell<bool>,
|
|
}
|
|
|
|
/// While this guard is active, additional calls to subscriber functions on
|
|
/// the default dispatcher will not be able to access the dispatch context.
|
|
/// Dropping the guard will allow the dispatch context to be re-entered.
|
|
#[cfg(feature = "std")]
|
|
struct Entered<'a>(&'a State);
|
|
|
|
/// A guard that resets the current default dispatcher to the prior
|
|
/// default dispatcher when dropped.
|
|
#[cfg(feature = "std")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
|
#[derive(Debug)]
|
|
pub struct DefaultGuard(Option<Dispatch>);
|
|
|
|
/// Sets this dispatch as the default for the duration of a closure.
|
|
///
|
|
/// The default dispatcher is used when creating a new [span] or
|
|
/// [`Event`].
|
|
///
|
|
/// <pre class="ignore" style="white-space:normal;font:inherit;">
|
|
/// <strong>Note</strong>: This function required the Rust standard library.
|
|
/// <code>no_std</code> users should use <a href="../fn.set_global_default.html">
|
|
/// <code>set_global_default</code></a> instead.
|
|
/// </pre>
|
|
///
|
|
/// [span]: super::span
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`Event`]: super::event::Event
|
|
/// [`set_global_default`]: super::set_global_default
|
|
#[cfg(feature = "std")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
|
pub fn with_default<T>(dispatcher: &Dispatch, f: impl FnOnce() -> T) -> T {
|
|
// When this guard is dropped, the default dispatcher will be reset to the
|
|
// prior default. Using this (rather than simply resetting after calling
|
|
// `f`) ensures that we always reset to the prior dispatcher even if `f`
|
|
// panics.
|
|
let _guard = set_default(dispatcher);
|
|
f()
|
|
}
|
|
|
|
/// Sets the dispatch as the default dispatch for the duration of the lifetime
|
|
/// of the returned DefaultGuard
|
|
///
|
|
/// <pre class="ignore" style="white-space:normal;font:inherit;">
|
|
/// <strong>Note</strong>: This function required the Rust standard library.
|
|
/// <code>no_std</code> users should use <a href="../fn.set_global_default.html">
|
|
/// <code>set_global_default</code></a> instead.
|
|
/// </pre>
|
|
///
|
|
/// [`set_global_default`]: super::set_global_default
|
|
#[cfg(feature = "std")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
|
#[must_use = "Dropping the guard unregisters the dispatcher."]
|
|
pub fn set_default(dispatcher: &Dispatch) -> DefaultGuard {
|
|
// When this guard is dropped, the default dispatcher will be reset to the
|
|
// prior default. Using this ensures that we always reset to the prior
|
|
// dispatcher even if the thread calling this function panics.
|
|
State::set_default(dispatcher.clone())
|
|
}
|
|
|
|
/// Sets this dispatch as the global default for the duration of the entire program.
|
|
/// Will be used as a fallback if no thread-local dispatch has been set in a thread
|
|
/// (using `with_default`.)
|
|
///
|
|
/// Can only be set once; subsequent attempts to set the global default will fail.
|
|
/// Returns `Err` if the global default has already been set.
|
|
///
|
|
/// <div class="example-wrap" style="display:inline-block"><pre class="compile_fail" style="white-space:normal;font:inherit;">
|
|
/// <strong>Warning</strong>: In general, libraries should <em>not</em> call
|
|
/// <code>set_global_default()</code>! Doing so will cause conflicts when
|
|
/// executables that depend on the library try to set the default later.
|
|
/// </pre></div>
|
|
///
|
|
/// [span]: super::span
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`Event`]: super::event::Event
|
|
pub fn set_global_default(dispatcher: Dispatch) -> Result<(), SetGlobalDefaultError> {
|
|
// if `compare_exchange` returns Result::Ok(_), then `new` has been set and
|
|
// `current`—now the prior value—has been returned in the `Ok()` branch.
|
|
if GLOBAL_INIT
|
|
.compare_exchange(
|
|
UNINITIALIZED,
|
|
INITIALIZING,
|
|
Ordering::SeqCst,
|
|
Ordering::SeqCst,
|
|
)
|
|
.is_ok()
|
|
{
|
|
unsafe {
|
|
GLOBAL_DISPATCH = Some(dispatcher);
|
|
}
|
|
GLOBAL_INIT.store(INITIALIZED, Ordering::SeqCst);
|
|
EXISTS.store(true, Ordering::Release);
|
|
Ok(())
|
|
} else {
|
|
Err(SetGlobalDefaultError { _no_construct: () })
|
|
}
|
|
}
|
|
|
|
/// Returns true if a `tracing` dispatcher has ever been set.
|
|
///
|
|
/// This may be used to completely elide trace points if tracing is not in use
|
|
/// at all or has yet to be initialized.
|
|
#[doc(hidden)]
|
|
#[inline(always)]
|
|
pub fn has_been_set() -> bool {
|
|
EXISTS.load(Ordering::Relaxed)
|
|
}
|
|
|
|
/// Returned if setting the global dispatcher fails.
|
|
#[derive(Debug)]
|
|
pub struct SetGlobalDefaultError {
|
|
_no_construct: (),
|
|
}
|
|
|
|
impl fmt::Display for SetGlobalDefaultError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.pad("a global default trace dispatcher has already been set")
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
|
impl error::Error for SetGlobalDefaultError {}
|
|
|
|
/// Executes a closure with a reference to this thread's current [dispatcher].
|
|
///
|
|
/// Note that calls to `get_default` should not be nested; if this function is
|
|
/// called while inside of another `get_default`, that closure will be provided
|
|
/// with `Dispatch::none` rather than the previously set dispatcher.
|
|
///
|
|
/// [dispatcher]: super::dispatcher::Dispatch
|
|
#[cfg(feature = "std")]
|
|
pub fn get_default<T, F>(mut f: F) -> T
|
|
where
|
|
F: FnMut(&Dispatch) -> T,
|
|
{
|
|
CURRENT_STATE
|
|
.try_with(|state| {
|
|
if let Some(entered) = state.enter() {
|
|
return f(&*entered.current());
|
|
}
|
|
|
|
f(&Dispatch::none())
|
|
})
|
|
.unwrap_or_else(|_| f(&Dispatch::none()))
|
|
}
|
|
|
|
/// Executes a closure with a reference to this thread's current [dispatcher].
|
|
///
|
|
/// Note that calls to `get_default` should not be nested; if this function is
|
|
/// called while inside of another `get_default`, that closure will be provided
|
|
/// with `Dispatch::none` rather than the previously set dispatcher.
|
|
///
|
|
/// [dispatcher]: super::dispatcher::Dispatch
|
|
#[cfg(feature = "std")]
|
|
#[doc(hidden)]
|
|
#[inline(never)]
|
|
pub fn get_current<T>(f: impl FnOnce(&Dispatch) -> T) -> Option<T> {
|
|
CURRENT_STATE
|
|
.try_with(|state| {
|
|
let entered = state.enter()?;
|
|
Some(f(&*entered.current()))
|
|
})
|
|
.ok()?
|
|
}
|
|
|
|
/// Executes a closure with a reference to the current [dispatcher].
|
|
///
|
|
/// [dispatcher]: super::dispatcher::Dispatch
|
|
#[cfg(not(feature = "std"))]
|
|
#[doc(hidden)]
|
|
pub fn get_current<T>(f: impl FnOnce(&Dispatch) -> T) -> Option<T> {
|
|
let dispatch = get_global()?;
|
|
Some(f(&dispatch))
|
|
}
|
|
|
|
/// Executes a closure with a reference to the current [dispatcher].
|
|
///
|
|
/// [dispatcher]: super::dispatcher::Dispatch
|
|
#[cfg(not(feature = "std"))]
|
|
pub fn get_default<T, F>(mut f: F) -> T
|
|
where
|
|
F: FnMut(&Dispatch) -> T,
|
|
{
|
|
if let Some(d) = get_global() {
|
|
f(d)
|
|
} else {
|
|
f(&Dispatch::none())
|
|
}
|
|
}
|
|
|
|
fn get_global() -> Option<&'static Dispatch> {
|
|
if GLOBAL_INIT.load(Ordering::SeqCst) != INITIALIZED {
|
|
return None;
|
|
}
|
|
unsafe {
|
|
// This is safe given the invariant that setting the global dispatcher
|
|
// also sets `GLOBAL_INIT` to `INITIALIZED`.
|
|
Some(GLOBAL_DISPATCH.as_ref().expect(
|
|
"invariant violated: GLOBAL_DISPATCH must be initialized before GLOBAL_INIT is set",
|
|
))
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
pub(crate) struct Registrar(Weak<dyn Subscriber + Send + Sync>);
|
|
|
|
impl Dispatch {
|
|
/// Returns a new `Dispatch` that discards events and spans.
|
|
#[inline]
|
|
pub fn none() -> Self {
|
|
Dispatch {
|
|
subscriber: Arc::new(NoSubscriber::default()),
|
|
}
|
|
}
|
|
|
|
/// Returns a `Dispatch` that forwards to the given [`Subscriber`].
|
|
///
|
|
/// [`Subscriber`]: super::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
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
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`] function on the [`Subscriber`]
|
|
/// that this `Dispatch` forwards to.
|
|
///
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`register_callsite`]: super::subscriber::Subscriber::register_callsite
|
|
#[inline]
|
|
pub fn register_callsite(&self, metadata: &'static Metadata<'static>) -> subscriber::Interest {
|
|
self.subscriber.register_callsite(metadata)
|
|
}
|
|
|
|
/// Returns the highest [verbosity level][level] that this [`Subscriber`] will
|
|
/// enable, or `None`, if the subscriber does not implement level-based
|
|
/// filtering or chooses not to implement this method.
|
|
///
|
|
/// This calls the [`max_level_hint`] function on the [`Subscriber`]
|
|
/// that this `Dispatch` forwards to.
|
|
///
|
|
/// [level]: super::Level
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`register_callsite`]: super::subscriber::Subscriber::max_level_hint
|
|
// TODO(eliza): consider making this a public API?
|
|
#[inline]
|
|
pub(crate) fn max_level_hint(&self) -> Option<LevelFilter> {
|
|
self.subscriber.max_level_hint()
|
|
}
|
|
|
|
/// Record the construction of a new span, returning a new [ID] for the
|
|
/// span being constructed.
|
|
///
|
|
/// This calls the [`new_span`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [ID]: super::span::Id
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`new_span`]: super::subscriber::Subscriber::new_span
|
|
#[inline]
|
|
pub fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
|
|
self.subscriber.new_span(span)
|
|
}
|
|
|
|
/// Record a set of values on a span.
|
|
///
|
|
/// This calls the [`record`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`record`]: super::subscriber::Subscriber::record
|
|
#[inline]
|
|
pub fn record(&self, span: &span::Id, values: &span::Record<'_>) {
|
|
self.subscriber.record(span, values)
|
|
}
|
|
|
|
/// Adds an indication that `span` follows from the span with the id
|
|
/// `follows`.
|
|
///
|
|
/// This calls the [`record_follows_from`] function on the [`Subscriber`]
|
|
/// that this `Dispatch` forwards to.
|
|
///
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`record_follows_from`]: super::subscriber::Subscriber::record_follows_from
|
|
#[inline]
|
|
pub fn record_follows_from(&self, span: &span::Id, follows: &span::Id) {
|
|
self.subscriber.record_follows_from(span, follows)
|
|
}
|
|
|
|
/// Returns true if a span with the specified [metadata] would be
|
|
/// recorded.
|
|
///
|
|
/// This calls the [`enabled`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [metadata]: super::metadata::Metadata
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`enabled`]: super::subscriber::Subscriber::enabled
|
|
#[inline]
|
|
pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
|
|
self.subscriber.enabled(metadata)
|
|
}
|
|
|
|
/// Records that an [`Event`] has occurred.
|
|
///
|
|
/// This calls the [`event`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [`Event`]: super::event::Event
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`event`]: super::subscriber::Subscriber::event
|
|
#[inline]
|
|
pub fn event(&self, event: &Event<'_>) {
|
|
self.subscriber.event(event)
|
|
}
|
|
|
|
/// Records that a span has been can_enter.
|
|
///
|
|
/// This calls the [`enter`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`enter`]: super::subscriber::Subscriber::enter
|
|
pub fn enter(&self, span: &span::Id) {
|
|
self.subscriber.enter(span);
|
|
}
|
|
|
|
/// Records that a span has been exited.
|
|
///
|
|
/// This calls the [`exit`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`exit`]: super::subscriber::Subscriber::exit
|
|
pub fn exit(&self, span: &span::Id) {
|
|
self.subscriber.exit(span);
|
|
}
|
|
|
|
/// Notifies the subscriber that a [span ID] has been cloned.
|
|
///
|
|
/// This function must only be called with span IDs that were returned by
|
|
/// this `Dispatch`'s [`new_span`] function. The `tracing` crate upholds
|
|
/// this guarantee and any other libraries implementing instrumentation APIs
|
|
/// must as well.
|
|
///
|
|
/// This calls the [`clone_span`] function on the `Subscriber` that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [span ID]: super::span::Id
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`clone_span`]: super::subscriber::Subscriber::clone_span
|
|
/// [`new_span`]: super::subscriber::Subscriber::new_span
|
|
#[inline]
|
|
pub fn clone_span(&self, id: &span::Id) -> span::Id {
|
|
self.subscriber.clone_span(id)
|
|
}
|
|
|
|
/// Notifies the subscriber that a [span ID] has been dropped.
|
|
///
|
|
/// This function must only be called with span IDs that were returned by
|
|
/// this `Dispatch`'s [`new_span`] function. The `tracing` crate upholds
|
|
/// this guarantee and any other libraries implementing instrumentation APIs
|
|
/// must as well.
|
|
///
|
|
/// This calls the [`drop_span`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// <pre class="compile_fail" style="white-space:normal;font:inherit;">
|
|
/// <strong>Deprecated</strong>: The <a href="#method.try_close"><code>
|
|
/// try_close</code></a> method is functionally identical, but returns
|
|
/// <code>true</code> if the span is now closed. It should be used
|
|
/// instead of this method.
|
|
/// </pre>
|
|
///
|
|
/// [span ID]: super::span::Id
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`drop_span`]: super::subscriber::Subscriber::drop_span
|
|
/// [`new_span`]: super::subscriber::Subscriber::new_span
|
|
/// [`try_close`]: Entered::try_close()
|
|
#[inline]
|
|
#[deprecated(since = "0.1.2", note = "use `Dispatch::try_close` instead")]
|
|
pub fn drop_span(&self, id: span::Id) {
|
|
#[allow(deprecated)]
|
|
self.subscriber.drop_span(id);
|
|
}
|
|
|
|
/// Notifies the subscriber that a [span ID] has been dropped, and returns
|
|
/// `true` if there are now 0 IDs referring to that span.
|
|
///
|
|
/// This function must only be called with span IDs that were returned by
|
|
/// this `Dispatch`'s [`new_span`] function. The `tracing` crate upholds
|
|
/// this guarantee and any other libraries implementing instrumentation APIs
|
|
/// must as well.
|
|
///
|
|
/// This calls the [`try_close`] function on the [`Subscriber`] that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [span ID]: super::span::Id
|
|
/// [`Subscriber`]: super::subscriber::Subscriber
|
|
/// [`try_close`]: super::subscriber::Subscriber::try_close
|
|
/// [`new_span`]: super::subscriber::Subscriber::new_span
|
|
pub fn try_close(&self, id: span::Id) -> bool {
|
|
self.subscriber.try_close(id)
|
|
}
|
|
|
|
/// Returns a type representing this subscriber's view of the current span.
|
|
///
|
|
/// This calls the [`current`] function on the `Subscriber` that this
|
|
/// `Dispatch` forwards to.
|
|
///
|
|
/// [`current`]: super::subscriber::Subscriber::current_span
|
|
#[inline]
|
|
pub fn current_span(&self) -> span::Current {
|
|
self.subscriber.current_span()
|
|
}
|
|
|
|
/// Returns `true` if this `Dispatch` forwards to a `Subscriber` of type
|
|
/// `T`.
|
|
#[inline]
|
|
pub fn is<T: Any>(&self) -> bool {
|
|
<dyn Subscriber>::is::<T>(&*self.subscriber)
|
|
}
|
|
|
|
/// Returns some reference to the `Subscriber` this `Dispatch` forwards to
|
|
/// if it is of type `T`, or `None` if it isn't.
|
|
#[inline]
|
|
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
|
<dyn Subscriber>::downcast_ref(&*self.subscriber)
|
|
}
|
|
}
|
|
|
|
impl Default for Dispatch {
|
|
/// Returns the current default dispatcher
|
|
fn default() -> Self {
|
|
get_default(|default| default.clone())
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Dispatch {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_tuple("Dispatch")
|
|
.field(&format_args!("{:p}", self.subscriber))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<S> From<S> for Dispatch
|
|
where
|
|
S: Subscriber + Send + Sync + 'static,
|
|
{
|
|
#[inline]
|
|
fn from(subscriber: S) -> Self {
|
|
Dispatch::new(subscriber)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl Registrar {
|
|
pub(crate) fn upgrade(&self) -> Option<Dispatch> {
|
|
self.0.upgrade().map(|subscriber| Dispatch { subscriber })
|
|
}
|
|
}
|
|
|
|
// ===== impl State =====
|
|
|
|
#[cfg(feature = "std")]
|
|
impl State {
|
|
/// Replaces the current default dispatcher on this thread with the provided
|
|
/// dispatcher.Any
|
|
///
|
|
/// Dropping the returned `ResetGuard` will reset the default dispatcher to
|
|
/// the previous value.
|
|
#[inline]
|
|
fn set_default(new_dispatch: Dispatch) -> DefaultGuard {
|
|
let prior = CURRENT_STATE
|
|
.try_with(|state| {
|
|
state.can_enter.set(true);
|
|
state.default.replace(Some(new_dispatch))
|
|
})
|
|
.ok()
|
|
.flatten();
|
|
EXISTS.store(true, Ordering::Release);
|
|
DefaultGuard(prior)
|
|
}
|
|
|
|
#[inline]
|
|
fn enter(&self) -> Option<Entered<'_>> {
|
|
if self.can_enter.replace(false) {
|
|
Some(Entered(self))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===== impl Entered =====
|
|
|
|
#[cfg(feature = "std")]
|
|
impl<'a> Entered<'a> {
|
|
#[inline]
|
|
fn current(&self) -> RefMut<'a, Dispatch> {
|
|
let default = self.0.default.borrow_mut();
|
|
RefMut::map(default, |default| {
|
|
default.get_or_insert_with(|| get_global().cloned().unwrap_or_else(Dispatch::none))
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl<'a> Drop for Entered<'a> {
|
|
#[inline]
|
|
fn drop(&mut self) {
|
|
self.0.can_enter.set(true);
|
|
}
|
|
}
|
|
|
|
// ===== impl DefaultGuard =====
|
|
|
|
#[cfg(feature = "std")]
|
|
impl Drop for DefaultGuard {
|
|
#[inline]
|
|
fn drop(&mut self) {
|
|
// Replace the dispatcher and then drop the old one outside
|
|
// of the thread-local context. Dropping the dispatch may
|
|
// lead to the drop of a subscriber which, in the process,
|
|
// could then also attempt to access the same thread local
|
|
// state -- causing a clash.
|
|
let prev = CURRENT_STATE.try_with(|state| state.default.replace(self.0.take()));
|
|
drop(prev)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
#[cfg(feature = "std")]
|
|
use crate::stdlib::sync::atomic::{AtomicUsize, Ordering};
|
|
use crate::{
|
|
callsite::Callsite,
|
|
metadata::{Kind, Level, Metadata},
|
|
subscriber::Interest,
|
|
};
|
|
|
|
#[test]
|
|
fn dispatch_is() {
|
|
let dispatcher = Dispatch::new(NoSubscriber::default());
|
|
assert!(dispatcher.is::<NoSubscriber>());
|
|
}
|
|
|
|
#[test]
|
|
fn dispatch_downcasts() {
|
|
let dispatcher = Dispatch::new(NoSubscriber::default());
|
|
assert!(dispatcher.downcast_ref::<NoSubscriber>().is_some());
|
|
}
|
|
|
|
struct TestCallsite;
|
|
static TEST_CALLSITE: TestCallsite = TestCallsite;
|
|
static TEST_META: Metadata<'static> = metadata! {
|
|
name: "test",
|
|
target: module_path!(),
|
|
level: Level::DEBUG,
|
|
fields: &[],
|
|
callsite: &TEST_CALLSITE,
|
|
kind: Kind::EVENT
|
|
};
|
|
|
|
impl Callsite for TestCallsite {
|
|
fn set_interest(&self, _: Interest) {}
|
|
fn metadata(&self) -> &Metadata<'_> {
|
|
&TEST_META
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(feature = "std")]
|
|
fn events_dont_infinite_loop() {
|
|
// This test ensures that an event triggered within a subscriber
|
|
// won't cause an infinite loop of events.
|
|
struct TestSubscriber;
|
|
impl Subscriber for TestSubscriber {
|
|
fn enabled(&self, _: &Metadata<'_>) -> bool {
|
|
true
|
|
}
|
|
|
|
fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
|
|
span::Id::from_u64(0xAAAA)
|
|
}
|
|
|
|
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
|
|
|
|
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
|
|
|
|
fn event(&self, _: &Event<'_>) {
|
|
static EVENTS: AtomicUsize = AtomicUsize::new(0);
|
|
assert_eq!(
|
|
EVENTS.fetch_add(1, Ordering::Relaxed),
|
|
0,
|
|
"event method called twice!"
|
|
);
|
|
Event::dispatch(&TEST_META, &TEST_META.fields().value_set(&[]))
|
|
}
|
|
|
|
fn enter(&self, _: &span::Id) {}
|
|
|
|
fn exit(&self, _: &span::Id) {}
|
|
}
|
|
|
|
with_default(&Dispatch::new(TestSubscriber), || {
|
|
Event::dispatch(&TEST_META, &TEST_META.fields().value_set(&[]))
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(feature = "std")]
|
|
fn spans_dont_infinite_loop() {
|
|
// This test ensures that a span created within a subscriber
|
|
// won't cause an infinite loop of new spans.
|
|
|
|
fn mk_span() {
|
|
get_default(|current| {
|
|
current.new_span(&span::Attributes::new(
|
|
&TEST_META,
|
|
&TEST_META.fields().value_set(&[]),
|
|
))
|
|
});
|
|
}
|
|
|
|
struct TestSubscriber;
|
|
impl Subscriber for TestSubscriber {
|
|
fn enabled(&self, _: &Metadata<'_>) -> bool {
|
|
true
|
|
}
|
|
|
|
fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
|
|
static NEW_SPANS: AtomicUsize = AtomicUsize::new(0);
|
|
assert_eq!(
|
|
NEW_SPANS.fetch_add(1, Ordering::Relaxed),
|
|
0,
|
|
"new_span method called twice!"
|
|
);
|
|
mk_span();
|
|
span::Id::from_u64(0xAAAA)
|
|
}
|
|
|
|
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
|
|
|
|
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
|
|
|
|
fn event(&self, _: &Event<'_>) {}
|
|
|
|
fn enter(&self, _: &span::Id) {}
|
|
|
|
fn exit(&self, _: &span::Id) {}
|
|
}
|
|
|
|
with_default(&Dispatch::new(TestSubscriber), mk_span)
|
|
}
|
|
|
|
#[test]
|
|
fn default_no_subscriber() {
|
|
let default_dispatcher = Dispatch::default();
|
|
assert!(default_dispatcher.is::<NoSubscriber>());
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
#[test]
|
|
fn default_dispatch() {
|
|
struct TestSubscriber;
|
|
impl Subscriber for TestSubscriber {
|
|
fn enabled(&self, _: &Metadata<'_>) -> bool {
|
|
true
|
|
}
|
|
|
|
fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
|
|
span::Id::from_u64(0xAAAA)
|
|
}
|
|
|
|
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
|
|
|
|
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
|
|
|
|
fn event(&self, _: &Event<'_>) {}
|
|
|
|
fn enter(&self, _: &span::Id) {}
|
|
|
|
fn exit(&self, _: &span::Id) {}
|
|
}
|
|
let guard = set_default(&Dispatch::new(TestSubscriber));
|
|
let default_dispatcher = Dispatch::default();
|
|
assert!(default_dispatcher.is::<TestSubscriber>());
|
|
|
|
drop(guard);
|
|
let default_dispatcher = Dispatch::default();
|
|
assert!(default_dispatcher.is::<NoSubscriber>());
|
|
}
|
|
}
|