🛈 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::{
237        boxed::Box,
238        sync::Arc
239    };
240
241    #[cfg(not(feature = "smallvec"))]
242    use alloc::vec::{self, Vec};
243    use core::{fmt,iter};
244
245    /// An iterator over the parents of a span, ordered from root to leaf.
246    ///
247    /// This is returned by the [`Scope::from_root`] method.
248    pub struct ScopeFromRoot<'a, R>
249    where
250        R: LookupSpan<'a>,
251    {
252        #[cfg(feature = "smallvec")]
253        spans: iter::Rev<smallvec::IntoIter<SpanRefVecArray<'a, R>>>,
254        #[cfg(not(feature = "smallvec"))]
255        spans: iter::Rev<vec::IntoIter<SpanRef<'a, R>>>,
256    }
257
258    #[cfg(feature = "smallvec")]
259    type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16];
260
261    impl<'a, S> LookupSpan<'a> for Arc<S>
262    where
263        S: LookupSpan<'a>,
264    {
265        type Data = <S as LookupSpan<'a>>::Data;
266
267        fn span_data(&'a self, id: &Id) -> Option<Self::Data> {
268            self.as_ref().span_data(id)
269        }
270
271        fn span(&'a self, id: &Id) -> Option<SpanRef<'a, Self>>
272        where
273            Self: Sized,
274        {
275            self.as_ref().span(id).map(
276                |SpanRef {
277                    registry: _,
278                    data,
279                    #[cfg(feature = "registry")]
280                    filter,
281                }| SpanRef {
282                    registry: self,
283                    data,
284                    #[cfg(feature = "registry")]
285                    filter,
286                },
287            )
288        }
289    }
290
291    impl<'a, S> LookupSpan<'a> for Box<S>
292    where
293        S: LookupSpan<'a>,
294    {
295        type Data = <S as LookupSpan<'a>>::Data;
296
297        fn span_data(&'a self, id: &Id) -> Option<Self::Data> {
298            self.as_ref().span_data(id)
299        }
300
301        fn span(&'a self, id: &Id) -> Option<SpanRef<'a, Self>>
302        where
303            Self: Sized,
304        {
305            self.as_ref().span(id).map(
306                |SpanRef {
307                    registry: _,
308                    data,
309                    #[cfg(feature = "registry")]
310                    filter,
311                }| SpanRef {
312                    registry: self,
313                    data,
314                    #[cfg(feature = "registry")]
315                    filter,
316                },
317            )
318        }
319    }
320
321    impl<'a, R> Scope<'a, R>
322    where
323        R: LookupSpan<'a>,
324    {
325        /// Flips the order of the iterator, so that it is ordered from root to leaf.
326        ///
327        /// The iterator will first return the root span, then that span's immediate child,
328        /// and so on until it finally returns the span that [`SpanRef::scope`] was called on.
329        ///
330        /// If any items were consumed from the [`Scope`] before calling this method then they
331        /// will *not* be returned from the [`ScopeFromRoot`].
332        ///
333        /// **Note**: this will allocate if there are many spans remaining, or if the
334        /// "smallvec" feature flag is not enabled.
335        #[allow(clippy::wrong_self_convention)]
336        pub fn from_root(self) -> ScopeFromRoot<'a, R> {
337            #[cfg(feature = "smallvec")]
338            type Buf<T> = smallvec::SmallVec<T>;
339            #[cfg(not(feature = "smallvec"))]
340            type Buf<T> = Vec<T>;
341            ScopeFromRoot {
342                spans: self.collect::<Buf<_>>().into_iter().rev(),
343            }
344        }
345    }
346
347    impl<'a, R> Iterator for ScopeFromRoot<'a, R>
348    where
349        R: LookupSpan<'a>,
350    {
351        type Item = SpanRef<'a, R>;
352
353        #[inline]
354        fn next(&mut self) -> Option<Self::Item> {
355            self.spans.next()
356        }
357
358        #[inline]
359        fn size_hint(&self) -> (usize, Option<usize>) {
360            self.spans.size_hint()
361        }
362    }
363
364    impl<'a, R> fmt::Debug for ScopeFromRoot<'a, R>
365    where
366        R: LookupSpan<'a>,
367    {
368        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369            f.pad("ScopeFromRoot { .. }")
370        }
371    }
372}
373
374impl<'a, R> Iterator for Scope<'a, R>
375where
376    R: LookupSpan<'a>,
377{
378    type Item = SpanRef<'a, R>;
379
380    fn next(&mut self) -> Option<Self::Item> {
381        loop {
382            let curr = self.registry.span(self.next.as_ref()?)?;
383
384            #[cfg(all(feature = "registry", feature = "std"))]
385            let curr = curr.with_filter(self.filter);
386            self.next = curr.data.parent().cloned();
387
388            // If the `Scope` is filtered, check if the current span is enabled
389            // by the selected filter ID.
390
391            #[cfg(all(feature = "registry", feature = "std"))]
392            {
393                if !curr.is_enabled_for(self.filter) {
394                    // The current span in the chain is disabled for this
395                    // filter. Try its parent.
396                    continue;
397                }
398            }
399
400            return Some(curr);
401        }
402    }
403}
404
405impl<'a, R> SpanRef<'a, R>
406where
407    R: LookupSpan<'a>,
408{
409    /// Returns this span's ID.
410    pub fn id(&self) -> Id {
411        self.data.id()
412    }
413
414    /// Returns a static reference to the span's metadata.
415    pub fn metadata(&self) -> &'static Metadata<'static> {
416        self.data.metadata()
417    }
418
419    /// Returns the span's name,
420    pub fn name(&self) -> &'static str {
421        self.data.metadata().name()
422    }
423
424    /// Returns a list of [fields] defined by the span.
425    ///
426    /// [fields]: tracing_core::field
427    pub fn fields(&self) -> &FieldSet {
428        self.data.metadata().fields()
429    }
430
431    /// Returns the ID of this span's parent, or `None` if this span is the root
432    /// of its trace tree.
433    #[deprecated(
434        note = "this method cannot properly support per-subscriber filtering, and may \
435            return the `Id` of a disabled span if per-subscriber filtering is in \
436            use. use `.parent().map(SpanRef::id)` instead.",
437        since = "0.2.21"
438    )]
439    pub fn parent_id(&self) -> Option<&Id> {
440        // XXX(eliza): this doesn't work with PSF because the ID is potentially
441        // borrowed from a parent we got from the registry, rather than from
442        // `self`, so we can't return a borrowed parent. so, right now, we just
443        // return the actual parent ID, and ignore PSF. which is not great.
444        //
445        // i think if we want this to play nice with PSF, we should just change
446        // it to return the `Id` by value instead of `&Id` (which we ought to do
447        // anyway since an `Id` is just a word) but that's a breaking change.
448        // alternatively, we could deprecate this method since it can't support
449        // PSF in its current form (which is what we would want to do if we want
450        // to release PSF in a minor version)...
451
452        // let mut id = self.data.parent()?;
453        // loop {
454        //     // Is this parent enabled by our filter?
455        //     if self
456        //         .filter
457        //         .map(|filter| self.registry.is_enabled_for(id, filter))
458        //         .unwrap_or(true)
459        //     {
460        //         return Some(id);
461        //     }
462        //     id = self.registry.span_data(id)?.parent()?;
463        // }
464        self.data.parent()
465    }
466
467    /// Returns a `SpanRef` describing this span's parent, or `None` if this
468    /// span is the root of its trace tree.
469    pub fn parent(&self) -> Option<Self> {
470        let id = self.data.parent()?;
471        let data = self.registry.span_data(id)?;
472
473        #[cfg(all(feature = "registry", feature = "std"))]
474        {
475            // move these into mut bindings if the registry feature is enabled,
476            // since they may be mutated in the loop.
477            let mut data = data;
478            loop {
479                // Is this parent enabled by our filter?
480                if data.is_enabled_for(self.filter) {
481                    return Some(Self {
482                        registry: self.registry,
483                        filter: self.filter,
484                        data,
485                    });
486                }
487
488                // It's not enabled. If the disabled span has a parent, try that!
489                let id = data.parent()?;
490                data = self.registry.span_data(id)?;
491            }
492        }
493
494        #[cfg(not(all(feature = "registry", feature = "std")))]
495        Some(Self {
496            registry: self.registry,
497            data,
498        })
499    }
500
501    /// Returns an iterator over all parents of this span, starting with this span,
502    /// ordered from leaf to root.
503    ///
504    /// The iterator will first return the span, then the span's immediate parent,
505    /// followed by that span's parent, and so on, until it reaches a root span.
506    ///
507    /// ```rust
508    /// use tracing::{span, Collect};
509    /// use tracing_subscriber::{
510    ///     subscribe::{Context, Subscribe},
511    ///     prelude::*,
512    ///     registry::LookupSpan,
513    /// };
514    ///
515    /// struct PrintingSubscriber;
516    /// impl<C> Subscribe<C> for PrintingSubscriber
517    /// where
518    ///     C: Collect + for<'lookup> LookupSpan<'lookup>,
519    /// {
520    ///     fn on_enter(&self, id: &span::Id, ctx: Context<C>) {
521    ///         let span = ctx.span(id).unwrap();
522    ///         let scope = span.scope().map(|span| span.name()).collect::<Vec<_>>();
523    ///         println!("Entering span: {:?}", scope);
524    ///     }
525    /// }
526    ///
527    /// tracing::collect::with_default(tracing_subscriber::registry().with(PrintingSubscriber), || {
528    ///     let _root = tracing::info_span!("root").entered();
529    ///     // Prints: Entering span: ["root"]
530    ///     let _child = tracing::info_span!("child").entered();
531    ///     // Prints: Entering span: ["child", "root"]
532    ///     let _leaf = tracing::info_span!("leaf").entered();
533    ///     // Prints: Entering span: ["leaf", "child", "root"]
534    /// });
535    /// ```
536    ///
537    /// If the opposite order (from the root to this span) is desired, calling [`Scope::from_root`] on
538    /// the returned iterator reverses the order.
539    ///
540    /// ```rust
541    /// # use tracing::{span, Collect};
542    /// # use tracing_subscriber::{
543    /// #     subscribe::{Context, Subscribe},
544    /// #     prelude::*,
545    /// #     registry::LookupSpan,
546    /// # };
547    /// # struct PrintingSubscriber;
548    /// impl<C> Subscribe<C> for PrintingSubscriber
549    /// where
550    ///     C: Collect + for<'lookup> LookupSpan<'lookup>,
551    /// {
552    ///     fn on_enter(&self, id: &span::Id, ctx: Context<C>) {
553    ///         let span = ctx.span(id).unwrap();
554    ///         let scope = span.scope().from_root().map(|span| span.name()).collect::<Vec<_>>();
555    ///         println!("Entering span: {:?}", scope);
556    ///     }
557    /// }
558    ///
559    /// tracing::collect::with_default(tracing_subscriber::registry().with(PrintingSubscriber), || {
560    ///     let _root = tracing::info_span!("root").entered();
561    ///     // Prints: Entering span: ["root"]
562    ///     let _child = tracing::info_span!("child").entered();
563    ///     // Prints: Entering span: ["root", "child"]
564    ///     let _leaf = tracing::info_span!("leaf").entered();
565    ///     // Prints: Entering span: ["root", "child", "leaf"]
566    /// });
567    /// ```
568    pub fn scope(&self) -> Scope<'a, R> {
569        Scope {
570            registry: self.registry,
571            next: Some(self.id()),
572
573            #[cfg(feature = "registry")]
574            filter: self.filter,
575        }
576    }
577
578    /// Returns a reference to this span's `Extensions`.
579    ///
580    /// The extensions may be used by `Subscriber`s to store additional data
581    /// describing the span.
582    #[cfg(feature = "std")]
583    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
584    pub fn extensions(&self) -> Extensions<'_> {
585        self.data.extensions()
586    }
587
588    /// Returns a mutable reference to this span's `Extensions`.
589    ///
590    /// The extensions may be used by `Subscriber`s to store additional data
591    /// describing the span.
592    #[cfg(feature = "std")]
593    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
594    pub fn extensions_mut(&self) -> ExtensionsMut<'_> {
595        self.data.extensions_mut()
596    }
597
598    #[cfg(all(feature = "registry", feature = "std"))]
599    pub(crate) fn try_with_filter(self, filter: FilterId) -> Option<Self> {
600        if self.is_enabled_for(filter) {
601            return Some(self.with_filter(filter));
602        }
603
604        None
605    }
606
607    #[inline]
608    #[cfg(all(feature = "registry", feature = "std"))]
609    pub(crate) fn is_enabled_for(&self, filter: FilterId) -> bool {
610        self.data.is_enabled_for(filter)
611    }
612
613    #[inline]
614    #[cfg(all(feature = "registry", feature = "std"))]
615    fn with_filter(self, filter: FilterId) -> Self {
616        Self { filter, ..self }
617    }
618}
619
620#[cfg(all(test, feature = "registry", feature = "std"))]
621mod tests {
622    use crate::{
623        prelude::*,
624        registry::LookupSpan,
625        subscribe::{Context, Subscribe},
626    };
627    use std::sync::{Arc, Mutex};
628    use tracing::{span, Collect};
629
630    #[test]
631    fn spanref_scope_iteration_order() {
632        let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
633
634        #[derive(Default)]
635        struct RecordingSubscriber {
636            last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
637        }
638
639        impl<S> Subscribe<S> for RecordingSubscriber
640        where
641            S: Collect + for<'lookup> LookupSpan<'lookup>,
642        {
643            fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
644                let span = ctx.span(id).unwrap();
645                let scope = span.scope().map(|span| span.name()).collect::<Vec<_>>();
646                *self.last_entered_scope.lock().unwrap() = scope;
647            }
648        }
649
650        let _guard = tracing::collect::set_default(crate::registry().with(RecordingSubscriber {
651            last_entered_scope: last_entered_scope.clone(),
652        }));
653
654        let _root = tracing::info_span!("root").entered();
655        assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
656        let _child = tracing::info_span!("child").entered();
657        assert_eq!(&*last_entered_scope.lock().unwrap(), &["child", "root"]);
658        let _leaf = tracing::info_span!("leaf").entered();
659        assert_eq!(
660            &*last_entered_scope.lock().unwrap(),
661            &["leaf", "child", "root"]
662        );
663    }
664
665    #[test]
666    fn spanref_scope_fromroot_iteration_order() {
667        let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
668
669        #[derive(Default)]
670        struct RecordingSubscriber {
671            last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
672        }
673
674        impl<S> Subscribe<S> for RecordingSubscriber
675        where
676            S: Collect + for<'lookup> LookupSpan<'lookup>,
677        {
678            fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
679                let span = ctx.span(id).unwrap();
680                let scope = span
681                    .scope()
682                    .from_root()
683                    .map(|span| span.name())
684                    .collect::<Vec<_>>();
685                *self.last_entered_scope.lock().unwrap() = scope;
686            }
687        }
688
689        let _guard = tracing::collect::set_default(crate::registry().with(RecordingSubscriber {
690            last_entered_scope: last_entered_scope.clone(),
691        }));
692
693        let _root = tracing::info_span!("root").entered();
694        assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
695        let _child = tracing::info_span!("child").entered();
696        assert_eq!(&*last_entered_scope.lock().unwrap(), &["root", "child",]);
697        let _leaf = tracing::info_span!("leaf").entered();
698        assert_eq!(
699            &*last_entered_scope.lock().unwrap(),
700            &["root", "child", "leaf"]
701        );
702    }
703}