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 = ®ISTRY.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 = ®ISTRY.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(®1);
484 linked_list.push(®2);
485
486 let mut i = 0;
487
488 linked_list.for_each(|reg| {
489 if i == 0 {
490 assert!(
491 ptr::eq(reg, ®2),
492 "Registration pointers need to match REG2"
493 );
494 } else {
495 assert!(
496 ptr::eq(reg, ®1),
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(®1);
532 linked_list.push(®2);
533 let regs = [®2, ®1];
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(®3);
543 let regs = [®3, ®2, ®1];
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(®4);
553 let regs = [®4, ®3, ®2, ®1];
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(®1);
571 // Pass in same reg and we should panic...
572 linked_list.push(®1);
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}