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