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 DUMMY: log::Metadata<'static> = log::MetadataBuilder::new().build();
81
82 pub static ref RESULTS_FILE: Mutex<File> = {
84 let mut log_path = std::env::temp_dir();
85 log_path.push("utils-results.log");
86
87 Mutex::new(File::options()
88 .append(true)
89 .create(true)
90 .open(log_path)
91 .expect("Results File FAILED!"))
92 };
93}
94
95pub enum LoggerPrintMode {
96 Results,
97 Info,
98}
99
100#[macro_export]
101macro_rules! results_info {
102 (mode:$mode:expr, $($arg:tt)+) => {{
103 if $mode == "results" {
104 $crate::results_info!($($arg)+);
105 }
106 else {
107 $crate::results_info!(info, $($arg)+);
108 }
109 }};
110
111 (info, $($arg:tt)+) => {{
112 $crate::log_info!($($arg)+);
113 }};
114
115 ($($arg:tt)+) => {{
116 use std::io::Write;
117 use chrono::Local;
118
119 $crate::log_info!($($arg)+);
120
121 let timestamp = Local::now().format("%d-%H:%M:%S%.3f").to_string();
122 let mut res_file = $crate::logger::RESULTS_FILE.lock().unwrap();
123 writeln!(res_file,"{} {}",timestamp, format!($($arg)+)).unwrap();
124 }};
125}
126
127#[macro_export]
128macro_rules! log_info {
129 ($($arg:tt)+) => {{
130 if log::log_enabled!(log::Level::Trace) {
131 if log::logger().enabled(&$crate::logger::DUMMY) {
132 log::info!($($arg)+);
133 }
134 else {
135 std::println!($($arg)+);
136 }
137 }
138 }};
139}
140
141#[macro_export]
142macro_rules! log_warn {
143 ($($arg:tt)+) => {{
144 if log::log_enabled!(log::Level::Warn) {
145 if log::logger().enabled(&$crate::logger::DUMMY) {
146 log::warn!($($arg)+);
147 }
148 else {
149 std::println!($($arg)+);
150 }
151 }
152 }};
153}
154
155#[macro_export]
156macro_rules! log_debug {
157 ($($arg:tt)+) => {{
158 if log::log_enabled!(log::Level::Debug) {
159 if log::logger().enabled(&$crate::logger::DUMMY) {
160 log::debug!($($arg)+);
161 }
162 else {
163 std::println!($($arg)+);
164 }
165 }
166 }};
167}
168
169#[macro_export]
170macro_rules! log_trace {
171 ($($arg:tt)+) => {{
172 if log::log_enabled!(log::Level::Trace) {
173 if log::logger().enabled(&$crate::logger::DUMMY) {
174 log::trace!($($arg)+);
175 }
176 else {
177 std::println!($($arg)+);
178 }
179 }
180 }};
181}
182
183#[macro_export]
184macro_rules! log_error {
185 ($($arg:tt)+) => {{
186 if log::log_enabled!(log::Level::Error) {
187 if log::logger().enabled(&$crate::logger::DUMMY) {
188 log::error!($($arg)+);
189 }
190 else {
191 std::println!($($arg)+);
192 }
193 }
194 }};
195}
196
197#[cfg(test)]
198mod tests {
199 use crate::logger::*;
200 use tempfile::tempdir;
201
202 #[test]
203 fn terminal_logger_init_test() {
204 log_info!("INFO Test TO PRINTLN!");
205 log_debug!("DEBUG Test TO PRINTLN!");
206
207 if !log::logger().enabled(&crate::logger::DUMMY) {
208 terminal_logger_init(LevelFilter::Debug).unwrap();
209 }
210
211 log_info!("INFO Test TO LOGGER!");
212 log_debug!("DEBUG Test TO LOGGER!");
213 }
214
215 #[test]
216 fn combined_logger_init_test() {
217 let log_dir = tempdir().expect("Failed to create temp directory!");
218
219 log_info!("INFO Test TO PRINTLN!");
220 log_debug!("DEBUG Test TO PRINTLN!");
221
222 if !log::logger().enabled(&crate::logger::DUMMY) {
223 combined_logger_init(
224 LevelFilter::Debug,
225 LevelFilter::Trace,
226 log_dir.path().to_str().unwrap(),
227 "test",
228 )
229 .unwrap();
230 }
231
232 log_info!("INFO Test TO combined LOGGER!");
233 log_debug!("DEBUG Test TO combined LOGGER!");
234 }
235
236 #[test]
237 fn results_macros_test() {
238 log_info!("INFO Test TO PRINTLN!");
239 results_info!("RESULTS Test from PRINTLN!");
240
241 if !log::logger().enabled(&crate::logger::DUMMY) {
242 terminal_logger_init(LevelFilter::Debug).unwrap();
243 }
244
245 log_info!("INFO Test TO LOGGER!");
246 results_info!("RESULTS Test from LOGGER!");
247
248 log_info!("INFO Test TO LOGGER!");
249 results_info!(info, "{}", "RESULTS Test from LOGGER2!");
250 results_info!("{}", "RESULTS Test from LOGGER3!");
251 let a = "results";
252 results_info!(mode: a, "{}", "TEST");
253 let b = "info";
254 results_info!(mode: b, "{}", "TEST2");
255 }
256}