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