🛈 Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

tracing_subscriber/registry/
mod.rs

1//! Storage for span data shared by multiple [`Subscribe`]s.
2//!
3//! ## Using the Span Registry
4//!
5//! This module provides the [`Registry`] type, a [`Collect`] implementation
6//! which tracks per-span data and exposes it to subscribers. When a `Registry`
7//! is used as the base `Collect` of a `Subscribe` stack, the
8//! [`subscribe::Context`][ctx] type will provide methods allowing subscribers to
9//! [look up span data][lookup] stored in the registry. While [`Registry`] is a
10//! reasonable default for storing spans and events, other stores that implement
11//! [`LookupSpan`] and [`Collect`] themselves (with [`SpanData`] implemented
12//! by the per-span data they store) can be used as a drop-in replacement.
13//!
14//! For example, we might create a `Registry` and add multiple `Subscriber`s like so:
15//! ```rust
16//! use tracing_subscriber::{registry::Registry, Subscribe, prelude::*};
17//! # use tracing_core::Collect;
18//! # pub struct FooSubscriber {}
19//! # pub struct BarSubscriber {}
20//! # impl<C: Collect> Subscribe<C> for FooSubscriber {}
21//! # impl<C: Collect> Subscribe<C> for BarSubscriber {}
22//! # impl FooSubscriber {
23//! # fn new() -> Self { Self {} }
24//! # }
25//! # impl BarSubscriber {
26//! # fn new() -> Self { Self {} }
27//! # }
28//!
29//! let subscriber = Registry::default()
30//!     .with(FooSubscriber::new())
31//!     .with(BarSubscriber::new());
32//! ```
33//!
34//! If a type implementing `Subscribe` depends on the functionality of a `Registry`
35//! implementation, it should bound its `Collect` type parameter with the
36//! [`LookupSpan`] trait, like so:
37//!
38//! ```rust
39//! use tracing_subscriber::{registry, Subscribe};
40//! use tracing_core::Collect;
41//!
42//! pub struct MySubscriber {
43//!     // ...
44//! }
45//!
46//! impl<C> Subscribe<C> for MySubscriber
47//! where
48//!     C: Collect + for<'a> registry::LookupSpan<'a>,
49//! {
50//!     // ...
51//! }
52//! ```
53//! When this bound is added, the subscriber implementation will be guaranteed
54//! access to the [`Context`][ctx] methods, such as [`Context::span`][lookup], that
55//! require the root collector to be a registry.
56//!
57//! [`Subscribe`]: crate::subscribe::Subscribe
58//! [`Collect`]: tracing_core::collect::Collect
59//! [ctx]: crate::subscribe::Context
60//! [lookup]: crate::subscribe::Context::span()
61use core::fmt::Debug;
62
63use tracing_core::{field::FieldSet, span::Id, Metadata};
64
65feature! {
66    #![feature = "std"]
67    /// A module containing a type map of span extensions.
68    mod extensions;
69    pub use extensions::{Extensions, ExtensionsMut};
70
71}
72
73feature! {
74    #![all(feature = "registry", feature = "std")]
75
76    mod sharded;
77    mod stack;
78
79    pub use sharded::Data;
80    pub use sharded::Registry;
81
82    use crate::filter::FilterId;
83}
84
85/// Provides access to stored span data.
86///
87/// Subscribers which store span data and associate it with span IDs should
88/// implement this trait; if they do, any [`Subscriber`]s wrapping them can look up
89/// metadata via the [`Context`] type's [`span()`] method.
90///
91/// [`Subscriber`]: crate::Subscribe
92/// [`Context`]: crate::subscribe::Context
93/// [`span()`]: crate::subscribe::Context::span()
94pub trait LookupSpan<'a> {
95    /// The type of span data stored in this registry.
96    type Data: SpanData<'a>;
97
98    /// Returns the [`SpanData`] for a given [`Id`], if it exists.
99    ///
100    /// <div class="example-wrap" style="display:inline-block">
101    /// <pre class="ignore" style="white-space:normal;font:inherit;">
102    ///
103    /// **Note**: users of the `LookupSpan` trait should
104    /// typically call the [`span`][Self::span] method rather
105    /// than this method. The `span` method is implemented by
106    /// *calling* `span_data`, but returns a reference which is
107    /// capable of performing more sophisticated queries.
108    ///
109    /// </pre></div>
110    ///
111    fn span_data(&'a self, id: &Id) -> Option<Self::Data>;
112
113    /// Returns a [`SpanRef`] for the span with the given `Id`, if it exists.
114    ///
115    /// A `SpanRef` is similar to [`SpanData`], but it allows performing
116    /// additional lookups against the registry that stores the wrapped data.
117    ///
118    /// In general, _users_ of the `LookupSpan` trait should use this method
119    /// rather than the [`span_data`] method; while _implementors_ of this trait
120    /// should only implement `span_data`.
121    ///
122    /// [`span_data`]: LookupSpan::span_data()
123    fn span(&'a self, id: &Id) -> Option<SpanRef<'a, Self>>
124    where
125        Self: Sized,
126    {
127        let data = self.span_data(id)?;
128        Some(SpanRef {
129            registry: self,
130            data,
131            #[cfg(feature = "registry")]
132            filter: FilterId::none(),
133        })
134    }
135
136    /// Registers a [`Filter`] for [per-subscriber filtering] with this
137    /// [collector].
138    ///
139    /// The [`Filter`] can then use the returned [`FilterId`] to
140    /// [check if it previously enabled a span][check].
141    ///
142    /// # Panics
143    ///
144    /// If this collector does not support [per-subscriber filtering].
145    ///
146    /// [`Filter`]: crate::subscribe::Filter
147    /// [per-subscriber filtering]: crate::subscribe#per-subscriber-filtering
148    /// [collector]: tracing_core::Collect
149    /// [`FilterId`]: crate::filter::FilterId
150    /// [check]: SpanData::is_enabled_for
151    #[cfg(feature = "registry")]
152    #[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
153    fn register_filter(&mut self) -> FilterId {
154        panic!(
155            "{} does not currently support filters",
156            std::any::type_name::<Self>()
157        )
158    }
159}
160
161/// A stored representation of data associated with a span.
162pub trait SpanData<'a> {
163    /// Returns this span's ID.
164    fn id(&self) -> Id;
165
166    /// Returns a reference to the span's `Metadata`.
167    fn metadata(&self) -> &'static Metadata<'static>;
168
169    /// Returns a reference to the ID
170    fn parent(&self) -> Option<&Id>;
171
172    /// Returns a reference to this span's `Extensions`.
173    ///
174    /// The extensions may be used by `Subscriber`s to store additional data
175    /// describing the span.
176    #[cfg(feature = "std")]
177    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
178    fn extensions(&self) -> Extensions<'_>;
179
180    /// Returns a mutable reference to this span's `Extensions`.
181    ///
182    /// The extensions may be used by `Subscriber`s to store additional data
183    /// describing the span.
184    #[cfg(feature = "std")]
185    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
186    fn extensions_mut(&self) -> ExtensionsMut<'_>;
187
188    /// Returns `true` if this span is enabled for the [per-subscriber filter][psf]
189    /// corresponding to the provided [`FilterId`].
190    ///
191    /// ## Default Implementation
192    ///
193    /// By default, this method assumes that the [`LookupSpan`] implementation
194    /// does not support [per-subscriber filtering][psf], and always returns `true`.
195    ///
196    /// [psf]: crate::subscribe#per-subscriber-filtering
197    /// [`FilterId`]: crate::filter::FilterId
198    #[cfg(feature = "registry")]
199    #[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
200    fn is_enabled_for(&self, filter: FilterId) -> bool {
201        let _ = filter;
202        true
203    }
204}
205
206/// A reference to [span data] and the associated [registry].
207///
208/// This type implements all the same methods as [`SpanData`], and provides
209/// additional methods for querying the registry based on values from the span.
210///
211/// [registry]: LookupSpan
212#[derive(Debug)]
213pub struct SpanRef<'a, R: LookupSpan<'a>> {
214    registry: &'a R,
215    data: R::Data,
216
217    #[cfg(feature = "registry")]
218    filter: FilterId,
219}
220
221/// An iterator over the parents of a span, ordered from leaf to root.
222///
223/// This is returned by the [`SpanRef::scope`] method.
224#[derive(Debug)]
225pub struct Scope<'a, R> {
226    registry: &'a R,
227    next: Option<Id>,
228
229    #[cfg(all(feature = "registry", feature = "std"))]
230    filter: FilterId,
231}
232
233feature! {
234    #![any(feature = "alloc", feature = "std")]
235
236    use alloc::boxed::Box;
237
238    #[cfg(feature = "portable-atomic")]
239    use portable_atomic_util::Arc;
240
241    #[cfg(not(feature = "portable-atomic"))]
242    use alloc::sync::Arc;
243
244    #[cfg(not(feature = "smallvec"))]
245    use alloc::vec::{self, Vec};
246    use core::{fmt,iter};
247
248    /// An iterator over the parents of a span, ordered from root to leaf.
249    ///
250    /// This is returned by the [`Scope::from_root`] method.
251    pub struct ScopeFromRoot<'a, R>
252    where
253        R: LookupSpan<'a>,
254    {
255        #[cfg(feature = "smallvec")]
256        spans: iter::Rev<smallvec::IntoIter<SpanRefVecArray<'a, R>>>,
257        #[cfg(not(feature = "smallvec"))]
258        spans: iter::Rev<vec::IntoIter<SpanRef<'a, R>>>,
259    }
260
261    #[cfg(feature = "smallvec")]
262    type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16];
263
264    impl<'a, S> LookupSpan<'a> for Arc<S>
265    where
266        S: LookupSpan<'a>,
267    {
268        type Data = <S as LookupSpan<'a>>::Data;
269
270        fn span_data(&'a self, id: &Id) -> Option<Self::Data> {
271            self.as_ref().span_data(id)
272        }
273
274        fn span(&'a self, id: &Id) -> Option<SpanRef<'a, Self>>
275        where
276            Self: Sized,
277        {
278            self.as_ref().span(id).map(
279                |SpanRef {
280                    registry: _,
281                    data,
282                    #[cfg(feature = "registry")]
283                    filter,
284                }| SpanRef {
285                    registry: self,
286                    data,
287                    #[cfg(feature = "registry")]
288                    filter,
289                },
290            )
291        }
292    }
293
294    impl<'a, S> LookupSpan<'a> for Box<S>
295    where
296        S: LookupSpan<'a>,
297    {
298        type Data = <S as LookupSpan<'a>>::Data;
299
300        fn span_data(&'a self, id: &Id) -> Option<Self::Data> {
301            self.as_ref().span_data(id)
302        }
303
304        fn span(&'a self, id: &Id) -> Option<SpanRef<'a, Self>>
305        where
306            Self: Sized,
307        {
308            self.as_ref().span(id).map(
309                |SpanRef {
310                    registry: _,
311                    data,
312                    #[cfg(feature = "registry")]
313                    filter,
314                }| SpanRef {
315                    registry: self,
316                    data,
317                    #[cfg(feature = "registry")]
318                    filter,
319                },
320            )
321        }
322    }
323
324    impl<'a, R> Scope<'a, R>
325    where
326        R: LookupSpan<'a>,
327    {
328        /// Flips the order of the iterator, so that it is ordered from root to leaf.
329        ///
330        /// The iterator will first return the root span, then that span's immediate child,
331        /// and so on until it finally returns the span that [`SpanRef::scope`] was called on.
332        ///
333        /// If any items were consumed from the [`Scope`] before calling this method then they
334        /// will *not* be returned from the [`ScopeFromRoot`].
335        ///
336        /// **Note**: this will allocate if there are many spans remaining, or if the
337        /// "smallvec" feature flag is not enabled.
338        #[allow(clippy::wrong_self_convention)]
339        pub fn from_root(self) -> ScopeFromRoot<'a, R> {
340            #[cfg(feature = "smallvec")]
341            type Buf<T> = smallvec::SmallVec<T>;
342            #[cfg(not(feature = "smallvec"))]
343            type Buf<T> = Vec<T>;
344            ScopeFromRoot {
345                spans: self.collect::<Buf<_>>().into_iter().rev(),
346            }
347        }
348    }
349
350    impl<'a, R> Iterator for ScopeFromRoot<'a, R>
351    where
352        R: LookupSpan<'a>,
353    {
354        type Item = SpanRef<'a, R>;
355
356        #[inline]
357        fn next(&mut self) -> Option<Self::Item> {
358            self.spans.next()
359        }
360
361        #[inline]
362        fn size_hint(&self) -> (usize, Option<usize>) {
363            self.spans.size_hint()
364        }
365    }
366
367    impl<'a, R> fmt::Debug for ScopeFromRoot<'a, R>
368    where
369        R: LookupSpan<'a>,
370    {
371        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372            f.pad("ScopeFromRoot { .. }")
373        }
374    }
375}
376
377impl<'a, R> Iterator for Scope<'a, R>
378where
379    R: LookupSpan<'a>,
380{
381    type Item = SpanRef<'a, R>;
382
383    fn next(&mut self) -> Option<Self::Item> {
384        loop {
385            let curr = self.registry.span(self.next.as_ref()?)?;
386
387            #[cfg(all(feature = "registry", feature = "std"))]
388            let curr = curr.with_filter(self.filter);
389            self.next = curr.data.parent().cloned();
390
391            // If the `Scope` is filtered, check if the current span is enabled
392            // by the selected filter ID.
393
394            #[cfg(all(feature = "registry", feature = "std"))]
395            {
396                if !curr.is_enabled_for(self.filter) {
397                    // The current span in the chain is disabled for this
398                    // filter. Try its parent.
399                    continue;
400                }
401            }
402
403            return Some(curr);
404        }
405    }
406}
407
408impl<'a, R> SpanRef<'a, R>
409where
410    R: LookupSpan<'a>,
411{
412    /// Returns this span's ID.
413    pub fn id(&self) -> Id {
414        self.data.id()
415    }
416
417    /// Returns a static reference to the span's metadata.
418    pub fn metadata(&self) -> &'static Metadata<'static> {
419        self.data.metadata()
420    }
421
422    /// Returns the span's name,
423    pub fn name(&self) -> &'static str {
424        self.data.metadata().name()
425    }
426
427    /// Returns a list of [fields] defined by the span.
428    ///
429    /// [fields]: tracing_core::field
430    pub fn fields(&self) -> &FieldSet {
431        self.data.metadata().fields()
432    }
433
434    /// Returns the ID of this span's parent, or `None` if this span is the root
435    /// of its trace tree.
436    #[deprecated(
437        note = "this method cannot properly support per-subscriber filtering, and may \
438            return the `Id` of a disabled span if per-subscriber filtering is in \
439            use. use `.parent().map(SpanRef::id)` instead.",
440        since = "0.2.21"
441    )]
442    pub fn parent_id(&self) -> Option<&Id> {
443        // XXX(eliza): this doesn't work with PSF because the ID is potentially
444        // borrowed from a parent we got from the registry, rather than from
445        // `self`, so we can't return a borrowed parent. so, right now, we just
446        // return the actual parent ID, and ignore PSF. which is not great.
447        //
448        // i think if we want this to play nice with PSF, we should just change
449        // it to return the `Id` by value instead of `&Id` (which we ought to do
450        // anyway since an `Id` is just a word) but that's a breaking change.
451        // alternatively, we could deprecate this method since it can't support
452        // PSF in its current form (which is what we would want to do if we want
453        // to release PSF in a minor version)...
454
455        // let mut id = self.data.parent()?;
456        // loop {
457        //     // Is this parent enabled by our filter?
458        //     if self
459        //         .filter
460        //         .map(|filter| self.registry.is_enabled_for(id, filter))
461        //         .unwrap_or(true)
462        //     {
463        //         return Some(id);
464        //     }
465        //     id = self.registry.span_data(id)?.parent()?;
466        // }
467        self.data.parent()
468    }
469
470    /// Returns a `SpanRef` describing this span's parent, or `None` if this
471    /// span is the root of its trace tree.
472    pub fn parent(&self) -> Option<Self> {
473        let id = self.data.parent()?;
474        let data = self.registry.span_data(id)?;
475
476        #[cfg(all(feature = "registry", feature = "std"))]
477        {
478            // move these into mut bindings if the registry feature is enabled,
479            // since they may be mutated in the loop.
480            let mut data = data;
481            loop {
482                // Is this parent enabled by our filter?
483                if data.is_enabled_for(self.filter) {
484                    return Some(Self {
485                        registry: self.registry,
486                        filter: self.filter,
487                        data,
488                    });
489                }
490
491                // It's not enabled. If the disabled span has a parent, try that!
492                let id = data.parent()?;
493                data = self.registry.span_data(id)?;
494            }
495        }
496
497        #[cfg(not(all(feature = "registry", feature = "std")))]
498        Some(Self {
499            registry: self.registry,
500            data,
501        })
502    }
503
504    /// Returns an iterator over all parents of this span, starting with this span,
505    /// ordered from leaf to root.
506    ///
507    /// The iterator will first return the span, then the span's immediate parent,
508    /// followed by that span's parent, and so on, until it reaches a root span.
509    ///
510    /// ```rust
511    /// use tracing::{span, Collect};
512    /// use tracing_subscriber::{
513    ///     subscribe::{Context, Subscribe},
514    ///     prelude::*,
515    ///     registry::LookupSpan,
516    /// };
517    ///
518    /// struct PrintingSubscriber;
519    /// impl<C> Subscribe<C> for PrintingSubscriber
520    /// where
521    ///     C: Collect + for<'lookup> LookupSpan<'lookup>,
522    /// {
523    ///     fn on_enter(&self, id: &span::Id, ctx: Context<C>) {
524    ///         let span = ctx.span(id).unwrap();
525    ///         let scope = span.scope().map(|span| span.name()).collect::<Vec<_>>();
526    ///         println!("Entering span: {:?}", scope);
527    ///     }
528    /// }
529    ///
530    /// tracing::collect::with_default(tracing_subscriber::registry().with(PrintingSubscriber), || {
531    ///     let _root = tracing::info_span!("root").entered();
532    ///     // Prints: Entering span: ["root"]
533    ///     let _child = tracing::info_span!("child").entered();
534    ///     // Prints: Entering span: ["child", "root"]
535    ///     let _leaf = tracing::info_span!("leaf").entered();
536    ///     // Prints: Entering span: ["leaf", "child", "root"]
537    /// });
538    /// ```
539    ///
540    /// If the opposite order (from the root to this span) is desired, calling [`Scope::from_root`] on
541    /// the returned iterator reverses the order.
542    ///
543    /// ```rust
544    /// # use tracing::{span, Collect};
545    /// # use tracing_subscriber::{
546    /// #     subscribe::{Context, Subscribe},
547    /// #     prelude::*,
548    /// #     registry::LookupSpan,
549    /// # };
550    /// # struct PrintingSubscriber;
551    /// impl<C> Subscribe<C> for PrintingSubscriber
552    /// where
553    ///     C: Collect + for<'lookup> LookupSpan<'lookup>,
554    /// {
555    ///     fn on_enter(&self, id: &span::Id, ctx: Context<C>) {
556    ///         let span = ctx.span(id).unwrap();
557    ///         let scope = span.scope().from_root().map(|span| span.name()).collect::<Vec<_>>();
558    ///         println!("Entering span: {:?}", scope);
559    ///     }
560    /// }
561    ///
562    /// tracing::collect::with_default(tracing_subscriber::registry().with(PrintingSubscriber), || {
563    ///     let _root = tracing::info_span!("root").entered();
564    ///     // Prints: Entering span: ["root"]
565    ///     let _child = tracing::info_span!("child").entered();
566    ///     // Prints: Entering span: ["root", "child"]
567    ///     let _leaf = tracing::info_span!("leaf").entered();
568    ///     // Prints: Entering span: ["root", "child", "leaf"]
569    /// });
570    /// ```
571    pub fn scope(&self) -> Scope<'a, R> {
572        Scope {
573            registry: self.registry,
574            next: Some(self.id()),
575
576            #[cfg(feature = "registry")]
577            filter: self.filter,
578        }
579    }
580
581    /// Returns a reference to this span's `Extensions`.
582    ///
583    /// The extensions may be used by `Subscriber`s to store additional data
584    /// describing the span.
585    #[cfg(feature = "std")]
586    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
587    pub fn extensions(&self) -> Extensions<'_> {
588        self.data.extensions()
589    }
590
591    /// Returns a mutable reference to this span's `Extensions`.
592    ///
593    /// The extensions may be used by `Subscriber`s to store additional data
594    /// describing the span.
595    #[cfg(feature = "std")]
596    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
597    pub fn extensions_mut(&self) -> ExtensionsMut<'_> {
598        self.data.extensions_mut()
599    }
600
601    #[cfg(all(feature = "registry", feature = "std"))]
602    pub(crate) fn try_with_filter(self, filter: FilterId) -> Option<Self> {
603        if self.is_enabled_for(filter) {
604            return Some(self.with_filter(filter));
605        }
606
607        None
608    }
609
610    #[inline]
611    #[cfg(all(feature = "registry", feature = "std"))]
612    pub(crate) fn is_enabled_for(&self, filter: FilterId) -> bool {
613        self.data.is_enabled_for(filter)
614    }
615
616    #[inline]
617    #[cfg(all(feature = "registry", feature = "std"))]
618    fn with_filter(self, filter: FilterId) -> Self {
619        Self { filter, ..self }
620    }
621}
622
623#[cfg(all(test, feature = "registry", feature = "std"))]
624mod tests {
625    use crate::{
626        prelude::*,
627        registry::LookupSpan,
628        subscribe::{Context, Subscribe},
629    };
630    use std::sync::{Arc, Mutex};
631    use tracing::{span, Collect};
632
633    #[test]
634    fn spanref_scope_iteration_order() {
635        let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
636
637        #[derive(Default)]
638        struct RecordingSubscriber {
639            last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
640        }
641
642        impl<S> Subscribe<S> for RecordingSubscriber
643        where
644            S: Collect + for<'lookup> LookupSpan<'lookup>,
645        {
646            fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
647                let span = ctx.span(id).unwrap();
648                let scope = span.scope().map(|span| span.name()).collect::<Vec<_>>();
649                *self.last_entered_scope.lock().unwrap() = scope;
650            }
651        }
652
653        let _guard = tracing::collect::set_default(crate::registry().with(RecordingSubscriber {
654            last_entered_scope: last_entered_scope.clone(),
655        }));
656
657        let _root = tracing::info_span!("root").entered();
658        assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
659        let _child = tracing::info_span!("child").entered();
660        assert_eq!(&*last_entered_scope.lock().unwrap(), &["child", "root"]);
661        let _leaf = tracing::info_span!("leaf").entered();
662        assert_eq!(
663            &*last_entered_scope.lock().unwrap(),
664            &["leaf", "child", "root"]
665        );
666    }
667
668    #[test]
669    fn spanref_scope_fromroot_iteration_order() {
670        let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
671
672        #[derive(Default)]
673        struct RecordingSubscriber {
674            last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
675        }
676
677        impl<S> Subscribe<S> for RecordingSubscriber
678        where
679            S: Collect + for<'lookup> LookupSpan<'lookup>,
680        {
681            fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
682                let span = ctx.span(id).unwrap();
683                let scope = span
684                    .scope()
685                    .from_root()
686                    .map(|span| span.name())
687                    .collect::<Vec<_>>();
688                *self.last_entered_scope.lock().unwrap() = scope;
689            }
690        }
691
692        let _guard = tracing::collect::set_default(crate::registry().with(RecordingSubscriber {
693            last_entered_scope: last_entered_scope.clone(),
694        }));
695
696        let _root = tracing::info_span!("root").entered();
697        assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
698        let _child = tracing::info_span!("child").entered();
699        assert_eq!(&*last_entered_scope.lock().unwrap(), &["root", "child",]);
700        let _leaf = tracing::info_span!("leaf").entered();
701        assert_eq!(
702            &*last_entered_scope.lock().unwrap(),
703            &["root", "child", "leaf"]
704        );
705    }
706}