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}