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