| //! 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 |
| //! ``` |
| //! |
| //! <div class="information"> |
| //! <div class="tooltip ignore" style="">ⓘ<span class="tooltiptext">Note</span></div> |
| //! </div> |
| //! <div class="example-wrap" style="display:inline-block"> |
| //! <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></div> |
| //! |
| //! ## 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. |
| //! |
| //! [`Subscriber`]: struct.Subscriber.html |
| //! [`with_default`]: fn.with_default.html |
| //! [`set_global_default`]: fn.set_global_default.html |
| //! [`get_default`]: fn.get_default.html |
| //! [`Dispatch`]: struct.Dispatch.html |
| use crate::{ |
| callsite, span, |
| subscriber::{self, Subscriber}, |
| Event, LevelFilter, Metadata, |
| }; |
| |
| use crate::stdlib::{ |
| any::Any, |
| fmt, |
| sync::{ |
| atomic::{AtomicBool, AtomicUsize, Ordering}, |
| Arc, Weak, |
| }, |
| }; |
| |
| #[cfg(feature = "std")] |
| use crate::stdlib::{ |
| cell::{Cell, RefCell, RefMut}, |
| error, |
| }; |
| |
| /// `Dispatch` trace data to a [`Subscriber`]. |
| /// |
| /// [`Subscriber`]: trait.Subscriber.html |
| #[derive(Clone)] |
| pub struct Dispatch { |
| subscriber: Arc<dyn Subscriber + Send + Sync>, |
| } |
| |
| #[cfg(feature = "std")] |
| thread_local! { |
| static CURRENT_STATE: State = State { |
| default: RefCell::new(Dispatch::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<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`]. |
| /// |
| /// <div class="information"> |
| /// <div class="tooltip ignore" style="">ⓘ<span class="tooltiptext">Note</span></div> |
| /// </div> |
| /// <div class="example-wrap" style="display:inline-block"> |
| /// <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></div> |
| /// |
| /// [span]: ../span/index.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`Event`]: ../event/struct.Event.html |
| /// [`set_global_default`]: ../fn.set_global_default.html |
| #[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 |
| /// |
| /// <div class="information"> |
| /// <div class="tooltip ignore" style="">ⓘ<span class="tooltiptext">Note</span></div> |
| /// </div> |
| /// <div class="example-wrap" style="display:inline-block"> |
| /// <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></div> |
| /// |
| /// [`set_global_default`]: ../fn.set_global_default.html |
| #[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="information"> |
| /// <div class="tooltip compile_fail" style="">⚠ ️<span class="tooltiptext">Warning</span></div> |
| /// </div><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]: ../span/index.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`Event`]: ../event/struct.Event.html |
| pub fn set_global_default(dispatcher: Dispatch) -> Result<(), SetGlobalDefaultError> { |
| #[allow(deprecated)] |
| if GLOBAL_INIT.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) == UNINITIALIZED |
| { |
| 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]: ../dispatcher/struct.Dispatch.html |
| #[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]: ../dispatcher/struct.Dispatch.html |
| #[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]: ../dispatcher/struct.Dispatch.html |
| #[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]: ../dispatcher/struct.Dispatch.html |
| #[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", |
| )) |
| } |
| } |
| |
| 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) } |
| } |
| |
| /// Returns a `Dispatch` that forwards to the given [`Subscriber`]. |
| /// |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| 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`] function on the [`Subscriber`] |
| /// that this `Dispatch` forwards to. |
| /// |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`register_callsite`]: ../subscriber/trait.Subscriber.html#method.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]: ../struct.Level.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`register_callsite`]: ../subscriber/trait.Subscriber.html#method.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]: ../span/struct.Id.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.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`]: ../subscriber/trait.Subscriber.html |
| /// [`record`]: ../subscriber/trait.Subscriber.html#method.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`]: ../subscriber/trait.Subscriber.html |
| /// [`record_follows_from`]: ../subscriber/trait.Subscriber.html#method.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]: ../metadata/struct.Metadata.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`enabled`]: ../subscriber/trait.Subscriber.html#method.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`]: ../event/struct.Event.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`event`]: ../subscriber/trait.Subscriber.html#method.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`]: ../subscriber/trait.Subscriber.html |
| /// [`enter`]: ../subscriber/trait.Subscriber.html#method.enter |
| #[inline] |
| 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`]: ../subscriber/trait.Subscriber.html |
| /// [`exit`]: ../subscriber/trait.Subscriber.html#method.exit |
| #[inline] |
| 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]: ../span/struct.Id.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`clone_span`]: ../subscriber/trait.Subscriber.html#method.clone_span |
| /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.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. |
| /// |
| /// <div class="information"> |
| /// <div class="tooltip compile_fail" style="">⚠ ️<span class="tooltiptext">Warning</span></div> |
| /// </div> |
| /// <div class="example-wrap" style="display:inline-block"><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></div> |
| /// |
| /// [span ID]: ../span/struct.Id.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`drop_span`]: ../subscriber/trait.Subscriber.html#method.drop_span |
| /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span |
| /// [`try_close`]: #method.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]: ../span/struct.Id.html |
| /// [`Subscriber`]: ../subscriber/trait.Subscriber.html |
| /// [`try_close`]: ../subscriber/trait.Subscriber.html#method.try_close |
| /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span |
| #[inline] |
| 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`]: ../subscriber/trait.Subscriber.html#method.current |
| #[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 { |
| #[allow(bare_trait_objects)] |
| 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> { |
| #[allow(bare_trait_objects)] |
| 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.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, _: &'static Metadata<'static>) -> subscriber::Interest { |
| subscriber::Interest::never() |
| } |
| |
| fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { |
| span::Id::from_u64(0xDEAD) |
| } |
| |
| fn event(&self, _event: &Event<'_>) {} |
| |
| fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {} |
| |
| fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {} |
| |
| #[inline] |
| fn enabled(&self, _metadata: &Metadata<'_>) -> bool { |
| false |
| } |
| |
| fn enter(&self, _span: &span::Id) {} |
| fn exit(&self, _span: &span::Id) {} |
| } |
| |
| impl Registrar { |
| pub(crate) fn try_register( |
| &self, |
| metadata: &'static Metadata<'static>, |
| ) -> Option<subscriber::Interest> { |
| self.0.upgrade().map(|s| s.register_callsite(metadata)) |
| } |
| |
| 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(new_dispatch) |
| }) |
| .ok(); |
| 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 mut default = self.0.default.borrow_mut(); |
| |
| if default.is::<NoSubscriber>() { |
| if let Some(global) = get_global() { |
| // don't redo this call on the next check |
| *default = global.clone(); |
| } |
| } |
| |
| default |
| } |
| } |
| |
| #[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) { |
| if let Some(dispatch) = self.0.take() { |
| // 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(dispatch)); |
| 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); |
| assert!(dispatcher.is::<NoSubscriber>()); |
| } |
| |
| #[test] |
| fn dispatch_downcasts() { |
| let dispatcher = Dispatch::new(NoSubscriber); |
| 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>()); |
| } |
| } |