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 anyhow::Result;
8use chrono;
9use file_rotate::{ContentLimit, FileRotate, compression::Compression, suffix::AppendCount};
10use lazy_static::lazy_static;
11use log::info;
12use simplelog::*;
13
14/// Initialize a terminal logger with the provided log level
15pub fn terminal_logger_init(level: LevelFilter) -> Result<()> {
16    // Initialize the loggers
17    let log_config = ConfigBuilder::new()
18        .set_time_format("%d-%H:%M:%S%.3f".to_string())
19        .set_time_to_local(true)
20        .build();
21
22    Ok(TermLogger::init(
23        level,
24        log_config.clone(),
25        TerminalMode::Mixed,
26        ColorChoice::Auto,
27    )?)
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) -> Result<()> {
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!("{filename_prefix}-logs"));
45    log_path.push(&instance_folder);
46    log_path.push(format!("{filename_prefix}.log"));
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
55    info!("[logger_init] Calculated log path [{log_path:?}]");
56
57    Ok(CombinedLogger::init(vec![
58        TermLogger::new(
59            terminal_level,
60            log_config.clone(),
61            TerminalMode::Mixed,
62            ColorChoice::Auto,
63        ),
64        WriteLogger::new(
65            file_level,
66            log_config,
67            FileRotate::new(
68                log_path.clone(),
69                AppendCount::new(5),
70                ContentLimit::Lines(30000),
71                Compression::None,
72                #[cfg(unix)]
73                None,
74            ),
75        ),
76    ])?)
77}
78
79lazy_static! {
80    pub static ref RESULTS_FILE: Mutex<File> = {
81        let mut log_path = std::env::temp_dir();
82        log_path.push("utils-results.log");
83
84        Mutex::new(
85            File::options()
86                .append(true)
87                .create(true)
88                .open(log_path)
89                .expect("Results File FAILED!"),
90        )
91    };
92}
93
94pub enum LoggerPrintMode {
95    Results,
96    Info,
97}
98
99#[macro_export]
100macro_rules! results_info {
101    (mode:$mode:expr, $($arg:tt)+) => {{
102        if $mode == "results" {
103            $crate::results_info!($($arg)+);
104        }
105        else {
106            $crate::results_info!(info, $($arg)+);
107        }
108    }};
109
110    (info, $($arg:tt)+) => {{
111        $crate::log_info!($($arg)+);
112    }};
113
114    ($($arg:tt)+) => {{
115        use std::io::Write;
116        use chrono::Local;
117
118        $crate::log_info!($($arg)+);
119
120        let timestamp = Local::now().format("%d-%H:%M:%S%.3f").to_string();
121        let mut res_file = $crate::logger::RESULTS_FILE.lock().unwrap();
122        writeln!(res_file,"{} {}",timestamp, format!($($arg)+)).unwrap();
123    }};
124}
125
126#[macro_export]
127macro_rules! log_info {
128    ($($arg:tt)+) => {{
129        if log::log_enabled!(log::Level::Info) {
130            log::info!($($arg)+);
131        }
132        else {
133            std::println!($($arg)+);
134        }
135    }};
136}
137
138#[macro_export]
139macro_rules! log_error {
140    ($($arg:tt)+) => {{
141        if log::log_enabled!(log::Level::Error) {
142            log::error!($($arg)+);
143        }
144        else {
145            std::println!($($arg)+);
146        }
147    }};
148}
149
150#[macro_export]
151macro_rules! log_warn {
152    ($($arg:tt)+) => {{
153        if log::log_enabled!(log::Level::Warn) {
154            log::warn!($($arg)+);
155        }
156        else {
157            std::println!($($arg)+);
158        }
159    }};
160}
161
162#[macro_export]
163macro_rules! log_debug {
164    ($($arg:tt)+) => {{
165        if log::log_enabled!(log::Level::Debug) {
166            log::debug!($($arg)+);
167        }
168        else {
169            std::println!($($arg)+);
170        }
171    }};
172}
173
174#[macro_export]
175macro_rules! log_trace {
176    ($($arg:tt)+) => {{
177        if log::log_enabled!(log::Level::Trace) {
178            log::trace!($($arg)+);
179        }
180        else {
181            std::println!($($arg)+);
182        }
183    }};
184}
185
186#[cfg(test)]
187mod tests {
188    use crate::logger::*;
189
190    #[test]
191    fn logger_test() {
192        log_info!("INFO Test TO PRINTLN!");
193        log_debug!("DEBUG Test TO PRINTLN!");
194
195        let _ = terminal_logger_init(LevelFilter::Debug);
196
197        log_info!("INFO Test TO LOGGER!");
198        log_warn!("WARN Test TO LOGGER!");
199        log_error!("ERROR Test TO LOGGER!");
200        log_debug!("DEBUG Test TO LOGGER!");
201    }
202
203    #[test]
204    fn results_logger_test() {
205        log_info!("INFO Test TO PRINTLN!");
206        results_info!("RESULTS Test from PRINTLN!");
207
208        let _ = terminal_logger_init(LevelFilter::Debug);
209
210        log_info!("INFO Test TO LOGGER!");
211        results_info!("RESULTS Test from LOGGER!");
212
213        log_info!("INFO Test TO LOGGER!");
214        results_info!(info, "{}", "RESULTS Test from LOGGER2!");
215        results_info!("{}", "RESULTS Test from LOGGER3!");
216        let a = "results";
217        results_info!(mode: a, "{}", "TEST");
218        let b = "info";
219        results_info!(mode: b, "{}", "TEST2");
220        let b = "warn";
221        results_info!(mode: b, "{}", "TEST3");
222    }
223}