utils_box/
logger.rs

1//! # Logger utilities
2//! A toolbox of small utilities to initialize and use loggers based on `simplelog` crate.
3//! Useful for binaries that you need a terminal or a file logger fast.
4
5use std::{fs::File, path::PathBuf, str::FromStr, sync::Mutex};
6
7use chrono;
8use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate};
9use lazy_static::lazy_static;
10use log::info;
11use simplelog::*;
12
13/// Initialize a terminal logger with the provided log level
14pub fn terminal_logger_init(level: LevelFilter) {
15    // Initialize the loggers
16    let log_config = ConfigBuilder::new()
17        .set_time_format("%d-%H:%M:%S%.3f".to_string())
18        .set_time_to_local(true)
19        .build();
20
21    TermLogger::init(
22        level,
23        log_config.clone(),
24        TerminalMode::Mixed,
25        ColorChoice::Auto,
26    )
27    .unwrap();
28}
29
30// Initialize a terminal and a file logger with the provided log levels
31pub fn combined_logger_init(
32    terminal_level: LevelFilter,
33    file_level: LevelFilter,
34    log_path: &str,
35    filename_prefix: &str,
36) {
37    let mut log_path = if log_path.is_empty() {
38        std::env::temp_dir()
39    } else {
40        PathBuf::from_str(log_path).unwrap()
41    };
42
43    let instance_folder = format!("{}_{:?}", filename_prefix, chrono::offset::Utc::now());
44    log_path.push(format!("{}-logs", filename_prefix));
45    log_path.push(&instance_folder);
46    log_path.push(format!("{}.log", filename_prefix));
47
48    println!("[logger_init] Calculated log path [{:?}]", log_path);
49
50    let log_config = ConfigBuilder::new()
51        .set_time_format("%d-%H:%M:%S%.3f".to_string())
52        .set_time_to_local(true)
53        .build();
54    CombinedLogger::init(vec![
55        TermLogger::new(
56            terminal_level,
57            log_config.clone(),
58            TerminalMode::Mixed,
59            ColorChoice::Auto,
60        ),
61        WriteLogger::new(
62            file_level,
63            log_config,
64            FileRotate::new(
65                log_path.clone(),
66                AppendCount::new(5),
67                ContentLimit::Lines(30000),
68                Compression::None,
69                #[cfg(unix)]
70                None,
71            ),
72        ),
73    ])
74    .unwrap();
75
76    log::logger().enabled(&DUMMY);
77
78    info!("[logger_init] Calculated log path [{:?}]", log_path);
79}
80
81lazy_static! {
82    pub static ref DUMMY: log::Metadata<'static> = log::MetadataBuilder::new().build();
83
84    /// Scanner Results File (Used for Demos)
85    pub static ref RESULTS_FILE: Mutex<File> = {
86        let mut log_path = std::env::temp_dir();
87        log_path.push("scanner-results.log");
88
89        Mutex::new(File::options()
90            .append(true)
91            .create(true)
92            .open(log_path)
93            .expect("Results File FAILED!"))
94    };
95}
96
97pub enum LoggerPrintMode {
98    Results,
99    Info,
100}
101
102#[macro_export]
103macro_rules! results_info {
104    (mode:$mode:expr, $($arg:tt)+) => {{
105        if $mode == "results" {
106            $crate::results_info!($($arg)+);
107        }
108        else {
109            $crate::results_info!(info, $($arg)+);
110        }
111    }};
112
113    (info, $($arg:tt)+) => {{
114        $crate::log_info!($($arg)+);
115    }};
116
117    ($($arg:tt)+) => {{
118        use std::io::Write;
119        use chrono::Local;
120
121        $crate::log_info!($($arg)+);
122
123        let timestamp = Local::now().format("%d-%H:%M:%S%.3f").to_string();
124        let mut res_file = $crate::logger::RESULTS_FILE.lock().unwrap();
125        writeln!(res_file,"{} {}",timestamp, format!($($arg)+)).unwrap();
126    }};
127}
128
129#[macro_export]
130macro_rules! log_info {
131    ($($arg:tt)+) => {{
132        if log::logger().enabled(&$crate::logger::DUMMY) {
133            log::info!($($arg)+);
134        }
135        else {
136            std::println!($($arg)+);
137        }
138    }};
139}
140
141#[macro_export]
142macro_rules! log_warn {
143    ($($arg:tt)+) => {{
144        if log::logger().enabled(&$crate::logger::DUMMY) {
145            log::warn!($($arg)+);
146        }
147        else {
148            std::println!($($arg)+);
149        }
150    }};
151}
152
153#[macro_export]
154macro_rules! log_debug {
155    ($($arg:tt)+) => {{
156        if log::logger().enabled(&$crate::logger::DUMMY) {
157            log::debug!($($arg)+);
158        }
159        else {
160            std::println!($($arg)+);
161        }
162    }};
163}
164
165#[macro_export]
166macro_rules! log_trace {
167    ($($arg:tt)+) => {{
168        if log::logger().enabled(&$crate::logger::DUMMY) {
169            log::trace!($($arg)+);
170        }
171        else {
172            std::println!($($arg)+);
173        }
174    }};
175}
176
177#[macro_export]
178macro_rules! log_error {
179    ($($arg:tt)+) => {{
180        if log::logger().enabled(&$crate::logger::DUMMY) {
181            log::error!($($arg)+);
182        }
183        else {
184            std::println!($($arg)+);
185        }
186    }};
187}
188
189#[cfg(test)]
190mod tests {
191    use crate::logger::*;
192    use tempfile::tempdir;
193
194    #[test]
195    fn terminal_logger_init_test() {
196        log_info!("INFO Test TO PRINTLN!");
197        log_debug!("DEBUG Test TO PRINTLN!");
198
199        if !log::logger().enabled(&crate::logger::DUMMY) {
200            terminal_logger_init(LevelFilter::Debug);
201        }
202
203        log_info!("INFO Test TO LOGGER!");
204        log_debug!("DEBUG Test TO LOGGER!");
205    }
206
207    #[test]
208    fn combined_logger_init_test() {
209        let log_dir = tempdir().expect("Failed to create temp directory!");
210
211        if !log::logger().enabled(&crate::logger::DUMMY) {
212            combined_logger_init(
213                LevelFilter::Debug,
214                LevelFilter::Debug,
215                log_dir.path().to_str().unwrap(),
216                "test",
217            );
218        }
219
220        log_info!("INFO Test TO combined LOGGER!");
221        log_debug!("DEBUG Test TO combined LOGGER!");
222    }
223
224    #[test]
225    fn results_macros_test() {
226        log_info!("INFO Test TO PRINTLN!");
227        results_info!("RESULTS Test from PRINTLN!");
228
229        if !log::logger().enabled(&crate::logger::DUMMY) {
230            terminal_logger_init(LevelFilter::Debug);
231        }
232
233        log_info!("INFO Test TO LOGGER!");
234        results_info!("RESULTS Test from LOGGER!");
235
236        log_info!("INFO Test TO LOGGER!");
237        results_info!(info, "{}", "RESULTS Test from LOGGER2!");
238        results_info!("{}", "RESULTS Test from LOGGER3!");
239        let a = "results";
240        results_info!(mode: a, "{}", "TEST");
241        let b = "info";
242        results_info!(mode: b, "{}", "TEST2");
243    }
244}