tracing_forest/
lib.rs

1//! [![github-img]][github-url] [![crates-img]][crates-url] [![docs-img]][docs-url]
2//!
3//! [github-url]: https://github.com/QnnOkabayashi/tracing-forest
4//! [crates-url]: https://crates.io/crates/tracing-forest
5//! [docs-url]: crate
6//! [github-img]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
7//! [crates-img]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
8//! [docs-img]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
9//!
10//! Preserve contextual coherence among trace data from concurrent tasks.
11//!
12//! # Overview
13//!
14//! [`tracing`] is a framework for instrumenting programs to collect structured
15//! and async-aware diagnostics via the [`Subscriber`] trait. The
16//! [`tracing-subscriber`] crate provides tools for composing [`Subscriber`]s
17//! from smaller units. This crate extends [`tracing-subscriber`] by providing
18//! [`ForestLayer`], a [`Layer`] that preserves contextual coherence of trace
19//! data from concurrent tasks when logging.
20//!
21//! This crate is intended for programs running many nontrivial and disjoint
22//! tasks concurrently, like server backends. Unlike [other `Subscriber`s][crate#contextual-coherence-in-action]
23//! which simply keep track of the context of an event, `tracing-forest` preserves
24//! the contextual coherence when writing logs even in parallel contexts, allowing
25//! readers to easily trace a sequence of events from the same task.
26//!
27//! `tracing-forest` is intended for authoring applications.
28//!
29//! [`tracing-subscriber`]: tracing_subscriber
30//! [`Layer`]: tracing_subscriber::layer::Layer
31//! [`Subscriber`]: tracing::subscriber::Subscriber
32//!
33//! # Getting started
34//!
35//! The easiest way to get started is to enable all features. Do this by
36//! adding the following to your `Cargo.toml` file:
37//! ```toml
38//! tracing-forest = { version = "0.1.6", features = ["full"] }
39//! ```
40//! Then, add [`tracing_forest::init`](crate::init) to your main function:
41//! ```
42//! fn main() {
43//!     // Initialize a default `ForestLayer` subscriber
44//!     tracing_forest::init();
45//!     // ...
46//! }
47//! ```
48//! This crate also provides tools for much more advanced configurations:
49//! ```
50//! use tracing_forest::{traits::*, util::*};
51//!
52//! #[tokio::main]
53//! async fn main() {
54//!     tracing_forest::worker_task()
55//!         .set_global(true)
56//!         .map_sender(|sender| sender.or_stderr())
57//!         .build_on(|subscriber| subscriber
58//!             .with(EnvFilter::from_default_env())
59//!             .with(LevelFilter::INFO)
60//!         )
61//!         .on(async {
62//!             // -- snip --
63//!         })
64//!         .await;
65//! }
66//! ```
67//! For useful configuration abstractions, see the [`runtime` module documentation][runtime].
68//!
69//! # Contextual coherence in action
70//!
71//! Similar to this crate, the [`tracing-tree`] crate collects and writes trace
72//! data as a tree. Unlike this crate, it doesn't maintain contextual coherence
73//! in parallel contexts.
74//!
75//! Observe the below program, which simulates serving multiple clients concurrently.
76//! ```
77//! # async fn some_expensive_operation() {}
78//! # mod tracing_tree {
79//! #     #[derive(Default)]
80//! #     pub struct HierarchicalLayer;
81//! #     impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for HierarchicalLayer {}
82//! # }
83//! use tracing::info;
84//! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Registry};
85//! use tracing_tree::HierarchicalLayer;
86//!
87//! #[tracing::instrument]
88//! async fn conn(id: u32) {
89//!     for i in 0..3 {
90//!         some_expensive_operation().await;
91//!         info!(id, "step {}", i);
92//!     }
93//! }
94//!
95//! #[tokio::main(flavor = "multi_thread")]
96//! async fn main() {
97//!     // Use a `tracing-tree` subscriber
98//!     Registry::default()
99//!         .with(HierarchicalLayer::default())
100//!         .init();
101//!
102//!     let connections: Vec<_> = (0..3)
103//!         .map(|id| tokio::spawn(conn(id)))
104//!         .collect();
105//!
106//!     for conn in connections {
107//!         conn.await.unwrap();
108//!     }
109//! }
110//! ```
111//! `tracing-tree` isn't intended for concurrent use, and this is demonstrated
112//! by the output of the program:
113//! ```log
114//! conn id=2
115//! conn id=0
116//! conn id=1
117//!   23ms  INFO step 0, id=2
118//!   84ms  INFO step 0, id=1
119//!   94ms  INFO step 1, id=2
120//!   118ms  INFO step 0, id=0
121//!   130ms  INFO step 1, id=1
122//!   193ms  INFO step 2, id=2
123//!
124//!   217ms  INFO step 1, id=0
125//!   301ms  INFO step 2, id=1
126//!
127//!   326ms  INFO step 2, id=0
128//!
129//! ```
130//! We can instead use `tracing-forest` as a drop-in replacement for `tracing-tree`.
131//! ```
132//! use tracing::info;
133//! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Registry};
134//! use tracing_forest::ForestLayer;
135//!
136//! #[tracing::instrument]
137//! async fn conn(id: u32) {
138//!     // -- snip --
139//! }
140//!
141//! #[tokio::main(flavor = "multi_thread")]
142//! async fn main() {
143//!     // Use a `tracing-forest` subscriber
144//!     Registry::default()
145//!         .with(ForestLayer::default())
146//!         .init();
147//!
148//!     // -- snip --
149//! }
150//! ```
151//! Now we can easily trace what happened:
152//! ```log
153//! INFO     conn [ 150µs | 100.00% ] id: 1
154//! INFO     ┝━ i [info]: step 0 | id: 1
155//! INFO     ┝━ i [info]: step 1 | id: 1
156//! INFO     ┕━ i [info]: step 2 | id: 1
157//! INFO     conn [ 343µs | 100.00% ] id: 0
158//! INFO     ┝━ i [info]: step 0 | id: 0
159//! INFO     ┝━ i [info]: step 1 | id: 0
160//! INFO     ┕━ i [info]: step 2 | id: 0
161//! INFO     conn [ 233µs | 100.00% ] id: 2
162//! INFO     ┝━ i [info]: step 0 | id: 2
163//! INFO     ┝━ i [info]: step 1 | id: 2
164//! INFO     ┕━ i [info]: step 2 | id: 2
165//! ```
166//!
167//! [`tracing-tree`]: https://crates.io/crates/tracing-tree
168//!
169//! # Categorizing events with tags
170//!
171//! This crate allows attaching supplemental categorical information to events with [`Tag`]s.
172//!
173//! Without tags, it's difficult to distinguish where events are occurring in a system.
174//! ```log
175//! INFO     i [info]: some info for the admin
176//! ERROR    🚨 [error]: the request timed out
177//! ERROR    🚨 [error]: the db has been breached
178//! ```
179//!
180//! Tags help make this distinction more visible.
181//! ```log
182//! INFO     i [admin.info]: some info for the admin
183//! ERROR    🚨 [request.error]: the request timed out
184//! ERROR    🔐 [security.critical]: the db has been breached
185//! ```
186//!
187//! See the [`tag` module-level documentation](mod@crate::tag) for details.
188//!
189//! # Attaching `Uuid`s to trace data
190//!
191//! When the `uuid` feature is enabled, the `ForestLayer` will automatically attach
192//! [`Uuid`]s to trace data. Events will adopt the UUID of their span, or the "nil"
193//! UUID at the root level. Spans will adopt the UUID of parent spans, or generate
194//! a new UUID at the root level.
195//!
196//! A span's `Uuid` can also be passed in manually to override adopting the parent's
197//! `Uuid` by passing it in as a field named `uuid`:
198//! ```
199//! # use tracing::info_span;
200//! # use uuid::Uuid;
201//! let id = Uuid::new_v4();
202//!
203//! let span = info_span!("my_span", uuid = %id);
204//! ```
205//!
206//! It can also be retreived from the most recently entered span with
207//! [`tracing_forest::id`](crate::id):
208//! ```
209//! # use tracing::info_span;
210//! # use uuid::Uuid;
211//! # tracing_forest::init();
212//! let id = Uuid::new_v4();
213//!
214//! info_span!("my_span", uuid = %id).in_scope(|| {
215//!     let current_id = tracing_forest::id();
216//!
217//!     assert!(id == current_id);
218//! });
219//! ```
220//!
221//! # Immediate logs
222//!
223//! Since `tracing-forest` stores trace data in memory until the root span finishes,
224//! it can be a long time until a log is written. This may not be acceptable in
225//! certain use cases.
226//!
227//! To resolve this, the `immediate` field can be used on an event to print the
228//! event and its parent spans to stderr. Unlike `eprintln!`, the event will
229//! still appear in the trace tree written once the root span closes.
230//!
231//! ## Example
232//!
233//! ```
234//! use tracing::{info, trace_span};
235//!
236//! tracing_forest::init();
237//!
238//! trace_span!("my_span").in_scope(|| {
239//!     info!("first");
240//!     info!("second");
241//!     info!(immediate = true, "third, but immediately");
242//! });
243//! ```
244//! ```log
245//! INFO     i IMMEDIATE i my_span > third, but immediately
246//! TRACE    my_span [ 125µs | 100.000% ]
247//! INFO     ┝━ i [info]: first
248//! INFO     ┝━ i [info]: second
249//! INFO     ┕━ i [info]: third, but immediately
250//! ```
251//!
252//! # Feature flags
253//!
254//! This crate uses feature flags to reduce dependency bloat.
255//!
256//! * `full`: Enables all features listed below.
257//! * `uuid`: Enables spans to carry operation IDs.
258//! * `chrono`: Enables timestamps on trace data.
259//! * `ansi`: Enables ANSI terminal colors.
260//! * `smallvec`: Enables some performance optimizations.
261//! * `tokio`: Enables [`worker_task`] and [`capture`].
262//! * `serde`: Enables log trees to be serialized, which is [useful for formatting][serde_fmt].
263//! * `env-filter`: Re-exports [`EnvFilter`] from the [`util`] module.
264//!
265//! By default, only `smallvec` in enabled.
266//!
267//! [`Uuid`]: uuid::Uuid
268//! [serde_fmt]: crate::printer::Formatter#examples
269//! [`EnvFilter`]: tracing_subscriber::EnvFilter
270
271#![doc(issue_tracker_base_url = "https://github.com/QnnOkabayashi/tracing-forest/issues")]
272#![cfg_attr(
273    docsrs,
274    // Allows displaying cfgs/feature flags in the documentation.
275    feature(doc_cfg),
276    // Allows adding traits to RustDoc's list of "notable traits"
277    // feature(doc_notable_trait),
278    // Fail the docs build if any intra-docs links are broken
279    deny(rustdoc::broken_intra_doc_links),
280)]
281#![deny(warnings)]
282#![warn(unused_extern_crates)]
283#![warn(missing_docs)]
284
285pub mod printer;
286pub mod processor;
287pub mod tag;
288pub mod tree;
289#[macro_use]
290mod cfg;
291mod fail;
292mod layer;
293
294pub use layer::{init, test_init, ForestLayer};
295pub use printer::{Formatter, PrettyPrinter, Printer};
296pub use processor::Processor;
297pub use tag::Tag;
298
299cfg_tokio! {
300    pub mod runtime;
301    pub use runtime::{capture, worker_task};
302}
303
304cfg_uuid! {
305    pub use layer::id::id;
306}
307
308/// Bring traits from this crate, `tracing`, and `tracing_subscriber` into scope
309/// anonymously.
310pub mod traits {
311    pub use crate::Processor as _;
312    pub use tracing::Instrument as _;
313    pub use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _};
314}
315
316/// Bring Tracing's event and span macros into scope, along with other sensible defaults.
317pub mod util {
318    #[doc(no_inline)]
319    pub use crate::ForestLayer;
320    #[doc(no_inline)]
321    pub use tracing::metadata::LevelFilter;
322    #[doc(no_inline)]
323    pub use tracing::{
324        debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, warn_span,
325        Event, Level,
326    };
327    #[cfg(feature = "env-filter")]
328    #[doc(no_inline)]
329    pub use tracing_subscriber::EnvFilter;
330}