1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
//! [![github-img]][github-url] [![crates-img]][crates-url] [![docs-img]][docs-url]
//!
//! [github-url]: https://github.com/QnnOkabayashi/tracing-forest
//! [crates-url]: https://crates.io/crates/tracing-forest
//! [docs-url]: crate
//! [github-img]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-img]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [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
//! 
//! Preserve contextual coherence among trace data from concurrent tasks.
//!
//! # Overview
//!
//! [`tracing`] is a framework for instrumenting programs to collect structured
//! and async-aware diagnostics via the [`Subscriber`] trait. The
//! [`tracing-subscriber`] crate provides tools for composing [`Subscriber`]s
//! from smaller units. This crate extends [`tracing-subscriber`] by providing
//! [`TreeLayer`], a [`Layer`] that preserves contextual coherence of trace
//! data from concurrent tasks when logging.
//!
//! This crate is intended for programs running many nontrivial and disjoint
//! tasks concurrently, like server backends. Unlike other [`Subscriber`]s which
//! simply keep track of the context of an event, `tracing-forest` preserves
//! the contextual coherence when writing logs, allowing readers to easily trace
//! a sequence of events from the same task.
//!
//! `tracing-forest` provides many tools for authoring applications, but can
//! also be extended to author other libraries.
//!
//! [`tracing-subscriber`]: tracing_subscriber
//! [`Layer`]: tracing_subscriber::layer::Layer
//! [`Subscriber`]: tracing::subscriber::Subscriber
//!
//! # Getting started
//!
//! The easiest way to get started is to enable all features. Do this by
//! adding the following to your `Cargo.toml` file:
//! ```toml
//! tracing-forest = { version = "1", features = ["full"] }
//! ```
//! Then, add the [`#[tracing_forest::main]`][attr_main] attribute to your main function:
//! ```
//! # #[allow(clippy::needless_doctest_main)]
//! #[tracing_forest::main]
//! fn main() {
//!     // do stuff here...
//!     tracing::trace!("Hello, world!");
//! }
//! ```
//! For more configuration options, see the
//! [`builder` module documentation][mod@builder].
//!
//! # Contextual Coherence in action
//!
//! This example contains two counters, one for evens and another for odds.
//! Running it will emit trace data at the root level, implying that all the
//! events are _independent_, meaning each trace will be processed and written
//! as it's collected. In this case, the logs will count up chronologically.
//! ```
//! # use std::time::Duration;
//! # use tokio::time::sleep;
//! # #[tracing_forest::test]
//! # #[tokio::test]
//! # async fn test_contextual_coherence() {
//! let evens = async {
//!     for i in 0..3 {
//!         tracing::info!("{}", i * 2);
//!         // pause for `odds`
//!         sleep(Duration::from_millis(100)).await;
//!     }
//! };
//!
//! let odds = async {
//!     // pause for `evens`
//!     sleep(Duration::from_millis(50)).await;
//!     for i in 0..3 {
//!         tracing::info!("{}", i * 2 + 1);
//!         // pause for `evens`
//!         sleep(Duration::from_millis(100)).await;
//!     }
//! };
//!
//! let _ = tokio::join!(evens, odds);
//! # }
//! ```
//! ```log
//! INFO     💬 [info]: 0
//! INFO     💬 [info]: 1
//! INFO     💬 [info]: 2
//! INFO     💬 [info]: 3
//! INFO     💬 [info]: 4
//! INFO     💬 [info]: 5
//! ```
//! [Instrumenting] the counters tells the [`TreeLayer`] to preserve the
//! contextual coherency of trace data from each task. Traces from the `even`
//! counter will be grouped, and traces from the `odd` counter will be grouped.
//! ```
//! # use std::time::Duration;
//! # use tokio::time::sleep;
//! # use tracing::Instrument;
//! # #[tracing_forest::main]
//! # #[tokio::main(flavor = "current_thread")]
//! # async fn concurrent_counting() {
//! let evens = async {
//!     // ...
//! #   for i in 0..3 {
//! #       tracing::info!("{}", i * 2);
//! #       sleep(Duration::from_millis(100)).await;
//! #   }
//! }.instrument(tracing::trace_span!("counting_evens"));
//!
//! let odds = async {
//!     // ...
//! #   sleep(Duration::from_millis(50)).await;
//! #   for i in 0..3 {
//! #       tracing::info!("{}", i * 2 + 1);
//! #       sleep(Duration::from_millis(100)).await;
//! #   }
//! }.instrument(tracing::trace_span!("counting_odds"));
//!     
//! let _ = tokio::join!(evens, odds);
//! # }
//! ```
//! ```log
//! TRACE    counting_evens [ 409µs | 100.000% ]
//! INFO     ┕━ 💬 [info]: 0
//! INFO     ┕━ 💬 [info]: 2
//! INFO     ┕━ 💬 [info]: 4
//! TRACE    counting_odds [ 320µs | 100.000% ]
//! INFO     ┕━ 💬 [info]: 1
//! INFO     ┕━ 💬 [info]: 3
//! INFO     ┕━ 💬 [info]: 5
//! ```
//! Although the numbers were logged chronologically, they appear grouped in the
//! spans they originated from.
//!
//! [join]: tokio::join
//! [instrumenting]: tracing::instrument::Instrument::instrument
//! [`Span`]: tracing::Span
//!
//! # Distinguishing event kinds with tags
//!
//! Beyond log levels, this crate provides the [`Tag`] trait, which allows
//! events to carry additional categorical data.
//!
//! Untagged logs aren't very informative at a glance.
//! ```log
//! INFO     💬 [info]: some info for the admin
//! ERROR    🚨 [error]: the request timed out
//! ERROR    🚨 [error]: the db has been breached
//! ```
//!
//! But with custom tags, they can be!
//! ```log
//! INFO     💬 [admin_info]: some info for the admin
//! ERROR    🚨 [request_error]: the request timed out
//! ERROR    🔐 [security_critical]: the db has been breached
//! ```
//!
//! See the [`tag` module documentation][crate::tag] for details.
//!
//! # Attaching `Uuid`s to spans
//!
//! By enabling the `uuid` feature flag, spans created in the context of a
//! [`TreeLayer`] subscriber are assigned a `Uuid`. At the root level, the uuid 
//! is randomly generated, whereas child spans adopt the uuid of their parent.
//!
//! ### Retreiving the current `Uuid`
//!
//! To retreive the uuid of the current span, use the [`id`] function.
//!
//! ### Initializing a span with a specific `Uuid`
//!
//! To set the `Uuid` of a new span, use [`uuid_span!`], or the shorthand
//! versions, [`uuid_trace_span!`], [`uuid_debug_span!`], [`uuid_info_span!`],
//! [`uuid_warn_span!`], or [`uuid_error_span!`].
//!
//! ## Examples
//!
//! Passing in custom `Uuid`s to nested spans:
//! ```
//! # use tracing_forest::uuid_trace_span;
//! # use ::uuid::Uuid;
//! # #[tracing_forest::test]
//! # fn test_stack_of_spans() {
//! let first_id = Uuid::new_v4();
//! let second_id = Uuid::new_v4();
//!
//! tracing::info!("first_id: {}", first_id);
//! tracing::info!("second_id: {}", second_id);
//!
//! // Explicitly pass `first_id` into a new span
//! uuid_trace_span!(first_id, "first").in_scope(|| {
//!
//!     // Check that the ID we passed in is the current ID
//!     assert_eq!(first_id, tracing_forest::id());
//!
//!     // Open another span, explicitly passing in a new ID
//!     uuid_trace_span!(second_id, "second").in_scope(|| {
//!
//!         // Check that the second ID was set
//!         assert_eq!(second_id, tracing_forest::id());
//!     });
//!
//!     // `first_id` should still be the current ID
//!     assert_eq!(first_id, tracing_forest::id());
//! });
//! # }
//! ```
//! ```log
//! 00000000-0000-0000-0000-000000000000 INFO     💬 [info]: first_id: 9f197cc3-b340-4df6-be53-4ab742a3c586
//! 00000000-0000-0000-0000-000000000000 INFO     💬 [info]: second_id: d552ecfa-a568-4b68-9e68-a4f1f7918579
//! 9f197cc3-b340-4df6-be53-4ab742a3c586 TRACE    first [ 76.6µs | 80.206% / 100.000% ]
//! d552ecfa-a568-4b68-9e68-a4f1f7918579 TRACE    ┕━ second [ 15.2µs | 19.794% ]
//! ```
//!
//! Instrumenting a future with a span using a custom `Uuid`:
//! ```
//! # use tracing::{info, Instrument};
//! # use ::uuid::Uuid;
//! # #[tracing_forest::test]
//! # #[tokio::test]
//! # async fn test_instrument_with_uuid() {
//! let id = Uuid::new_v4();
//! info!("id: {}", id);
//!
//! async {
//!     assert_eq!(id, tracing_forest::id());
//! }.instrument(uuid_trace_span!(id, "in_async")).await;
//! # }
//! ```
//! ```log
//! 00000000-0000-0000-0000-000000000000 INFO     💬 [info]: id: 5aacc2d4-f625-401b-9bb8-dc5c355fd31b
//! 5aacc2d4-f625-401b-9bb8-dc5c355fd31b TRACE    in_async [ 18.6µs | 100.000% ]
//! ```
//!
//! # Immediate logs
//!
//! This crate also provides functionality to immediately format and print logs
//! to stderr in the case of logs with high urgency. This can be done by setting
//! the `immediate` field to `true` in the trace data.
//!
//! ## Example
//!
//! ```
//! # use tracing::{info, trace_span};
//! # #[tracing_forest::test]
//! # fn test_immediate() {
//! trace_span!("my_span").in_scope(|| {
//!     info!("first");
//!     info!("second");
//!     info!(immediate = true, "third, but immediately");
//! })
//! # }
//! ```
//! ```log
//! 💬 IMMEDIATE 💬 INFO     my_span > third, but immediately
//! TRACE    my_span [ 163µs | 100.000% ]
//! INFO     ┕━ 💬 [info]: first
//! INFO     ┕━ 💬 [info]: second
//! INFO     ┕━ 💬 [info]: third, but immediately
//! ```
//!
//! # Feature flags
//!
//! `tracing-forest` uses feature flags to reduce dependencies in your code.
//!
//! * `full`: Enables all features listed below.
//! * `uuid`: Enables spans to carry operation IDs.
//! * `chrono`: Enables timestamps on trace data.
//! * `smallvec`: Enables some performance optimizations.
//! * `sync`: Enables the [`AsyncProcessor`] type.
//! * `json`: Enables JSON formatting for logs.
//! * `derive`: Enables [`#[derive(Tag)]`][derive] for making custom [tag] types.
//! * `attributes`: Enables the [`#[tracing_forest::test]`][attr_test] and
//! [`#[tracing_forest::main]`][attr_main] attributes.
//!
//! [`Uuid`]: ::uuid::Uuid
//! [`AsyncProcessor`]: crate::processor::sync::AsyncProcessor
//! [derive]: tracing_forest_macros::Tag
//! [attr_test]: tracing_forest_macros::test
//! [attr_main]: tracing_forest_macros::main

pub mod builder;
pub mod formatter;
pub mod layer;
pub mod processor;
pub mod tag;
#[doc(hidden)]
#[macro_use]
mod cfg;
#[doc(hidden)]
#[cfg(feature = "json")]
mod ser;
cfg_uuid! {
    mod uuid;

    #[macro_use]
    mod macros;
}
mod fail;

// Items that are required for macros but not intended for public API
#[doc(hidden)]
pub mod private {
    #[cfg(feature = "uuid")]
    pub use crate::uuid::into_u64_pair;
    pub use tracing::subscriber::set_default;
    pub use tracing_subscriber::{fmt::TestWriter, Layer, Registry};
    pub const TRACE_ICON: char = '📍';
    pub const DEBUG_ICON: char = '🐛';
    pub const INFO_ICON: char = '💬';
    pub const WARN_ICON: char = '🚧';
    pub const ERROR_ICON: char = '🚨';
}

pub use crate::builder::builder;
pub use crate::layer::TreeLayer;
pub use crate::tag::Tag;

cfg_uuid! {
    pub use crate::uuid::id;
}

cfg_attributes! {
    #[doc(inline)]
    pub use tracing_forest_macros::test;
    #[doc(inline)]
    pub use tracing_forest_macros::main;
}

cfg_derive! {
    #[doc(inline)]
    pub use tracing_forest_macros::Tag;
}