1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
pub use crate::config::{
LogVariables,
LogVariablesImpl,
LogLevel,
LOG_PATH, LOG_ROTATOR_CONFIG
};
use std::{
fs,
path::Path,
fs::OpenOptions,
io::{self, Write},
};
use chrono::Local;
pub fn log_to_file(now: &str, message: &str) -> io::Result<()> {
//! # log_to_file
//! The `log_to_file` function takes two arguments: `now` and `message`.
//! - The `now` argument is a string representing the current time and the message argument is the message to be logged.
//! The function checks if the log path is empty. If it is, it creates a new filename using the current date. If the log path is not empty,
//! it joins the log path with the filename.
//! The function then creates a new file at the specified path using the `OpenOptions` struct and writes the message to the file.
let log_path = LOG_PATH.lock().unwrap();
let filename = if log_path.as_os_str().is_empty() {
format!("{}.log", Local::now().format("%Y-%m-%d"))
} else {
log_path.join(format!("{}.log", Local::now().format("%Y-%m-%d"))).to_string_lossy().into_owned()
};
let path = Path::new(&filename);
// Check if we need to rotate the logs
if let Some(log_rotator_config) = &*LOG_ROTATOR_CONFIG.lock().unwrap() {
// Check if the current log file has exceeded the maximum size or lifetime
if let Ok(metadata) = fs::metadata(&path) {
if metadata.len() > log_rotator_config.max_size
|| metadata.modified()?.elapsed().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?
> log_rotator_config.max_time
{
// Rotate the logs
let mut i = 1;
loop {
let rotated_filename = format!("{}_rot-{}.log", filename.trim_end_matches(".log"), i);
let rotated_path = Path::new(&rotated_filename);
if !rotated_path.exists() {
fs::rename(&path, rotated_path)?;
break;
}
i += 1;
}
}
}
}
let mut file = OpenOptions::new().create(true).append(true).open(path)?;
writeln!(file, "{} {}", now, message)?;
Ok(())
}
pub fn log_message(level: &str, now: &str, message: &str) {
//! # log_message
//! The log_message function takes three arguments: level, now, and message.
//! - The `level` argument is a string representing the log level *(e.g. “ERROR”, “WARN”, “INFO”, “DEBUG”)*.
//! - The `now` argument is a string representing the current time and the message argument is the message to be logged.
//! - The `function` matches the log level with a color code and formats the message with the color code and log level.
//! It then checks the current log level and logs the message to either the console, a file, or both depending on the current log level.
let color_code = match level {
"ERROR" => "\x1b[31m", // red
"WARN" => "\x1b[33m", // yellow
"INFO" => "\x1b[36m", // cyan
"DEBUG" => "\x1b[34m", // blue
_ => "\x1b[0m", // reset
};
let message = format!(
"\x1b[1m\x1b[37m{}\x1b[0m {}[{}]\x1b[0m {}{}\x1b[0m",
now, color_code, level, color_code, message
);
let log_variables = LogVariablesImpl;
let log_level = log_variables.log_level().lock().unwrap();
match *log_level {
LogLevel::Console => eprintln!("{}", message),
LogLevel::File => {
log_to_file(now, &message).unwrap_or_else(|e| eprintln!("Failed to write to log file: {}", e));
}
LogLevel::Both => {
eprintln!("{}", message);
log_to_file(now, &message).unwrap_or_else(|e| eprintln!("Failed to write to log file: {}", e));
}
}
}