tree_logger/
profile.rs

1use log::Record;
2use std::{
3    fmt::Display,
4    sync::atomic::{AtomicUsize, Ordering},
5};
6
7use crate::constants;
8
9static ID: AtomicUsize = AtomicUsize::new(0);
10
11pub fn profile_core<L, F, T>(
12    label: L,
13    location: &'static str,
14    func: F,
15    level: log::Level,
16    quiet: bool,
17) -> T
18where
19    L: Display,
20    F: FnOnce() -> T,
21{
22    let id = ID.fetch_add(1, Ordering::SeqCst); // TODO: is this correct?
23    log::logger().log(
24        &Record::builder()
25            .key_values(&[(constants::INCREMENT, ())])
26            .build(),
27    );
28    log::logger().log(
29        &Record::builder()
30            .level(level)
31            .key_values(&[
32                (constants::ID, id),
33                (constants::QUIET, if quiet { 1 } else { 0 }),
34            ])
35            .target(location)
36            .args(format_args!("{label}"))
37            .build(),
38    );
39
40    let now = std::time::Instant::now();
41
42    log::logger().log(
43        &Record::builder()
44            .key_values(&[(constants::INCREMENT, ())])
45            .build(),
46    );
47    let rv = func();
48    log::logger().log(
49        &Record::builder()
50            .key_values(&[(constants::DECREMENT, ())])
51            .build(),
52    );
53
54    let elapsed = now.elapsed().as_millis();
55    log::logger().log(
56        &Record::builder()
57            .key_values(&[
58                (constants::SET_TIME, ""),
59                (constants::TIME, &elapsed.to_string()),
60                (constants::ID, &id.to_string()),
61            ])
62            .build(),
63    );
64    log::logger().log(
65        &Record::builder()
66            .key_values(&[(constants::DECREMENT, ())])
67            .build(),
68    );
69    rv
70}
71
72/// Utility macro that profiles code and nests the logging output.
73///
74/// ```no_run
75/// use tree_logger::TreeLogger;
76/// TreeLogger::new().with_colors(true).with_threads(true).init().unwrap();
77/// log::warn!("This is an example message.");
78/// ```
79///
80#[macro_export]
81macro_rules! profile {
82    ($label:expr, $func:expr, $level:expr) => {
83        tree_logger::profile::profile_core($label, file!(), $func, $level, false)
84    };
85    ($label:expr, $func:expr) => {
86        tree_logger::profile::profile_core($label, file!(), $func, log::Level::Info, false)
87    };
88}
89
90#[macro_export]
91macro_rules! profile_quiet {
92    ($label:literal, $func:expr, $level:expr) => {
93        tree_logger::profile::profile_core($label, file!(), $func, $level, true)
94    };
95    ($label:literal, $func:expr) => {
96        tree_logger::profile::profile_core($label, file!(), $func, log::Level::Info, true)
97    };
98}
99
100#[cfg(test)]
101mod test {
102    use crate as tree_logger;
103    use crate::profile;
104
105    #[test]
106    fn logging_works() {
107        profile!("test", || {});
108        profile!("test", || {}, log::Level::Error);
109    }
110}