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); 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#[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}