Skip to main content

dcontext_tracing/
lib.rs

1//! # dcontext-tracing
2//!
3//! Automatic [dcontext](https://docs.rs/dcontext) scope management via
4//! [tracing](https://docs.rs/tracing) spans.
5//!
6//! This crate provides a [`tracing_subscriber::Layer`] that automatically
7//! creates and manages dcontext scopes when tracing spans are entered and
8//! exited. This means your context values follow the natural span lifecycle
9//! without any manual scope management.
10//!
11//! ## Quick Start
12//!
13//! ```rust,no_run
14//! use tracing_subscriber::prelude::*;
15//!
16//! // Zero-config: every span creates a dcontext scope
17//! tracing_subscriber::registry()
18//!     .with(dcontext_tracing::DcontextLayer::new())
19//!     .init();
20//! ```
21//!
22//! ## Features
23//!
24//! ### Level 1: Automatic Scoping
25//!
26//! With zero configuration, `DcontextLayer` creates a new dcontext scope
27//! every time a span is entered. Values set inside a span are automatically
28//! cleaned up when the span exits, just like tracing's own span lifecycle.
29//!
30//! ```rust,no_run
31//! # use tracing_subscriber::prelude::*;
32//! # tracing_subscriber::registry()
33//! #     .with(dcontext_tracing::DcontextLayer::new())
34//! #     .init();
35//! #
36//! // Register context keys, then inside a span:
37//! // dcontext::set_context("user", "alice".to_string());
38//! // {
39//! //     let _span = tracing::info_span!("request").entered();
40//! //     // New scope created — inherits parent values
41//! //     dcontext::set_context("request_id", "abc-123".to_string());
42//! // }
43//! // Scope reverted — "request_id" gone, "user" remains
44//! ```
45//!
46//! ### Level 2: Field-to-Context Extraction
47//!
48//! Extract tracing span fields directly into dcontext values using
49//! [`TracingField`] metadata:
50//!
51//! ```rust,no_run
52//! use dcontext_tracing::{DcontextLayer, TracingField};
53//!
54//! #[derive(Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
55//! struct RequestId(String);
56//!
57//! let mut builder = dcontext::RegistryBuilder::new();
58//! builder.register_with::<RequestId>("request_id", |opts| {
59//!     opts.with_metadata(
60//!         TracingField::builder("request_id")
61//!             .extract_from_str(|s| Some(RequestId(s.to_string())))
62//!             .build(),
63//!     )
64//! });
65//!
66//! // Then use DcontextLayer — it discovers TracingField metadata automatically
67//! // let layer = DcontextLayer::new();
68//! ```
69//!
70//! ### Level 3: Span Info
71//!
72//! Expose span metadata as a context value:
73//!
74//! ```rust,no_run
75//! use dcontext_tracing::{DcontextLayer, SpanInfo};
76//! use tracing_subscriber::Registry;
77//!
78//! let layer: DcontextLayer<Registry> = DcontextLayer::builder()
79//!     .include_span_info()
80//!     .build();
81//!
82//! // Inside a span:
83//! // let info: SpanInfo = dcontext::get_context("dcontext.span");
84//! // info.name, info.target, info.level
85//! ```
86//!
87//! ## How It Works
88//!
89//! The layer uses a thread-local stack to store dcontext `ScopeGuard`s
90//! (which are `!Send` and cannot be stored in tracing's span extensions).
91//! On span enter, a new scope is pushed; on span exit, the scope is popped
92//! and the guard dropped, reverting context changes made in that scope.
93//!
94//! This mirrors the approach used by `tracing-opentelemetry` for similar
95//! thread-local guard management.
96//!
97//! ## Async Behavior
98//!
99//! When used with [`Instrument`](tracing::Instrument), the layer creates and
100//! reverts a scope around each poll of the future. Mapped field values and span
101//! info are re-applied on each enter, so reads via `force_thread_local()` will
102//! see the correct values during each poll. However, **mutations made inside a
103//! span do not persist across `.await` points** — each poll gets a fresh scope.
104//!
105//! For full async context propagation across `.await`, use `dcontext::with_context()`
106//! or `dcontext::ContextFuture` directly.
107
108mod field_mapping;
109mod guard_stack;
110mod layer;
111mod span_info;
112mod tracing_field;
113
114#[cfg(test)]
115mod tests;
116
117pub use layer::{DcontextLayer, DcontextLayerBuilder};
118pub use tracing_field::{TracingField, TracingFieldBuilder, WithContextFields, collect_log_fields};
119pub use span_info::{SpanInfo, SPAN_INFO_KEY};