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 = ®ISTRY.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 = ®ISTRY.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(®1);
490 linked_list.push(®2);
491
492 let mut i = 0;
493
494 linked_list.for_each(|reg| {
495 if i == 0 {
496 assert!(
497 ptr::eq(reg, ®2),
498 "Registration pointers need to match REG2"
499 );
500 } else {
501 assert!(
502 ptr::eq(reg, ®1),
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(®1);
538 linked_list.push(®2);
539 let regs = [®2, ®1];
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(®3);
549 let regs = [®3, ®2, ®1];
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(®4);
559 let regs = [®4, ®3, ®2, ®1];
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(®1);
577 // Pass in same reg and we should panic...
578 linked_list.push(®1);
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}