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
//! the test span library provides you with two functions:
//!
//! `get_logs()` that returns [`prelude::Records`]
//!
//! `get_span()` that returns a [`prelude::Span`],
//! Which can be serialized and used with [insta](https://crates.io/crates/insta) for snapshot tests.
//!  Refer to the tests.rs file to see how it behaves.
//!
//! Example:
//! ```ignore
//! #[test_span]
//! async fn test_it_works() {
//!   futures::join!(do_stuff(), do_stuff())
//! }
//!
//! #[tracing::instrument(name = "do_stuff", level = "info")]
//! async fn do_stuff() -> u8 {
//!     // ...
//!     do_stuff2().await;
//! }
//!
//! #[tracing::instrument(
//!     name = "do_stuff2",
//!     target = "my_crate::an_other_target",
//!     level = "info"
//! )]
//! async fn do_stuff_2(number: u8) -> u8 {
//!     // ...
//! }
//! ```
//! ```text
//! `get_span()` will provide you with:
//!
//!             ┌──────┐
//!             │ root │
//!             └──┬───┘
//!                │
//!        ┌───────┴───────┐
//!        ▼               ▼
//!   ┌──────────┐   ┌──────────┐
//!   │ do_stuff │   │ do_stuff │
//!   └────┬─────┘   └─────┬────┘
//!        │               │
//!        │               │
//!        ▼               ▼
//!  ┌───────────┐   ┌───────────┐
//!  │ do_stuff2 │   │ do_stuff2 │
//!  └───────────┘   └───────────┘
//! ```

use layer::Layer;
use once_cell::sync::Lazy;
use std::sync::{Arc, Mutex};
use tracing::Id;
use tracing_core::dispatcher;
use tracing_subscriber::{prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt};
type LazyMutex<T> = Lazy<Arc<Mutex<T>>>;

mod attribute;
mod layer;
mod log;
mod record;
mod report;

pub use record::{Record, RecordValue};
pub use report::{Filter, Records, Report, Span};

static INIT: Lazy<()> = Lazy::new(|| {
    if dispatcher::has_been_set() {
        dispatcher::get_default(|dispatcher| {
            assert!(dispatcher.is::<Layer>(), "A tracing global subscriber has already been set by an other crate than test-span, cannot proceed");
        })
    } else {
        let dispatcher = tracing_subscriber::registry().with(Layer {});
        dispatcher
            .try_init()
            .expect("couldn't set test-span subscriber as a default")
    }
});

/// `init_default` is the default way to call `with_targets`,
/// it sets up `Level::INFO` and looks for environment variables to filter spans.
pub fn init() {
    Lazy::force(&INIT);
}

/// Unlike its `get_logs` counterpart provided by the trace_span macro,
/// `get_all_logs` will return all of the module's tests logs.
pub fn get_all_logs(filter: &Filter) -> Records {
    let logs = layer::ALL_LOGS.lock().unwrap().clone();

    Records::new(logs.all_records_for_filter(filter))
}

/// Returns both the output of `get_spans_for_root` and `get_logs_for_root`
pub fn get_telemetry_for_root(root_id: &Id, filter: &Filter) -> (Span, Records) {
    let report = Report::from_root(root_id.into_u64());

    (report.spans(filter), report.logs(filter))
}

/// Returns a `Span`, a Tree containing all the spans that are children of `root_id`.
///
/// This function filters the `Span` children and `Records`,
/// to only return the ones that match the set verbosity level
pub fn get_spans_for_root(root_id: &Id, filter: &Filter) -> Span {
    Report::from_root(root_id.into_u64()).spans(filter)
}

/// Returns Records, which is a Vec, containing all entries recorded by children of `root_id`.
///
/// This function filters the `Records`to only return the ones that match the set verbosity level.
///
/// / ! \ Logs recorded in spawned threads won't appear here / ! \ use `get_all_logs` instead.
pub fn get_logs_for_root(root_id: &Id, filter: &Filter) -> Records {
    Report::from_root(root_id.into_u64()).logs(filter)
}

pub mod prelude {
    pub use crate::{get_all_logs, get_logs_for_root, get_spans_for_root, get_telemetry_for_root};
    pub use test_span_macro::test_span;
}

pub mod reexports {
    pub use daggy;
    pub use serde;
    pub use tracing;
    pub use tracing_futures;
    pub use tracing_subscriber;
}

#[cfg(test)]
mod test_span_doesnt_panic_tests {
    use super::*;

    #[test]
    fn init_with_already_set_test_span_global_subscriber_doesnt_panic() {
        tracing_subscriber::registry()
            .with(Layer {})
            .try_init()
            .unwrap();
        init();
    }
}