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}