πŸ›ˆ Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

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

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 collector's [`Interest`][^1] in that span or event, to avoid
12//!   re-evaluating filters,
13//! * Storing a [`Registration`] that allows the callsite to be part of a global
14//!   list of all callsites in the program.
15//!
16//! # Registering Callsites
17//!
18//! When a span or event is recorded for the first time, its callsite
19//! [`register`]s itself with the global callsite registry. Registering a
20//! callsite calls the [`Collect::register_callsite`][`register_callsite`]
21//! method with that callsite's [`Metadata`] on every currently active
22//! collector. This serves two primary purposes: informing collectors of the
23//! callsite's existence, and performing static filtering.
24//!
25//! ## Callsite Existence
26//!
27//! If a [`Collect`] implementation wishes to allocate storage for each
28//! unique span/event location in the program, or pre-compute some value
29//! that will be used to record that span or event in the future, it can
30//! do so in its [`register_callsite`] method.
31//!
32//! ## Performing Static Filtering
33//!
34//! The [`register_callsite`] method returns an [`Interest`] value,
35//! which indicates that the collector either [always] wishes to record
36//! that span or event, [sometimes] wishes to record it based on a
37//! dynamic filter evaluation, or [never] wishes to record it.
38//!
39//! When registering a new callsite, the [`Interest`]s returned by every
40//! currently active collector are combined, and the result is stored at
41//! each callsite. This way, when the span or event occurs in the
42//! future, the cached [`Interest`] value can be checked efficiently
43//! to determine if the span or event should be recorded, without
44//! needing to perform expensive filtering (i.e. calling the
45//! [`Collect::enabled`] method every time a span or event occurs).
46//!
47//! ### Rebuilding Cached Interest
48//!
49//! When a new [`Dispatch`] is created (i.e. a new collector becomes
50//! active), any previously cached [`Interest`] values are re-evaluated
51//! for all callsites in the program. This way, if the new collector
52//! will enable a callsite that was not previously enabled, the
53//! [`Interest`] in that callsite is updated. Similarly, when a
54//! collector is dropped, the interest cache is also re-evaluated, so
55//! that any callsites enabled only by that collector are disabled.
56//!
57//! In addition, the [`rebuild_interest_cache`] function in this module can be
58//! used to manually invalidate all cached interest and re-register those
59//! callsites. This function is useful in situations where a collector's
60//! interest can change, but it does so relatively infrequently. The collector
61//! may wish for its interest to be cached most of the time, and return
62//! [`Interest::always`][always] or [`Interest::never`][never] in its
63//! [`register_callsite`] method, so that its [`Collect::enabled`] method
64//! doesn't need to be evaluated every time a span or event is recorded.
65//! However, when the configuration changes, the collector can call
66//! [`rebuild_interest_cache`] to re-evaluate the entire interest cache with its
67//! new configuration. This is a relatively costly operation, but if the
68//! configuration changes infrequently, it may be more efficient than calling
69//! [`Collect::enabled`] frequently.
70//!
71//! [^1]: Returned by the [`Collect::register_callsite`][`register_callsite`]
72//!     method.
73//!
74//! [`Metadata`]: crate::metadata::Metadata
75//! [`Interest`]: crate::collect::Interest
76//! [`Collect`]: crate::collect::Collect
77//! [`register_callsite`]: crate::collect::Collect::register_callsite
78//! [`Collect::enabled`]: crate::collect::Collect::enabled
79//! [always]: crate::collect::Interest::always
80//! [sometimes]: crate::collect::Interest::sometimes
81//! [never]: crate::collect::Interest::never
82//! [`Dispatch`]: crate::dispatch::Dispatch
83use crate::{
84    collect::Interest,
85    dispatch::{self, Dispatch},
86    metadata::{LevelFilter, Metadata},
87};
88use core::{
89    fmt,
90    hash::{Hash, Hasher},
91    ptr,
92    sync::atomic::Ordering,
93};
94
95#[cfg(feature = "portable-atomic")]
96use portable_atomic::AtomicPtr;
97
98#[cfg(not(feature = "portable-atomic"))]
99use core::sync::atomic::AtomicPtr;
100
101type Callsites = LinkedList;
102
103/// Trait implemented by callsites.
104///
105/// These functions are only intended to be called by the callsite registry, which
106/// correctly handles determining the common interest between all collectors.
107///
108/// See the [module-level documentation](crate::callsite) for details on
109/// callsites.
110pub trait Callsite: Sync {
111    /// Sets the [`Interest`] for this callsite.
112    ///
113    /// See the [documentation on callsite interest caching][cache-docs] for
114    /// details.
115    ///
116    /// [`Interest`]: super::collect::Interest
117    /// [cache-docs]: crate::callsite#performing-static-filtering
118    fn set_interest(&self, interest: Interest);
119
120    /// Returns the [metadata] associated with the callsite.
121    ///
122    /// <div class="example-wrap" style="display:inline-block">
123    /// <pre class="ignore" style="white-space:normal;font:inherit;">
124    ///
125    /// **Note:** Implementations of this method should not produce [`Metadata`]
126    /// that share the same callsite [`Identifier`] but otherwise differ in any
127    /// way (e.g., have different `name`s).
128    ///
129    /// </pre></div>
130    ///
131    /// [metadata]: super::metadata::Metadata
132    fn metadata(&self) -> &Metadata<'_>;
133}
134
135/// Uniquely identifies a [`Callsite`]
136///
137/// Two `Identifier`s are equal if they both refer to the same callsite.
138///
139/// [`Callsite`]: super::callsite::Callsite
140#[derive(Clone)]
141pub struct Identifier(
142    /// **Warning**: The fields on this type are currently `pub` because it must
143    /// be able to be constructed statically by macros. However, when `const
144    /// fn`s are available on stable Rust, this will no longer be necessary.
145    /// Thus, these fields are *not* considered stable public API, and they may
146    /// change warning. Do not rely on any fields on `Identifier`. When
147    /// constructing new `Identifier`s, use the `identify_callsite!` macro or
148    /// the `Callsite::id` function instead.
149    // TODO: When `Callsite::id` is a const fn, this need no longer be `pub`.
150    #[doc(hidden)]
151    pub &'static dyn Callsite,
152);
153
154/// A registration with the callsite registry.
155///
156/// Every [`Callsite`] implementation must provide a `&'static Registration`
157/// when calling [`register`] to add itself to the global callsite registry.
158///
159/// See [the documentation on callsite registration][registry-docs] for details
160/// on how callsites are registered.
161///
162/// [`Callsite`]: crate::callsite::Callsite
163/// [`register`]: crate::callsite::register
164/// [registry-docs]: crate::callsite#registering-callsites
165pub struct Registration<T = &'static dyn Callsite> {
166    callsite: T,
167    next: AtomicPtr<Registration<T>>,
168}
169
170pub(crate) use self::inner::register_dispatch;
171pub use self::inner::{rebuild_interest_cache, register};
172
173#[cfg(feature = "std")]
174mod inner {
175    use super::*;
176    use once_cell::sync::Lazy;
177    use std::sync::RwLock;
178    use std::vec::Vec;
179
180    type Dispatchers = Vec<dispatch::Registrar>;
181
182    struct Registry {
183        callsites: Callsites,
184        dispatchers: RwLock<Dispatchers>,
185    }
186
187    static REGISTRY: Lazy<Registry> = Lazy::new(|| Registry {
188        callsites: LinkedList::new(),
189        dispatchers: RwLock::new(Vec::new()),
190    });
191
192    /// Clear and reregister interest on every [`Callsite`]
193    ///
194    /// This function is intended for runtime reconfiguration of filters on traces
195    /// when the filter recalculation is much less frequent than trace events are.
196    /// The alternative is to have the [`Collect`] that supports runtime
197    /// reconfiguration of filters always return [`Interest::sometimes()`] so that
198    /// [`enabled`] is evaluated for every event.
199    ///
200    /// This function will also re-compute the global maximum level as determined by
201    /// the [`max_level_hint`] method. If a [`Collect`]
202    /// implementation changes the value returned by its `max_level_hint`
203    /// implementation at runtime, then it **must** call this function after that
204    /// value changes, in order for the change to be reflected.
205    ///
206    /// See the [documentation on callsite interest caching][cache-docs] for
207    /// additional information on this function's usage.
208    ///
209    /// [`max_level_hint`]: crate::collect::Collect::max_level_hint
210    /// [`Callsite`]: crate::callsite::Callsite
211    /// [`enabled`]: crate::collect::Collect::enabled
212    /// [`Interest::sometimes()`]: crate::collect::Interest::sometimes
213    /// [`Collect`]: crate::collect::Collect
214    /// [cache-docs]: crate::callsite#rebuilding-cached-interest
215    pub fn rebuild_interest_cache() {
216        let mut dispatchers = REGISTRY.dispatchers.write().unwrap();
217        let callsites = &REGISTRY.callsites;
218        rebuild_interest(callsites, &mut dispatchers);
219    }
220
221    /// Register a new [`Callsite`] with the global registry.
222    ///
223    /// This should be called once per callsite after the callsite has been
224    /// constructed.
225    ///
226    /// See the [documentation on callsite registration][reg-docs] for details
227    /// on the global callsite registry.
228    ///
229    /// [`Callsite`]: crate::callsite::Callsite
230    /// [reg-docs]: crate::callsite#registering-callsites
231    pub fn register(registration: &'static Registration) {
232        let dispatchers = REGISTRY.dispatchers.read().unwrap();
233        rebuild_callsite_interest(&dispatchers, registration.callsite);
234        REGISTRY.callsites.push(registration);
235    }
236
237    pub(crate) fn register_dispatch(dispatch: &Dispatch) {
238        let mut dispatchers = REGISTRY.dispatchers.write().unwrap();
239        let callsites = &REGISTRY.callsites;
240
241        dispatch.collector().on_register_dispatch(dispatch);
242        dispatchers.push(dispatch.registrar());
243
244        rebuild_interest(callsites, &mut dispatchers);
245    }
246
247    fn rebuild_callsite_interest(
248        dispatchers: &[dispatch::Registrar],
249        callsite: &'static dyn Callsite,
250    ) {
251        let meta = callsite.metadata();
252
253        // Iterate over the collectors in the registry, and β€” if they are
254        // active β€” register the callsite with them.
255        let mut interests = dispatchers.iter().filter_map(|registrar| {
256            registrar
257                .upgrade()
258                .map(|dispatch| dispatch.register_callsite(meta))
259        });
260
261        // Use the first collector's `Interest` as the base value.
262        let interest = if let Some(interest) = interests.next() {
263            // Combine all remaining `Interest`s.
264            interests.fold(interest, Interest::and)
265        } else {
266            // If nobody was interested in this thing, just return `never`.
267            Interest::never()
268        };
269
270        callsite.set_interest(interest)
271    }
272
273    fn rebuild_interest(callsites: &Callsites, dispatchers: &mut Vec<dispatch::Registrar>) {
274        let mut max_level = LevelFilter::OFF;
275        dispatchers.retain(|registrar| {
276            if let Some(dispatch) = registrar.upgrade() {
277                // If the collector did not provide a max level hint, assume
278                // that it may enable every level.
279                let level_hint = dispatch.max_level_hint().unwrap_or(LevelFilter::TRACE);
280                if level_hint > max_level {
281                    max_level = level_hint;
282                }
283                true
284            } else {
285                false
286            }
287        });
288
289        callsites.for_each(|reg| rebuild_callsite_interest(dispatchers, reg.callsite));
290
291        LevelFilter::set_max(max_level);
292    }
293}
294
295#[cfg(not(feature = "std"))]
296mod inner {
297    use super::*;
298    static REGISTRY: Callsites = LinkedList::new();
299
300    /// Clear and reregister interest on every [`Callsite`]
301    ///
302    /// This function is intended for runtime reconfiguration of filters on traces
303    /// when the filter recalculation is much less frequent than trace events are.
304    /// The alternative is to have the [collector] that supports runtime
305    /// reconfiguration of filters always return [`Interest::sometimes()`] so that
306    /// [`enabled`] is evaluated for every event.
307    ///
308    /// This function will also re-compute the global maximum level as determined by
309    /// the [`max_level_hint`] method. If a [`Collect`]
310    /// implementation changes the value returned by its `max_level_hint`
311    /// implementation at runtime, then it **must** call this function after that
312    /// value changes, in order for the change to be reflected.
313    ///
314    /// See the [documentation on callsite interest caching][cache-docs] for
315    /// additional information on this function's usage.
316    ///
317    /// [`max_level_hint`]: crate::collector::Collector::max_level_hint
318    /// [`Callsite`]: crate::callsite::Callsite
319    /// [`enabled`]: crate::collector::Collector::enabled
320    /// [`Interest::sometimes()`]: crate::collect::Interest::sometimes
321    /// [collector]: crate::collect::Collect
322    /// [`Collect`]: crate::collect::Collect
323    /// [cache-docs]: crate::callsite#rebuilding-cached-interest
324    pub fn rebuild_interest_cache() {
325        register_dispatch(dispatch::get_global());
326    }
327
328    /// Register a new [`Callsite`] with the global registry.
329    ///
330    /// This should be called once per callsite after the callsite has been
331    /// constructed.
332    ///
333    /// See the [documentation on callsite registration][reg-docs] for details
334    /// on the global callsite registry.
335    ///
336    /// [`Callsite`]: crate::callsite::Callsite
337    /// [reg-docs]: crate::callsite#registering-callsites
338    pub fn register(registration: &'static Registration) {
339        rebuild_callsite_interest(dispatch::get_global(), registration.callsite);
340        REGISTRY.push(registration);
341    }
342
343    pub(crate) fn register_dispatch(dispatcher: &Dispatch) {
344        // If the collector did not provide a max level hint, assume
345        // that it may enable every level.
346        let level_hint = dispatcher.max_level_hint().unwrap_or(LevelFilter::TRACE);
347
348        REGISTRY.for_each(|reg| rebuild_callsite_interest(dispatcher, reg.callsite));
349
350        LevelFilter::set_max(level_hint);
351    }
352
353    fn rebuild_callsite_interest(dispatcher: &Dispatch, callsite: &'static dyn Callsite) {
354        let meta = callsite.metadata();
355
356        callsite.set_interest(dispatcher.register_callsite(meta))
357    }
358}
359
360// ===== impl Identifier =====
361
362impl PartialEq for Identifier {
363    fn eq(&self, other: &Identifier) -> bool {
364        core::ptr::eq(
365            self.0 as *const _ as *const (),
366            other.0 as *const _ as *const (),
367        )
368    }
369}
370
371impl Eq for Identifier {}
372
373impl fmt::Debug for Identifier {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        write!(f, "Identifier({:p})", self.0)
376    }
377}
378
379impl Hash for Identifier {
380    fn hash<H>(&self, state: &mut H)
381    where
382        H: Hasher,
383    {
384        (self.0 as *const dyn Callsite).hash(state)
385    }
386}
387
388// ===== impl Registration =====
389
390impl<T> Registration<T> {
391    /// Construct a new `Registration` from some `&'static dyn Callsite`
392    pub const fn new(callsite: T) -> Self {
393        Self {
394            callsite,
395            next: AtomicPtr::new(ptr::null_mut()),
396        }
397    }
398}
399
400impl fmt::Debug for Registration {
401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402        f.debug_struct("Registration")
403            .field("callsite", &format_args!("{:p}", self.callsite))
404            .field(
405                "next",
406                &format_args!("{:p}", self.next.load(Ordering::Acquire)),
407            )
408            .finish()
409    }
410}
411
412// ===== impl LinkedList =====
413
414/// An intrusive atomic push-only linked list.
415struct LinkedList<T = &'static dyn Callsite> {
416    head: AtomicPtr<Registration<T>>,
417}
418
419impl<T> LinkedList<T> {
420    const fn new() -> Self {
421        LinkedList {
422            head: AtomicPtr::new(ptr::null_mut()),
423        }
424    }
425}
426
427impl LinkedList {
428    fn for_each(&self, mut f: impl FnMut(&'static Registration)) {
429        let mut head = self.head.load(Ordering::Acquire);
430
431        while let Some(reg) = unsafe { head.as_ref() } {
432            f(reg);
433
434            head = reg.next.load(Ordering::Acquire);
435        }
436    }
437
438    fn push(&self, registration: &'static Registration) {
439        let mut head = self.head.load(Ordering::Acquire);
440
441        loop {
442            registration.next.store(head, Ordering::Release);
443
444            assert_ne!(
445                registration as *const _, head,
446                "Attempted to register a `Callsite` that already exists! \
447                This will cause an infinite loop when attempting to read from the \
448                callsite cache. This is likely a bug! You should only need to call \
449                `tracing-core::callsite::register` once per `Callsite`."
450            );
451
452            match self.head.compare_exchange(
453                head,
454                registration as *const _ as *mut _,
455                Ordering::AcqRel,
456                Ordering::Acquire,
457            ) {
458                Ok(_) => {
459                    break;
460                }
461                Err(current) => head = current,
462            }
463        }
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470
471    struct TestCallsite;
472    static CS1: TestCallsite = TestCallsite;
473    static CS2: TestCallsite = TestCallsite;
474
475    impl Callsite for TestCallsite {
476        fn set_interest(&self, _interest: Interest) {}
477        fn metadata(&self) -> &Metadata<'_> {
478            unimplemented!("not needed for this test")
479        }
480    }
481
482    #[test]
483    fn linked_list_push() {
484        static REG1: Registration = Registration::new(&CS1);
485        static REG2: Registration = Registration::new(&CS2);
486
487        let linked_list = LinkedList::new();
488
489        linked_list.push(&REG1);
490        linked_list.push(&REG2);
491
492        let mut i = 0;
493
494        linked_list.for_each(|reg| {
495            if i == 0 {
496                assert!(
497                    ptr::eq(reg, &REG2),
498                    "Registration pointers need to match REG2"
499                );
500            } else {
501                assert!(
502                    ptr::eq(reg, &REG1),
503                    "Registration pointers need to match REG1"
504                );
505            }
506
507            i += 1;
508        });
509    }
510
511    #[test]
512    fn linked_list_push_several() {
513        static REG1: Registration = Registration::new(&CS1);
514        static REG2: Registration = Registration::new(&CS2);
515        static REG3: Registration = Registration::new(&CS1);
516        static REG4: Registration = Registration::new(&CS2);
517
518        let linked_list = LinkedList::new();
519
520        fn expect(
521            callsites: &mut impl Iterator<Item = &'static Registration>,
522        ) -> impl FnMut(&'static Registration) + '_ {
523            move |reg: &'static Registration| {
524                let ptr = callsites
525                    .next()
526                    .expect("list contained more than the expected number of registrations!");
527
528                assert!(
529                    ptr::eq(reg, ptr),
530                    "Registration pointers need to match ({:?} != {:?})",
531                    reg,
532                    ptr
533                );
534            }
535        }
536
537        linked_list.push(&REG1);
538        linked_list.push(&REG2);
539        let regs = [&REG2, &REG1];
540        let mut callsites = regs.iter().copied();
541        linked_list.for_each(expect(&mut callsites));
542        assert!(
543            callsites.next().is_none(),
544            "some registrations were expected but not present: {:?}",
545            callsites
546        );
547
548        linked_list.push(&REG3);
549        let regs = [&REG3, &REG2, &REG1];
550        let mut callsites = regs.iter().copied();
551        linked_list.for_each(expect(&mut callsites));
552        assert!(
553            callsites.next().is_none(),
554            "some registrations were expected but not present: {:?}",
555            callsites
556        );
557
558        linked_list.push(&REG4);
559        let regs = [&REG4, &REG3, &REG2, &REG1];
560        let mut callsites = regs.iter().copied();
561        linked_list.for_each(expect(&mut callsites));
562        assert!(
563            callsites.next().is_none(),
564            "some registrations were expected but not present: {:?}",
565            callsites
566        );
567    }
568
569    #[test]
570    #[should_panic]
571    fn linked_list_repeated() {
572        static REG1: Registration = Registration::new(&CS1);
573
574        let linked_list = LinkedList::new();
575
576        linked_list.push(&REG1);
577        // Pass in same reg and we should panic...
578        linked_list.push(&REG1);
579
580        linked_list.for_each(|_| {});
581    }
582
583    #[test]
584    fn linked_list_empty() {
585        let linked_list = LinkedList::new();
586
587        linked_list.for_each(|_| {
588            panic!("List should be empty");
589        });
590    }
591}