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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use chrono::Local;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::path::Path;
use std::sync::Mutex;

lazy_static::lazy_static! {
    static ref LOG_LEVEL: Mutex<LogLevel> = Mutex::new(LogLevel::Console);
}

#[derive(Copy, Clone)]
pub enum LogLevel {
    Console,
    File,
    Both,
}

pub fn set_log_level(level: LogLevel) {
    let mut log_level = LOG_LEVEL.lock().unwrap();
    *log_level = level;
}

fn log_to_file(now: &str, message: &str) -> io::Result<()> {
    let filename = format!("{}.log", Local::now().format("%Y-%m-%d"));
    let path = Path::new(&filename);
    let mut file = OpenOptions::new().create(true).append(true).open(path)?;
    writeln!(file, "{} {}", now, message)?;
    Ok(())
}

pub fn error(now: &str, message: &str) {
    let message = format!("\x1b[2m\x1b[1m{}\x1b[0m \x1b[31m\x1b[1m[ERROR]\x1b[0m \x1b[31m{}\x1b[0m", now, message);
    let log_level = 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));
        }
    }
}

pub fn warn(now: &str, message: &str) {
    // define warn function for printing warning messages
    eprintln!("\x1b[2m\x1b[1m{}\x1b[0m \x1b[33m\x1b[1m[WARN]\x1b[0m \x1b[33m{}\x1b[0m", now, message); // print formatted warning message to stderr
    let log_level = 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));
        }
    }
}

pub fn info(now: &str, message: &str) {
    // define info function for printing info messages
    println!("\x1b[2m\x1b[1m{}\x1b[0m \x1b[36m\x1b[1m[INFO]\x1b[0m \x1b[36m{}\x1b[0m", now, message); // print formatted info message to stdout
    let log_level = 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));
        }
    }
}

pub fn debug(now: &str, message: &str) {
    // define debug function for printing debug messages
    println!("\x1b[2m\x1b[1m{}\x1b[0m \x1b[34m\x1b[1m[DEBUG]\x1b[0m \x1b[34m{}\x1b[0m", now, message); // print formatted debug message to stdout
    let log_level = 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));
        }
    }
}

pub fn current_time() -> String {
    Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}

#[macro_export]
macro_rules! log_error {
    // define log_error macro for logging error messages
    ($($arg:tt)*) => {{
        let now = $crate::current_time();
        $crate::error(&now, &format!($($arg)*));
    }}
}

#[macro_export]
macro_rules! log_warn {
    // define log_warn macro for logging warning messages
    ($($arg:tt)*) => {{
        let now = $crate::current_time();
        $crate::warn(&now, &format!($($arg)*)); // call warn function with formatted arguments
    }}
}

#[macro_export]
macro_rules! log_info {
    // define log_info macro for logging info messages
    ($($arg:tt)*) => {{
        let now = $crate::current_time();
        $crate::info(&now, &format!($($arg)*)); // call info function with formatted arguments
    }}
}

#[macro_export]
macro_rules! log_debug {
    // define log_debug macro for logging info messages
    ($($arg:tt)*) => {{
        let now = $crate::current_time();
        $crate::debug(&now, &format!($($arg)*)); // call info function with formatted arguments
    }}
}