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}