clilog/
lib.rs

1//! This crate provides a wrapper over `log` crate that allows you
2//! to specify the type of messages and automatically suppress
3//! types of messages that are overwhelmingly sent.
4//!
5//! Basic usage:
6//! ```
7//! clilog::info!(I01TEST, "test message");
8//! ```
9//! when message tagged `I01TEST` is sent over 20 times, a tip will be
10//! printed and further such messages will be suppressed.
11//!
12//! At the end, you can optionally print a statistics of how many messages
13//! are suppressed.
14//! (TODO: not implemented yet.)
15
16use std::sync::Mutex;
17use std::collections::HashMap;
18
19pub use log;
20pub use paste;
21
22lazy_static::lazy_static! {
23    static ref PRINT_COUNT: Mutex<HashMap<(log::Level, &'static str), u64>>
24        = Mutex::new(HashMap::new());
25}
26pub const MAX_PRINT_COUNT: u64 = 20;
27
28/// convenient shortcut that you can call in your `main()`
29/// to initialize logging with stderr color output.
30pub fn init_stderr_color_debug() {
31    use simplelog::*;
32    TermLogger::init(
33        LevelFilter::Debug,
34        ConfigBuilder::new()
35            .set_location_level(LevelFilter::Debug)
36            .set_thread_level(LevelFilter::Trace)
37            .build(),
38        TerminalMode::Stderr,
39        ColorChoice::Auto,
40    ).unwrap();
41}
42
43/// get and increment by 1 the number of occurrence for a specific
44/// kind of message. this is not intended to be used separately,
45/// but is expected to be called from our macro expansion.
46pub fn obtain_count(typ: log::Level, id: &'static str) -> u64 {
47    let mut print_counts = PRINT_COUNT.lock().unwrap();
48    match print_counts.get_mut(&(typ, id)) {
49        Some(v) => {
50            *v += 1;
51            *v
52        },
53        None => {
54            print_counts.insert((typ, id), 1);
55            1
56        }
57    }
58}
59
60/// general logging macro that can either log normally, or
61/// check the count before logging.
62/// this is the basis of other macros like `info`, `warn`, etc.
63#[macro_export]
64macro_rules! log_monitor {
65    ($typ:ident, $id:ident, $fmt:expr $(,$param:expr)*) => {{
66        let count = $crate::obtain_count(
67            $crate::paste::paste!($crate::log::Level::[<$typ:camel>]),
68            stringify!($id));
69        if count <= $crate::MAX_PRINT_COUNT {
70            $crate::log::$typ!(concat!("(", stringify!($id), ") ", $fmt)
71                               $(,$param)*);
72        }
73        if count == $crate::MAX_PRINT_COUNT {
74            $crate::log::$typ!(
75                concat!("Further ",
76                        // stringify will not work properly with paste inside.
77                        stringify!($typ),
78                        " (", stringify!($id), ") will be suppressed."));
79        }
80    }};
81    ($typ:ident, $fmt:expr $(,$param:expr)*) => {{
82        $crate::log::$typ!($fmt $(,$param)*);
83    }}
84}
85
86// unstable yet:
87
88// macro_rules! define_log {
89//     ($($n:ident),+) => {$(
90//         #[macro_export]
91//         macro_rules! $n {
92//             ($$($$p:tt),+) => ($crate::log_monitor!($n $$(,$$p)+))
93//         }
94//     )+}
95// }
96
97// define_log!(info, warn, error);
98
99#[macro_export]
100macro_rules! info {
101    ($t:tt $(,$p:expr)*) => ($crate::log_monitor!(info, $t $(,$p)*))
102}
103
104#[macro_export]
105macro_rules! warn {
106    ($t:tt $(,$p:expr)*) => ($crate::log_monitor!(warn, $t $(,$p)*))
107}
108
109#[macro_export]
110macro_rules! error {
111    ($t:tt $(,$p:expr)*) => ($crate::log_monitor!(error, $t $(,$p)*))
112}
113
114#[macro_export]
115macro_rules! debug {
116    ($t:tt $(,$p:expr)*) => ($crate::log_monitor!(debug, $t $(,$p)*))
117}
118
119#[macro_export]
120macro_rules! trace {
121    ($t:tt $(,$p:expr)*) => ($crate::log_monitor!(trace, $t $(,$p)*))
122}
123
124mod logging_timer;
125pub use logging_timer::*;