Skip to main content

tracing_wide/
lib.rs

1// Via the package-local symlink, so the path also resolves inside the
2// published archive (where `../../README.md` would not exist).
3#![doc = include_str!("../README.md")]
4//!
5//! # Feature flags
6//!
7#![cfg_attr(
8    feature = "docs",
9    cfg_attr(doc, doc = ::document_features::document_features!())
10)]
11// The bare core (define + emit) uses only `core`; `std` — and every feature
12// that enables it — links std.
13#![cfg_attr(not(feature = "std"), no_std)]
14
15use core::any::Any;
16use core::fmt;
17
18pub use tracing_wide_macros::{event, message};
19
20#[cfg(doc)]
21pub mod examples;
22
23/// Re-export of the [`facet`](https://docs.rs/facet) crate, so a subscriber names
24/// `Peek` and the `Facet` trait through the exact version tracing-wide compiled
25/// against. facet is pre-1.0 — every minor is a breaking change — so naming it via a
26/// separate direct dependency risks a type mismatch across the `as_facet` boundary;
27/// going through `tracing_wide::facet` guarantees one version.
28///
29/// To `#[derive(Facet)]` on a message, either depend on `facet` directly (pinned to
30/// the same version), or derive through this re-export with
31/// `#[derive(tracing_wide::facet::Facet)]` + `#[facet(crate = tracing_wide::facet)]`,
32/// which needs no direct facet dependency.
33#[cfg(feature = "facet")]
34pub use ::facet;
35
36#[cfg(feature = "catalogue")]
37pub mod catalogue;
38#[cfg(feature = "instrument")]
39pub mod instrument;
40#[cfg(feature = "subscriber")]
41pub mod subscriber;
42
43/// Marker trait: a type eligible to be a field of a message.
44///
45/// Blanket-implemented for every `tracing::field::Value` — the supertrait bound
46/// guarantees every field can be handed to tracing, and `Option<T: Value>` is
47/// covered for free.
48///
49/// The trait carries no constraint of its own; it exists for the name and as the
50/// single seam to tighten the field contract later. Serialization is never part
51/// of this bound — it is opted into case-by-case at the subscriber.
52pub trait Field: tracing::field::Value {}
53
54/// A struct that may be emitted as a wide event.
55///
56/// Apply `#[message]`, which implements this trait, enforces that every field is
57/// a [`Field`], and fills the inherent consts (`MSG`, `LEVEL`, `ORIGIN`, `TAGS`)
58/// the methods below mirror (associated consts aren't object-safe).
59///
60/// Object-safe, so subscribers can receive `&dyn Message`. Pseudo-sealed
61/// against accidental hand-written impls via the hidden
62/// [`__private::MessageBehaviour`] supertrait, which only `#[message]` emits —
63/// see [`__private::Sealed`] for why it's only a *pseudo*-seal.
64pub trait Message: __private::MessageBehaviour {
65    /// Escape hatch for subscribers that want the concrete type back, via
66    /// `as_any().downcast_ref::<T>()`.
67    fn as_any(&self) -> &dyn Any;
68
69    /// Erased reflection hook, keyed on one knob: `#[derive(Facet)]`.
70    ///
71    /// Via [`__private::facet`] autoref specialization the generated body returns
72    /// `Some` (a [`Peek`](::facet::Peek) over `self`) when the type derives
73    /// `Facet` and `None` otherwise — no `Facet` bound ever lands on a message
74    /// that didn't derive it. The introspection parallel to
75    /// [`as_serialize`](Self::as_serialize): a subscriber walks the `Peek` to
76    /// read fields by name and filter on a field's value, which static
77    /// [`tags`](Self::tags) cannot.
78    #[cfg(feature = "facet")]
79    fn as_facet(&self) -> Option<::facet::Peek<'_, 'static>> {
80        None
81    }
82
83    /// Erased serialization hook, keyed on one knob: `#[derive(Serialize)]`.
84    ///
85    /// Via [`__private::serde`] autoref specialization the generated body
86    /// returns `Some(self)` when the type derives `Serialize` and `None`
87    /// otherwise — no `Serialize` bound ever lands on a message that didn't
88    /// derive it. The only serde surface in the core; `dyn
89    /// erased_serde::Serialize` implements `serde::Serialize`, so a subscriber
90    /// serializes the result with any format.
91    #[cfg(feature = "serde")]
92    fn as_serialize(&self) -> Option<&dyn ::erased_serde::Serialize> {
93        None
94    }
95
96    /// Severity of this event type; `#[message(level = ...)]`, default `INFO`.
97    fn level(&self) -> tracing::Level;
98
99    /// The constant, static message text.
100    fn msg(&self) -> &'static str;
101
102    /// Where this message type is defined — automatic provenance, never set by
103    /// hand. See [`Origin`].
104    fn origin(&self) -> &'static Origin;
105
106    /// Static routing tags: the sorted, deduped, lowercased set from
107    /// `#[message(tags = [...])]`, default empty. Where [`origin`](Self::origin)
108    /// is provenance (where defined), tags are intent (where to send) — routing
109    /// is the subscriber's call, e.g. `m.tags().contains(&"analytics")`.
110    fn tags(&self) -> &'static [&'static str] {
111        &[]
112    }
113}
114
115/// Where a message *type* is defined — automatic provenance captured by
116/// `#[message]`, which emits the `core` location builtins
117/// (`env!("CARGO_PKG_NAME")`, `module_path!`, `file!`, `line!`, `column!`) for
118/// rustc to fill while compiling the *defining* crate.
119///
120/// Reachable on every message through [`Message::origin`], so a subscriber can
121/// attribute or route a `&dyn Message` without naming the concrete type. All
122/// fields are `&'static`/`u32`, so `Origin` is `Copy` and stays `no_std`.
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124#[cfg_attr(feature = "facet", derive(::facet::Facet))]
125#[cfg_attr(feature = "facet", facet(proxy = String))]
126pub struct Origin {
127    /// Column of the definition (`column!()`).
128    pub column: u32,
129    /// Source file of the definition (`file!()`).
130    pub file: &'static str,
131    /// The defining crate (`CARGO_PKG_NAME`); named `krate` because `crate`
132    /// cannot be written as a raw identifier.
133    pub krate: &'static str,
134    /// Line of the definition (`line!()`).
135    pub line: u32,
136    /// The defining module path (`module_path!()`), e.g. `mycrate::sub`.
137    pub module: &'static str,
138}
139
140impl fmt::Display for Origin {
141    /// Compact one-line form: `crate file:line:column` (the `module` is
142    /// omitted — it's prefixed by the crate and rarely needed at a glance).
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        write!(
145            f,
146            "{} {}:{}:{}",
147            self.krate, self.file, self.line, self.column
148        )
149    }
150}
151
152/// Facet serializes `Origin` through its compact `Display` string (the `proxy`),
153/// matching the serde manifest. Serialize-only — provenance is never parsed back,
154/// so the reverse conversion is a stub.
155// Infallible, but facet's `proxy` mechanism is defined in terms of `TryFrom`.
156#[cfg(feature = "facet")]
157#[allow(clippy::infallible_try_from)]
158impl TryFrom<&Origin> for String {
159    type Error = core::convert::Infallible;
160    fn try_from(origin: &Origin) -> Result<Self, Self::Error> {
161        Ok(origin.to_string())
162    }
163}
164
165#[cfg(feature = "facet")]
166impl TryFrom<String> for Origin {
167    type Error = &'static str;
168    fn try_from(_: String) -> Result<Self, Self::Error> {
169        Err("Origin is serialize-only in the catalogue")
170    }
171}
172
173impl<T: tracing::field::Value> Field for T {}
174
175/// `#[message]`-internal: register a descriptor in the catalogue, or not.
176/// Cfg-selected by *tracing-wide's* `catalogue` feature; with it off, the call
177/// expands to nothing and never names the (absent) descriptor types.
178#[cfg(feature = "catalogue")]
179#[doc(hidden)]
180#[macro_export]
181macro_rules! __register_message {
182    ($desc:expr) => {
183        // In a const item so the allow reliably covers the whole expansion — a
184        // deprecated message type is still named by its own registration.
185        #[allow(deprecated)]
186        const _: () = {
187            $crate::__private::inventory::submit! { $desc }
188        };
189    };
190}
191
192#[cfg(not(feature = "catalogue"))]
193#[doc(hidden)]
194#[macro_export]
195macro_rules! __register_message {
196    ($desc:expr) => {};
197}
198
199/// `#[message]`-internal: emit the `Message::as_facet` override, or not.
200/// Cfg-selected by *tracing-wide's* `facet` feature. Every message gets the
201/// same body — `Some(Peek::new(self))` iff `Self: Facet`, decided at the call
202/// site by [`__private::facet`] autoref specialization; `#[derive(Facet)]` is
203/// the only knob.
204#[cfg(feature = "facet")]
205#[doc(hidden)]
206#[macro_export]
207macro_rules! __message_facet_method {
208    () => {
209        fn as_facet(&self) -> ::core::option::Option<$crate::__private::facet::Peek<'_, 'static>> {
210            #[allow(unused_imports)]
211            use $crate::__private::facet::{ViaFacet as _, ViaNotFacet as _};
212            (&$crate::__private::facet::Probe::new(self)).__tracing_wide_as_facet()
213        }
214    };
215}
216
217#[cfg(not(feature = "facet"))]
218#[doc(hidden)]
219#[macro_export]
220macro_rules! __message_facet_method {
221    () => {};
222}
223
224/// `#[message]`-internal: emit the `MessageBehaviour::join_ambient` override,
225/// or not. Cfg-selected by *tracing-wide's* `instrument` feature (a `#[cfg]`
226/// the proc macro emitted would be evaluated in the wrong crate). Receives
227/// every `Option` field as `(ident, InnerType)`; the override fills each
228/// still-`None` field from the span scope by name, with
229/// [`__private::instrument`] autoref specialization deciding per inner type
230/// whether a [`FromCaptured`](instrument::FromCaptured) conversion exists.
231#[cfg(feature = "instrument")]
232#[doc(hidden)]
233#[macro_export]
234macro_rules! __message_ambient_method {
235    ( $( ($field:ident, $ty:ty) ),* $(,)? ) => {
236        // Deprecated fields stay joinable; only producer construction should warn.
237        #[allow(deprecated)]
238        fn join_ambient(&mut self) {
239            $(
240                if self.$field.is_none() {
241                    if let ::core::option::Option::Some(__tracing_wide_value) =
242                        $crate::__private::instrument::get(::core::stringify!($field))
243                    {
244                        #[allow(unused_imports)]
245                        use $crate::__private::instrument::{
246                            ViaFromCaptured as _, ViaNotCapturable as _,
247                        };
248                        self.$field = (&$crate::__private::instrument::Probe::<$ty>::new())
249                            .__tracing_wide_from_captured(&__tracing_wide_value);
250                    }
251                }
252            )*
253        }
254    };
255}
256
257#[cfg(not(feature = "instrument"))]
258#[doc(hidden)]
259#[macro_export]
260macro_rules! __message_ambient_method {
261    ( $($tt:tt)* ) => {};
262}
263
264/// `#[message]`-internal: emit the `Message::as_serialize` override, or not.
265/// Cfg-selected by *tracing-wide's* `serde` feature. Every message gets the
266/// same body — `Some(self)` iff `Self: Serialize`, decided at the call site by
267/// [`__private::serde`] autoref specialization; `#[derive(Serialize)]` is the
268/// only knob.
269#[cfg(feature = "serde")]
270#[doc(hidden)]
271#[macro_export]
272macro_rules! __message_serialize_method {
273    () => {
274        fn as_serialize(&self) -> ::core::option::Option<&dyn $crate::__private::serde::Serialize> {
275            #[allow(unused_imports)]
276            use $crate::__private::serde::{ViaNotSerializable as _, ViaSerialize as _};
277            (&$crate::__private::serde::Probe::new(self)).__tracing_wide_as_serialize()
278        }
279    };
280}
281
282#[cfg(not(feature = "serde"))]
283#[doc(hidden)]
284#[macro_export]
285macro_rules! __message_serialize_method {
286    () => {};
287}
288
289/// Macro-internal plumbing — everything `#[message]`/`event!` expansions name
290/// in *downstream* crates. All of it is `pub` only because generated code must
291/// reach it across crate boundaries; none of it is supported API. (The `__*!`
292/// macros can't join it — `#[macro_export]` forces them to the crate root.)
293#[doc(hidden)]
294pub mod __private {
295    /// Re-export so `__register_message!` can name inventory without the user
296    /// crate depending on it directly. Gated by `catalogue`, like its caller.
297    #[cfg(feature = "catalogue")]
298    pub use ::inventory;
299    /// Re-export so `#[message]` expansions can name `Level` and the level
300    /// macros through *this* crate — a downstream crate that only defines
301    /// messages needs no direct `tracing` dependency.
302    pub use ::tracing;
303
304    /// Recording behavior for [`Message`](crate::Message), generated by
305    /// `#[message]` and itself pseudo-sealed via [`Sealed`] — combined with
306    /// `Message: MessageBehaviour`, `#[message]` is the only supported way to
307    /// obtain a `Message`.
308    pub trait MessageBehaviour: Sealed {
309        /// The single entry point `event!` expands to: fan out to registered
310        /// subscribers (with the `subscriber` feature), then hand off to
311        /// tracing. The body lives here so the cfg keys on *tracing-wide's*
312        /// feature and can reach the crate-private registry; the `Self: Sized`
313        /// bound lets `self` coerce to `&dyn Message` while keeping the trait
314        /// object-safe.
315        fn emit(&self)
316        where
317            Self: crate::Message + Sized,
318        {
319            #[cfg(feature = "subscriber")]
320            crate::subscriber::dispatch(self);
321            self.record();
322        }
323
324        /// Fill still-`None` `Option` fields from the ambient span scope.
325        /// `event!` calls this *before* `emit`. A no-op unless the `instrument`
326        /// feature lets `#[message]` override it; unconditional (not cfg-gated)
327        /// so `event!` expansions compile regardless of tracing-wide's features.
328        fn join_ambient(&mut self) {}
329
330        /// The tracing handoff for this concrete type. Generated by `#[message]`.
331        fn record(&self);
332    }
333
334    /// The pseudo-seal: only `#[message]` emits `impl Sealed`, so neither
335    /// [`MessageBehaviour`] nor (transitively) [`Message`](crate::Message) can
336    /// be implemented by hand *by accident*. Only a *pseudo*-seal because
337    /// `#[message]` emits this impl in the *downstream* crate, so the trait must
338    /// stay `pub`/reachable — and anything the macro can write, a hand can write
339    /// too. It catches accidents, not deliberate reach-through, which stays
340    /// unsupported.
341    pub trait Sealed {}
342
343    /// Autoref specialization for the `__message_facet_method!` shim —
344    /// `Some(Peek::new(self))` iff `Self: Facet<'static>` — plus the `Peek`
345    /// re-export the shim names. The module is deliberately named `facet`; the
346    /// `::facet` bound below names the crate absolutely.
347    #[cfg(feature = "facet")]
348    pub mod facet {
349        use ::facet::Facet;
350        pub use ::facet::Peek;
351
352        pub struct Probe<'a, T>(&'a T);
353
354        pub trait ViaFacet<'a> {
355            fn __tracing_wide_as_facet(&self) -> Option<Peek<'a, 'static>>;
356        }
357
358        pub trait ViaNotFacet<'a> {
359            fn __tracing_wide_as_facet(&self) -> Option<Peek<'a, 'static>>;
360        }
361
362        impl<'a, T> Probe<'a, T> {
363            pub fn new(value: &'a T) -> Self {
364                Probe(value)
365            }
366        }
367
368        impl<'a, T: Facet<'static>> ViaFacet<'a> for Probe<'a, T> {
369            fn __tracing_wide_as_facet(&self) -> Option<Peek<'a, 'static>> {
370                Some(Peek::new(self.0))
371            }
372        }
373
374        impl<'a, T> ViaNotFacet<'a> for &Probe<'a, T> {
375            fn __tracing_wide_as_facet(&self) -> Option<Peek<'a, 'static>> {
376                None
377            }
378        }
379    }
380
381    /// Autoref specialization for the `__message_ambient_method!` shim —
382    /// deciding per inner type whether a
383    /// [`FromCaptured`](crate::instrument::FromCaptured) conversion exists —
384    /// plus [`get`](instrument::get), the read half of ambient autocapture.
385    #[cfg(feature = "instrument")]
386    pub mod instrument {
387        use core::marker::PhantomData;
388
389        use tracing::{Span, dispatcher};
390        use tracing_subscriber::registry::{LookupSpan, Registry};
391
392        use crate::instrument::{CapturedFields, CapturedValue, FromCaptured};
393
394        pub struct Probe<T>(PhantomData<T>);
395
396        pub trait ViaFromCaptured<T> {
397            fn __tracing_wide_from_captured(&self, value: &CapturedValue) -> Option<T>;
398        }
399
400        pub trait ViaNotCapturable<T> {
401            fn __tracing_wide_from_captured(&self, value: &CapturedValue) -> Option<T>;
402        }
403
404        impl<T> Probe<T> {
405            #[allow(clippy::new_without_default)]
406            pub fn new() -> Self {
407                Probe(PhantomData)
408            }
409        }
410
411        impl<T: FromCaptured> ViaFromCaptured<T> for Probe<T> {
412            fn __tracing_wide_from_captured(&self, value: &CapturedValue) -> Option<T> {
413                T::from_captured(value)
414            }
415        }
416
417        impl<T> ViaNotCapturable<T> for &Probe<T> {
418            fn __tracing_wide_from_captured(&self, _: &CapturedValue) -> Option<T> {
419                None
420            }
421        }
422
423        /// Look `name` up in the current span scope, innermost span first.
424        /// Requires a stack built on the concrete `Registry`
425        /// (`tracing_subscriber::registry()`); every failure mode — no
426        /// dispatcher, no current span, a non-`Registry` subscriber, no
427        /// [`CaptureLayer`](crate::instrument::CaptureLayer), name not
428        /// captured — is a quiet `None`. Emission never fails on ambient state.
429        pub fn get(name: &str) -> Option<CapturedValue> {
430            let current = Span::current();
431            let id = current.id()?;
432
433            dispatcher::get_default(|dispatch| {
434                let registry = dispatch.downcast_ref::<Registry>()?;
435
436                let span = registry.span(&id)?;
437
438                for span in span.scope() {
439                    if let Some(captured) = span.extensions().get::<CapturedFields>()
440                        && let Some((_, value)) = captured.0.iter().find(|(n, _)| *n == name)
441                    {
442                        return Some(value.clone());
443                    }
444                }
445                None
446            })
447        }
448    }
449
450    /// Autoref specialization for the `__message_serialize_method!` shim —
451    /// `Some(self)` iff `Self: Serialize` — plus the erased-serde re-export the
452    /// shim names. The module is deliberately named `serde`; the `::serde`
453    /// bound below names the crate absolutely.
454    #[cfg(feature = "serde")]
455    pub mod serde {
456        pub use ::erased_serde::Serialize;
457
458        pub struct Probe<'a, T>(&'a T);
459
460        pub trait ViaNotSerializable<'a> {
461            fn __tracing_wide_as_serialize(&self) -> Option<&'a dyn Serialize>;
462        }
463
464        pub trait ViaSerialize<'a> {
465            fn __tracing_wide_as_serialize(&self) -> Option<&'a dyn Serialize>;
466        }
467
468        impl<'a, T> Probe<'a, T> {
469            pub fn new(value: &'a T) -> Self {
470                Probe(value)
471            }
472        }
473
474        impl<'a, T> ViaNotSerializable<'a> for &Probe<'a, T> {
475            fn __tracing_wide_as_serialize(&self) -> Option<&'a dyn Serialize> {
476                None
477            }
478        }
479
480        impl<'a, T: ::serde::Serialize> ViaSerialize<'a> for Probe<'a, T> {
481            fn __tracing_wide_as_serialize(&self) -> Option<&'a dyn Serialize> {
482                Some(self.0)
483            }
484        }
485    }
486}