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