1use 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
14pub fn terminal_logger_init(level: LevelFilter) -> Result<()> {
16 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
30pub 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}