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