logger_rust/
log_file.rs

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