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