Skip to main content

tracing_core/
callsite.rs

1//! Callsites represent the source locations from which spans or events
2//! originate.
3//!
4//! # What Are Callsites?
5//!
6//! Every span or event in `tracing` is associated with a [`Callsite`]. A
7//! callsite is a small `static` value that is responsible for the following:
8//!
9//! * Storing the span or event's [`Metadata`],
10//! * Uniquely [identifying](Identifier) the span or event definition,
11//! * Caching the subscriber's [`Interest`][^1] in that span or event, to avoid
12//!   re-evaluating filters.
13//!
14//! # Registering Callsites
15//!
16//! When a span or event is recorded for the first time, its callsite
17//! [`register`]s itself with the global callsite registry. Registering a
18//! callsite calls the [`Subscriber::register_callsite`][`register_callsite`]
19//! method with that callsite's [`Metadata`] on every currently active
20//! subscriber. This serves two primary purposes: informing subscribers of the
21//! callsite's existence, and performing static filtering.
22//!
23//! ## Callsite Existence
24//!
25//! If a [`Subscriber`] implementation wishes to allocate storage for each
26//! unique span/event location in the program, or pre-compute some value
27//! that will be used to record that span or event in the future, it can
28//! do so in its [`register_callsite`] method.
29//!
30//! ## Performing Static Filtering
31//!
32//! The [`register_callsite`] method returns an [`Interest`] value,
33//! which indicates that the subscriber either [always] wishes to record
34//! that span or event, [sometimes] wishes to record it based on a
35//! dynamic filter evaluation, or [never] wishes to record it.
36//!
37//! When registering a new callsite, the [`Interest`]s returned by every
38//! currently active subscriber are combined, and the result is stored at
39//! each callsite. This way, when the span or event occurs in the
40//! future, the cached [`Interest`] value can be checked efficiently
41//! to determine if the span or event should be recorded, without
42//! needing to perform expensive filtering (i.e. calling the
43//! [`Subscriber::enabled`] method every time a span or event occurs).
44//!
45//! ### Rebuilding Cached Interest
46//!
47//! When a new [`Dispatch`] is created (i.e. a new subscriber becomes
48//! active), any previously cached [`Interest`] values are re-evaluated
49//! for all callsites in the program. This way, if the new subscriber
50//! will enable a callsite that was not previously enabled, the
51//! [`Interest`] in that callsite is updated. Similarly, when a
52//! subscriber is dropped, the interest cache is also re-evaluated, so
53//! that any callsites enabled only by that subscriber are disabled.
54//!
55//! In addition, the [`rebuild_interest_cache`] function in this module can be
56//! used to manually invalidate all cached interest and re-register those
57//! callsites. This function is useful in situations where a subscriber's
58//! interest can change, but it does so relatively infrequently. The subscriber
59//! may wish for its interest to be cached most of the time, and return
60//! [`Interest::always`][always] or [`Interest::never`][never] in its
61//! [`register_callsite`] method, so that its [`Subscriber::enabled`] method
62//! doesn't need to be evaluated every time a span or event is recorded.
63//! However, when the configuration changes, the subscriber can call
64//! [`rebuild_interest_cache`] to re-evaluate the entire interest cache with its
65//! new configuration. This is a relatively costly operation, but if the
66//! configuration changes infrequently, it may be more efficient than calling
67//! [`Subscriber::enabled`] frequently.
68//!
69//! # Implementing Callsites
70//!
71//! In most cases, instrumenting code using `tracing` should *not* require
72//! implementing the [`Callsite`] trait directly. When using the [`tracing`
73//! crate's macros][macros] or the [`#[instrument]` attribute][instrument], a
74//! `Callsite` is automatically generated.
75//!
76//! However, code which provides alternative forms of `tracing` instrumentation
77//! may need to interact with the callsite system directly. If
78//! instrumentation-side code needs to produce a `Callsite` to emit spans or
79//! events, the [`DefaultCallsite`] struct provided in this module is a
80//! ready-made `Callsite` implementation that is suitable for most uses. When
81//! possible, the use of `DefaultCallsite` should be preferred over implementing
82//! [`Callsite`] for user types, as `DefaultCallsite` may benefit from
83//! additional performance optimizations.
84//!
85//! [^1]: Returned by the [`Subscriber::register_callsite`][`register_callsite`]
86//!     method.
87//!
88//! [`Metadata`]: crate::metadata::Metadata
89//! [`Interest`]: crate::subscriber::Interest
90//! [`Subscriber`]: crate::subscriber::Subscriber
91//! [`register_callsite`]: crate::subscriber::Subscriber::register_callsite
92//! [`Subscriber::enabled`]: crate::subscriber::Subscriber::enabled
93//! [always]: crate::subscriber::Interest::always
94//! [sometimes]: crate::subscriber::Interest::sometimes
95//! [never]: crate::subscriber::Interest::never
96//! [`Dispatch`]: crate::dispatcher::Dispatch
97//! [macros]: https://docs.rs/tracing/latest/tracing/#macros
98//! [instrument]: https://docs.rs/tracing/latest/tracing/attr.instrument.html
99
100use alloc::vec::Vec;
101use core::{
102    any::TypeId,
103    fmt,
104    hash::{Hash, Hasher},
105    ptr,
106    sync::atomic::{AtomicBool, AtomicPtr, AtomicU8, Ordering},
107};
108
109use self::dispatchers::Dispatchers;
110use crate::{
111    dispatcher::Dispatch,
112    metadata::{LevelFilter, Metadata},
113    subscriber::Interest,
114    sync::Mutex,
115};
116
117/// Trait implemented by callsites.
118///
119/// These functions are only intended to be called by the callsite registry, which
120/// correctly handles determining the common interest between all subscribers.
121///
122/// See the [module-level documentation](crate::callsite) for details on
123/// callsites.
124pub trait Callsite: Sync {
125    /// Sets the [`Interest`] for this callsite.
126    ///
127    /// See the [documentation on callsite interest caching][cache-docs] for
128    /// details.
129    ///
130    /// [`Interest`]: super::subscriber::Interest
131    /// [cache-docs]: crate::callsite#performing-static-filtering
132    fn set_interest(&self, interest: Interest);
133
134    /// Returns the [metadata] associated with the callsite.
135    ///
136    /// <div class="example-wrap" style="display:inline-block">
137    /// <pre class="ignore" style="white-space:normal;font:inherit;">
138    ///
139    /// **Note:** Implementations of this method should not produce [`Metadata`]
140    /// that share the same callsite [`Identifier`] but otherwise differ in any
141    /// way (e.g., have different `name`s).
142    ///
143    /// </pre></div>
144    ///
145    /// [metadata]: super::metadata::Metadata
146    fn metadata(&self) -> &Metadata<'_>;
147
148    /// This method is an *internal implementation detail* of `tracing-core`. It
149    /// is *not* intended to be called or overridden from downstream code.
150    ///
151    /// The `Private` type can only be constructed from within `tracing-core`.
152    /// Because this method takes a `Private` as an argument, it cannot be
153    /// called from (safe) code external to `tracing-core`. Because it must
154    /// *return* a `Private`, the only valid implementation possible outside of
155    /// `tracing-core` would have to always unconditionally panic.
156    ///
157    /// THIS IS BY DESIGN. There is currently no valid reason for code outside
158    /// of `tracing-core` to override this method.
159    // TODO(eliza): this could be used to implement a public downcasting API
160    // for `&dyn Callsite`s in the future.
161    #[doc(hidden)]
162    #[inline]
163    fn private_type_id(&self, _: private::Private<()>) -> private::Private<TypeId>
164    where
165        Self: 'static,
166    {
167        private::Private(TypeId::of::<Self>())
168    }
169}
170
171/// Uniquely identifies a [`Callsite`]
172///
173/// Two `Identifier`s are equal if they both refer to the same callsite.
174///
175/// [`Callsite`]: super::callsite::Callsite
176#[derive(Clone)]
177pub struct Identifier(
178    /// **Warning**: The fields on this type are currently `pub` because it must
179    /// be able to be constructed statically by macros. However, when `const
180    /// fn`s are available on stable Rust, this will no longer be necessary.
181    /// Thus, these fields are *not* considered stable public API, and they may
182    /// change warning. Do not rely on any fields on `Identifier`. When
183    /// constructing new `Identifier`s, use the `identify_callsite!` macro
184    /// instead.
185    #[doc(hidden)]
186    pub &'static dyn Callsite,
187);
188
189/// A default [`Callsite`] implementation.
190#[derive(Debug)]
191pub struct DefaultCallsite {
192    interest: AtomicU8,
193    registration: AtomicU8,
194    meta: &'static Metadata<'static>,
195    next: AtomicPtr<Self>,
196}
197
198/// Clear and reregister interest on every [`Callsite`]
199///
200/// This function is intended for runtime reconfiguration of filters on traces
201/// when the filter recalculation is much less frequent than trace events are.
202/// The alternative is to have the [`Subscriber`] that supports runtime
203/// reconfiguration of filters always return [`Interest::sometimes()`] so that
204/// [`enabled`] is evaluated for every event.
205///
206/// This function will also re-compute the global maximum level as determined by
207/// the [`max_level_hint`] method. If a [`Subscriber`]
208/// implementation changes the value returned by its `max_level_hint`
209/// implementation at runtime, then it **must** call this function after that
210/// value changes, in order for the change to be reflected.
211///
212/// See the [documentation on callsite interest caching][cache-docs] for
213/// additional information on this function's usage.
214///
215/// [`max_level_hint`]: super::subscriber::Subscriber::max_level_hint
216/// [`Callsite`]: super::callsite::Callsite
217/// [`enabled`]: super::subscriber::Subscriber#tymethod.enabled
218/// [`Interest::sometimes()`]: super::subscriber::Interest::sometimes
219/// [`Subscriber`]: super::subscriber::Subscriber
220/// [cache-docs]: crate::callsite#rebuilding-cached-interest
221pub fn rebuild_interest_cache() {
222    CALLSITES.rebuild_interest(DISPATCHERS.rebuilder());
223}
224
225/// Register a new [`Callsite`] with the global registry.
226///
227/// This should be called once per callsite after the callsite has been
228/// constructed.
229///
230/// See the [documentation on callsite registration][reg-docs] for details
231/// on the global callsite registry.
232///
233/// [`Callsite`]: crate::callsite::Callsite
234/// [reg-docs]: crate::callsite#registering-callsites
235pub fn register(callsite: &'static dyn Callsite) {
236    // Is this a `DefaultCallsite`? If so, use the fancy linked list!
237    if callsite.private_type_id(private::Private(())).0 == TypeId::of::<DefaultCallsite>() {
238        let callsite = unsafe {
239            // Safety: the pointer cast is safe because the type id of the
240            // provided callsite matches that of the target type for the cast
241            // (`DefaultCallsite`). Because user implementations of `Callsite`
242            // cannot override `private_type_id`, we can trust that the callsite
243            // is not lying about its type ID.
244            &*(callsite as *const dyn Callsite as *const DefaultCallsite)
245        };
246        CALLSITES.push_default(callsite);
247    } else {
248        CALLSITES.push_dyn(callsite);
249    }
250
251    rebuild_callsite_interest(callsite, &DISPATCHERS.rebuilder());
252}
253
254static CALLSITES: Callsites = Callsites {
255    list_head: AtomicPtr::new(ptr::null_mut()),
256    has_locked_callsites: AtomicBool::new(false),
257};
258
259static DISPATCHERS: Dispatchers = Dispatchers::new();
260
261static LOCKED_CALLSITES: Mutex<Vec<&'static dyn Callsite>> = Mutex::new(Vec::new());
262
263struct Callsites {
264    list_head: AtomicPtr<DefaultCallsite>,
265    has_locked_callsites: AtomicBool,
266}
267
268// === impl DefaultCallsite ===
269
270impl DefaultCallsite {
271    const UNREGISTERED: u8 = 0;
272    const REGISTERING: u8 = 1;
273    const REGISTERED: u8 = 2;
274
275    const INTEREST_NEVER: u8 = 0;
276    const INTEREST_SOMETIMES: u8 = 1;
277    const INTEREST_ALWAYS: u8 = 2;
278
279    /// Returns a new `DefaultCallsite` with the specified `Metadata`.
280    pub const fn new(meta: &'static Metadata<'static>) -> Self {
281        Self {
282            interest: AtomicU8::new(0xFF),
283            meta,
284            next: AtomicPtr::new(ptr::null_mut()),
285            registration: AtomicU8::new(Self::UNREGISTERED),
286        }
287    }
288
289    /// Registers this callsite with the global callsite registry.
290    ///
291    /// If the callsite is already registered, this does nothing. When using
292    /// [`DefaultCallsite`], this method should be preferred over
293    /// [`tracing_core::callsite::register`], as it ensures that the callsite is
294    /// only registered a single time.
295    ///
296    /// Other callsite implementations will generally ensure that
297    /// callsites are not re-registered through another mechanism.
298    ///
299    /// See the [documentation on callsite registration][reg-docs] for details
300    /// on the global callsite registry.
301    ///
302    /// [`tracing_core::callsite::register`]: crate::callsite::register
303    /// [reg-docs]: crate::callsite#registering-callsites
304    #[inline(never)]
305    // This only happens once (or if the cached interest value was corrupted).
306    #[cold]
307    pub fn register(&'static self) -> Interest {
308        // Attempt to advance the registration state to `REGISTERING`...
309        match self.registration.compare_exchange(
310            Self::UNREGISTERED,
311            Self::REGISTERING,
312            Ordering::AcqRel,
313            Ordering::Acquire,
314        ) {
315            Ok(_) => {
316                // Okay, we advanced the state, try to register the callsite.
317                CALLSITES.push_default(self);
318                rebuild_callsite_interest(self, &DISPATCHERS.rebuilder());
319                self.registration.store(Self::REGISTERED, Ordering::Release);
320            }
321            // Great, the callsite is already registered! Just load its
322            // previous cached interest.
323            Err(Self::REGISTERED) => {}
324            // Someone else is registering...
325            Err(_state) => {
326                debug_assert_eq!(
327                    _state,
328                    Self::REGISTERING,
329                    "weird callsite registration state"
330                );
331                // Just hit `enabled` this time.
332                return Interest::sometimes();
333            }
334        }
335
336        match self.interest.load(Ordering::Relaxed) {
337            Self::INTEREST_NEVER => Interest::never(),
338            Self::INTEREST_ALWAYS => Interest::always(),
339            _ => Interest::sometimes(),
340        }
341    }
342
343    /// Returns the callsite's cached `Interest`, or registers it for the
344    /// first time if it has not yet been registered.
345    #[inline]
346    pub fn interest(&'static self) -> Interest {
347        match self.interest.load(Ordering::Relaxed) {
348            Self::INTEREST_NEVER => Interest::never(),
349            Self::INTEREST_SOMETIMES => Interest::sometimes(),
350            Self::INTEREST_ALWAYS => Interest::always(),
351            _ => self.register(),
352        }
353    }
354}
355
356impl Callsite for DefaultCallsite {
357    fn set_interest(&self, interest: Interest) {
358        let interest = match () {
359            _ if interest.is_never() => Self::INTEREST_NEVER,
360            _ if interest.is_always() => Self::INTEREST_ALWAYS,
361            _ => Self::INTEREST_SOMETIMES,
362        };
363        self.interest.store(interest, Ordering::SeqCst);
364    }
365
366    #[inline(always)]
367    fn metadata(&self) -> &Metadata<'static> {
368        self.meta
369    }
370}
371
372// ===== impl Identifier =====
373
374impl PartialEq for Identifier {
375    fn eq(&self, other: &Identifier) -> bool {
376        core::ptr::eq(
377            self.0 as *const _ as *const (),
378            other.0 as *const _ as *const (),
379        )
380    }
381}
382
383impl Eq for Identifier {}
384
385impl fmt::Debug for Identifier {
386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387        write!(f, "Identifier({:p})", self.0)
388    }
389}
390
391impl Hash for Identifier {
392    fn hash<H>(&self, state: &mut H)
393    where
394        H: Hasher,
395    {
396        (self.0 as *const dyn Callsite).hash(state)
397    }
398}
399
400// === impl Callsites ===
401
402impl Callsites {
403    /// Rebuild `Interest`s for all callsites in the registry.
404    ///
405    /// This also re-computes the max level hint.
406    fn rebuild_interest(&self, dispatchers: dispatchers::Rebuilder<'_>) {
407        let mut max_level = LevelFilter::OFF;
408        dispatchers.for_each(|dispatch| {
409            // If the subscriber did not provide a max level hint, assume
410            // that it may enable every level.
411            let level_hint = dispatch.max_level_hint().unwrap_or(LevelFilter::TRACE);
412            if level_hint > max_level {
413                max_level = level_hint;
414            }
415        });
416
417        self.for_each(|callsite| {
418            rebuild_callsite_interest(callsite, &dispatchers);
419        });
420        LevelFilter::set_max(max_level);
421    }
422
423    /// Push a `dyn Callsite` trait object to the callsite registry.
424    ///
425    /// This will attempt to lock the callsites vector.
426    fn push_dyn(&self, callsite: &'static dyn Callsite) {
427        let mut lock = LOCKED_CALLSITES.lock().unwrap();
428        self.has_locked_callsites.store(true, Ordering::Release);
429        lock.push(callsite);
430    }
431
432    /// Push a `DefaultCallsite` to the callsite registry.
433    ///
434    /// If we know the callsite being pushed is a `DefaultCallsite`, we can push
435    /// it to the linked list without having to acquire a lock.
436    fn push_default(&self, callsite: &'static DefaultCallsite) {
437        let mut head = self.list_head.load(Ordering::Acquire);
438
439        loop {
440            callsite.next.store(head, Ordering::Release);
441
442            assert_ne!(
443                callsite as *const _, head,
444                "Attempted to register a `DefaultCallsite` that already exists! \
445                This will cause an infinite loop when attempting to read from the \
446                callsite cache. This is likely a bug! You should only need to call \
447                `DefaultCallsite::register` once per `DefaultCallsite`."
448            );
449
450            match self.list_head.compare_exchange(
451                head,
452                callsite as *const _ as *mut _,
453                Ordering::AcqRel,
454                Ordering::Acquire,
455            ) {
456                Ok(_) => {
457                    break;
458                }
459                Err(current) => head = current,
460            }
461        }
462    }
463
464    /// Invokes the provided closure `f` with each callsite in the registry.
465    fn for_each(&self, mut f: impl FnMut(&'static dyn Callsite)) {
466        let mut head = self.list_head.load(Ordering::Acquire);
467
468        while let Some(cs) = unsafe { head.as_ref() } {
469            f(cs);
470
471            head = cs.next.load(Ordering::Acquire);
472        }
473
474        if self.has_locked_callsites.load(Ordering::Acquire) {
475            let locked = LOCKED_CALLSITES.lock().unwrap();
476            for &cs in locked.iter() {
477                f(cs);
478            }
479        }
480    }
481}
482
483pub(crate) fn register_dispatch(dispatch: &Dispatch) {
484    let dispatchers = DISPATCHERS.register_dispatch(dispatch);
485    dispatch.subscriber().on_register_dispatch(dispatch);
486    CALLSITES.rebuild_interest(dispatchers);
487}
488
489fn rebuild_callsite_interest(
490    callsite: &'static dyn Callsite,
491    dispatchers: &dispatchers::Rebuilder<'_>,
492) {
493    let meta = callsite.metadata();
494
495    let mut interest = None;
496    dispatchers.for_each(|dispatch| {
497        let this_interest = dispatch.register_callsite(meta);
498        interest = match interest.take() {
499            None => Some(this_interest),
500            Some(that_interest) => Some(that_interest.and(this_interest)),
501        }
502    });
503
504    let interest = interest.unwrap_or_else(Interest::never);
505    callsite.set_interest(interest)
506}
507
508mod private {
509    /// Don't call this function, it's private.
510    #[allow(missing_debug_implementations)]
511    pub struct Private<T>(pub(crate) T);
512}
513
514#[cfg(feature = "std")]
515mod dispatchers {
516    use crate::dispatcher;
517    use alloc::vec::Vec;
518    use std::sync::{
519        atomic::{AtomicBool, Ordering},
520        RwLock, RwLockReadGuard, RwLockWriteGuard,
521    };
522
523    pub(super) struct Dispatchers {
524        has_just_one: AtomicBool,
525    }
526
527    static LOCKED_DISPATCHERS: RwLock<Vec<dispatcher::Registrar>> = RwLock::new(Vec::new());
528
529    pub(super) enum Rebuilder<'a> {
530        JustOne,
531        Read(RwLockReadGuard<'a, Vec<dispatcher::Registrar>>),
532        Write(RwLockWriteGuard<'a, Vec<dispatcher::Registrar>>),
533    }
534
535    impl Dispatchers {
536        pub(super) const fn new() -> Self {
537            Self {
538                has_just_one: AtomicBool::new(true),
539            }
540        }
541
542        pub(super) fn rebuilder(&self) -> Rebuilder<'_> {
543            if self.has_just_one.load(Ordering::SeqCst) {
544                return Rebuilder::JustOne;
545            }
546            Rebuilder::Read(LOCKED_DISPATCHERS.read().unwrap())
547        }
548
549        pub(super) fn register_dispatch(&self, dispatch: &dispatcher::Dispatch) -> Rebuilder<'_> {
550            let mut dispatchers = LOCKED_DISPATCHERS.write().unwrap();
551            dispatchers.retain(|d| d.upgrade().is_some());
552            dispatchers.push(dispatch.registrar());
553            self.has_just_one
554                .store(dispatchers.len() <= 1, Ordering::SeqCst);
555            Rebuilder::Write(dispatchers)
556        }
557    }
558
559    impl Rebuilder<'_> {
560        pub(super) fn for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch)) {
561            let iter = match self {
562                Rebuilder::JustOne => {
563                    dispatcher::get_default(f);
564                    return;
565                }
566                Rebuilder::Read(vec) => vec.iter(),
567                Rebuilder::Write(vec) => vec.iter(),
568            };
569            iter.filter_map(dispatcher::Registrar::upgrade)
570                .for_each(|dispatch| f(&dispatch))
571        }
572    }
573}
574
575#[cfg(not(feature = "std"))]
576mod dispatchers {
577    use crate::dispatcher;
578
579    pub(super) struct Dispatchers(());
580    pub(super) struct Rebuilder<'a>(Option<&'a dispatcher::Dispatch>);
581
582    impl Dispatchers {
583        pub(super) const fn new() -> Self {
584            Self(())
585        }
586
587        pub(super) fn rebuilder(&self) -> Rebuilder<'_> {
588            Rebuilder(None)
589        }
590
591        pub(super) fn register_dispatch<'dispatch>(
592            &self,
593            dispatch: &'dispatch dispatcher::Dispatch,
594        ) -> Rebuilder<'dispatch> {
595            // nop; on no_std, there can only ever be one dispatcher
596            Rebuilder(Some(dispatch))
597        }
598    }
599
600    impl Rebuilder<'_> {
601        #[inline]
602        pub(super) fn for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch)) {
603            if let Some(dispatch) = self.0 {
604                // we are rebuilding the interest cache because a new dispatcher
605                // is about to be set. on `no_std`, this should only happen
606                // once, because the new dispatcher will be the global default.
607                f(dispatch)
608            } else {
609                // otherwise, we are rebuilding the cache because the subscriber
610                // configuration changed, so use the global default.
611                // on no_std, there can only ever be one dispatcher
612                dispatcher::get_default(f)
613            }
614        }
615    }
616}