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