//! 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 //! ``` //! //!
//!     Note:the thread-local scoped dispatcher
//!     (with_default) requires the
//!     Rust standard library. no_std users should use
//!     set_global_default
//!     instead.
//! 
//! //! ## 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, } #[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 = None; /// The dispatch state of a thread. #[cfg(feature = "std")] struct State { /// This thread's current default dispatcher. default: RefCell>, /// 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, } /// 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); /// Sets this dispatch as the default for the duration of a closure. /// /// The default dispatcher is used when creating a new [span] or /// [`Event`]. /// ///
///     Note: This function required the Rust standard library.
///     no_std users should use 
///     set_global_default instead.
/// 
/// /// [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(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 /// ///
///     Note: This function required the Rust standard library.
///     no_std users should use 
///     set_global_default instead.
/// 
/// /// [`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. /// ///
///     Warning: In general, libraries should not call
///     set_global_default()! Doing so will cause conflicts when
///     executables that depend on the library try to set the default later.
/// 
/// /// [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. pub struct SetGlobalDefaultError { _no_construct: (), } impl fmt::Debug for SetGlobalDefaultError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("SetGlobalDefaultError") .field(&Self::MESSAGE) .finish() } } impl fmt::Display for SetGlobalDefaultError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad(Self::MESSAGE) } } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl error::Error for SetGlobalDefaultError {} impl SetGlobalDefaultError { const MESSAGE: &'static str = "a global default trace dispatcher has already been set"; } /// 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(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(f: impl FnOnce(&Dispatch) -> T) -> Option { 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(f: impl FnOnce(&Dispatch) -> T) -> Option { 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(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); 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(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 { 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<'_>) { if self.subscriber.event_enabled(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. /// ///
    ///     Deprecated: The 
    ///     try_close method is functionally identical, but returns
    ///     true if the span is now closed. It should be used
    ///     instead of this method.
    /// 
/// /// [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(&self) -> bool { ::is::(&*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(&self) -> Option<&T> { ::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 From 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 { 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> { 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::()); } #[test] fn dispatch_downcasts() { let dispatcher = Dispatch::new(NoSubscriber::default()); assert!(dispatcher.downcast_ref::().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::()); } #[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::()); drop(guard); let default_dispatcher = Dispatch::default(); assert!(default_dispatcher.is::()); } }