1#[cfg(test)]
12mod tests;
13
14#[doc = include_str!("../README.md")]
15mod fileio;
16mod setters;
17mod json;
18
19pub mod colors;
20pub mod config;
21
22use fileio::append_to_file;
23use chrono::{Local, DateTime};
24use serde::{Serialize, Deserialize};
25use colors::*;
26use config::*;
27
28#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default,
41 Serialize, Deserialize)]
42pub struct Logger {
43 pub(crate) console_out_enabled: bool,
44
45 pub(crate) use_custom_log_buffer: bool,
46
47 pub(crate) verbosity: Verbosity,
48 pub(crate) filtering_enabled: bool,
49 pub(crate) log_header_color_enabled: bool,
50
51 pub(crate) debug_color: Color,
52 pub(crate) info_color: Color,
53 pub(crate) warning_color: Color,
54 pub(crate) error_color: Color,
55 pub(crate) fatal_color: Color,
56
57 pub(crate) debug_header: String,
58 pub(crate) info_header: String,
59 pub(crate) warning_header: String,
60 pub(crate) error_header: String,
61 pub(crate) fatal_header: String,
62
63 pub(crate) log_format: String,
64 pub(crate) datetime_format: String,
65
66 pub(crate) file_logging_enabled: bool,
67 pub(crate) log_file_path: String,
68 pub(crate) file_log_buffer_max_size: u32,
69 pub(crate) on_drop_policy: OnDropPolicy,
70
71 #[serde(skip)]
73 pub(crate) custom_log_buffer: Vec<LogStruct>,
74 #[serde(skip)]
75 pub(crate) file_log_buffer: Vec<LogStruct>,
76 #[serde(skip)]
77 pub(crate) show_datetime: bool,
78 #[serde(skip)]
79 pub(crate) log_file_lock: bool,
80 #[serde(skip)]
81 pub(crate) log_count: u128,
82}
83
84impl Logger {
85 pub(crate) fn get_log_headers(&self, log: &LogStruct)
86 -> (String, String, String) {
87 let header = self.get_log_type_header(log.log_type);
88 let datetime = self.get_datetime_formatted(&log.datetime);
89 return (header, datetime, log.message.clone());
90 }
91
92 pub(crate) fn get_log_type_header(&self, log_type: LogType) -> String {
93 match log_type {
94 LogType::Debug => {
95 self.colorify(&self.debug_header,
96 self.log_header_color(log_type))
97 }
98 LogType::Info => {
99 self.colorify(&self.info_header,
100 self.log_header_color(log_type))
101 }
102 LogType::Warning => {
103 self.colorify(&self.warning_header,
104 self.log_header_color(log_type))
105 }
106 LogType::Err => {
107 self.colorify(&self.error_header,
108 self.log_header_color(log_type))
109 }
110 LogType::FatalError => {
111 self.colorify(&self.fatal_header,
112 self.log_header_color(log_type))
113 }
114 }
115 }
116
117 pub(crate) fn get_datetime_formatted(&self,
118 datetime: &DateTime<Local>) -> String {
119 if self.show_datetime {
120 let datetime_formatted = datetime.format(&self.datetime_format);
121 return datetime_formatted.to_string();
122 }
123 else {
124 return String::from("");
125 }
126 }
127
128 pub(crate) fn colorify(&self, text: &str, color: Color) -> String {
129 if self.log_header_color_enabled {
130 return color_text(text, color);
131 }
132 return text.to_string();
133 }
134
135 pub(crate) fn filter_log(&self, log_type: LogType) -> bool {
136 if self.filtering_enabled {
137 return (log_type as i32) < self.verbosity as i32;
138 }
139 return false;
140 }
141
142 pub(crate) fn log_header_color(&self, log_type: LogType) -> Color {
143 match log_type {
144 LogType::Debug => { self.debug_color.clone() }
145 LogType::Info => { self.info_color.clone() }
146 LogType::Warning => { self.warning_color.clone() }
147 LogType::Err => { self.error_color.clone() }
148 LogType::FatalError => { self.fatal_color.clone() }
149 }
150 }
151
152 pub(crate) fn drop_flush(&mut self) {
153 if self.file_logging_enabled {
154 let _ = self.flush_file_log_buffer(true);
155 }
156 }
157
158 pub(crate) fn flush_file_log_buffer(&mut self, is_drop_flush: bool)
159 -> Result<(), String> {
160 if self.log_file_lock {
161 if is_drop_flush {
162 match self.on_drop_policy {
163 OnDropPolicy::IgnoreLogFileLock => { }
164 OnDropPolicy::DiscardLogBuffer => {
165 let message = format!("Log file lock enabled and on
166 drop policy set to {}!",
167 self.on_drop_policy);
168 return Err(message);
169 }
170 }
171 }
172 else {
173 return Err(String::from("Log file lock enabled!"))
174 }
175 }
176 let mut buf = String::from("");
177
178 for log in &self.file_log_buffer {
179 buf += &self.format_log(&log);
180 }
181
182 self.file_log_buffer = Vec::new();
183 let result = append_to_file(&self.log_file_path, &buf);
184
185 match result {
186 Ok(_) => Ok(()),
187 Err(_) => {
188 self.file_logging_enabled = false;
189 Err(String::from("Failed to write log buffer to a file!"))
190 },
191 }
192 }
193
194
195 pub fn default() -> Self {
197 let log_format = String::from("[%h] %m");
198 Logger {
199 console_out_enabled: true,
200
201 use_custom_log_buffer: false,
202
203 verbosity: Verbosity::default(),
204 filtering_enabled: true,
205 log_header_color_enabled: true,
206
207 debug_color: Color::Blue,
208 info_color: Color::Green,
209 warning_color: Color::Yellow,
210 error_color: Color::Red,
211 fatal_color: Color::Magenta,
212
213 debug_header: String::from("DBG"),
214 info_header: String::from("INF"),
215 warning_header: String::from("WAR"),
216 error_header: String::from("ERR"),
217 fatal_header: String::from("FATAL"),
218
219 log_format: log_format.clone(),
220 datetime_format: String::from("%Y-%m-%d %H:%M:%S"),
221
222 file_logging_enabled: false,
223 log_file_path: String::new(),
224 file_log_buffer_max_size: 128,
225 on_drop_policy: OnDropPolicy::default(),
226
227 custom_log_buffer: Vec::new(),
228 file_log_buffer: Vec::new(),
229 show_datetime: log_format.contains("%d"),
230 log_file_lock: false,
231 log_count: 1,
232 }
233 }
234
235
236 pub fn print_log(&mut self, log: &LogStruct) {
245 self.log_count += 1;
246 let log_str = self.format_log(log);
247
248 if self.console_out_enabled {
249 if log.log_type == LogType::Err
250 || log.log_type == LogType::FatalError {
251 eprint!("{}", log_str);
252 }
253 else {
254 print!("{}", log_str);
255 }
256 }
257
258 if self.use_custom_log_buffer {
259 self.custom_log_buffer.push(log.clone());
260 }
261
262 if self.file_logging_enabled {
263 self.file_log_buffer.push(log.clone());
264
265 if self.file_log_buffer_max_size != 0
266 && self.file_log_buffer.len() >=
267 self.file_log_buffer_max_size.try_into().unwrap() {
268 let _ = self.flush_file_log_buffer(false);
269 }
270 }
271 }
272
273 pub fn format_log(&self, log: &LogStruct) -> String {
283 let headers = self.get_log_headers(&log);
284 let mut result = String::new();
285 let mut char_iter = self.log_format.char_indices().peekable();
286
287 while let Some((_, c)) = char_iter.next() {
288 match c {
289 '%' => {
290 if let Some((_, nc)) = char_iter.peek() {
291 match nc {
292 'h' => {
293 result += &headers.0;
294 }
295 'd' => {
296 result += &headers.1;
297 }
298 'm' => {
299 result += &headers.2;
300 }
301 'c' => {
302 result += &self.log_count.to_string();
303 }
304 _ => {
305 result += &nc.to_string();
306 }
307 }
308 char_iter.next();
309 }
310 }
311 _ => {
312 result += &c.to_string();
313 }
314 }
315 }
316
317 result += &"\n";
318 return result;
319 }
320
321 pub fn flush(&mut self) -> Result<(), String> {
327 if self.file_logging_enabled {
328 self.flush_file_log_buffer(false)?;
329 }
330 return Ok(());
331 }
332
333 pub fn debug(&mut self, message: &str) {
335 if self.filter_log(LogType::Debug)
336 {
337 return;
338 }
339 let log = LogStruct::debug(message);
340 self.print_log(&log);
341 }
342
343 pub fn debug_no_filtering(&mut self, message: &str) {
345 let log = LogStruct::debug(message);
346 self.print_log(&log);
347 }
348
349 pub fn info(&mut self, message: &str) {
351 if self.filter_log(LogType::Info)
352 {
353 return;
354 }
355 let log = LogStruct::info(message);
356 self.print_log(&log);
357 }
358
359 pub fn info_no_filtering(&mut self, message: &str) {
361 let log = LogStruct::info(message);
362 self.print_log(&log);
363 }
364
365 pub fn warning(&mut self, message: &str) {
367 if self.filter_log(LogType::Warning)
368 {
369 return;
370 }
371 let log = LogStruct::warning(message);
372 self.print_log(&log);
373 }
374
375 pub fn warning_no_filtering(&mut self, message: &str) {
377 let log = LogStruct::warning(message);
378 self.print_log(&log);
379 }
380
381 pub fn error(&mut self, message: &str) {
383 let log = LogStruct::error(message);
384 self.print_log(&log);
385 }
386
387 pub fn fatal(&mut self, message: &str) {
389 let log = LogStruct::fatal_error(message);
390 self.print_log(&log);
391 }
392
393 pub fn log_buffer<'a>(&'a self) -> &'a Vec<LogStruct> {
395 return &self.custom_log_buffer;
396 }
397}
398
399impl Drop for Logger {
400 fn drop(&mut self) {
401 let _ = self.drop_flush();
402 }
403}