use std::fs::{self, OpenOptions}; use std::io::Write; use std::path::Path; use chrono::Utc; use colored::Colorize; use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; pub(crate) struct ServerLogger; static LOGGER: ServerLogger = ServerLogger; impl ServerLogger { pub(crate) fn init() -> Result<(), SetLoggerError> { log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info)) } fn log_to_console(record: &Record) { let formatted_message = format!( "[{}] [{}] [{}:{}]\n{}", Utc::now().to_rfc3339(), match record.level() { Level::Error => "ERROR".red(), Level::Warn => "WARN".yellow(), Level::Info => "INFO".green(), Level::Debug => "DEBUG".blue(), Level::Trace => "TRACE".blue(), }, record.file().expect("Failed to get file name!"), record.line().expect("Failed to get line number!"), record.args() ); println!("{formatted_message}") } fn log_to_file(record: &Record) { let formatted_message = format!( "[{}] [{}] [{}:{}]\n{}", Utc::now().to_rfc3339(), record.level(), record.file().expect("Failed to get file name!"), record.line().expect("Failed to get line number!"), record.args() ); let logging_dir = Path::new("./logs"); if !logging_dir.exists() { fs::create_dir_all(logging_dir).expect("Failed to create directory!"); } let mut logging_file = OpenOptions::new() .append(true) .create(true) .open(logging_dir.join("server_log.txt")) .expect("Failed to create logging file!"); writeln!(logging_file, "{formatted_message}").expect("Failed to write into logging file!"); } } impl Log for ServerLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Info } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { ServerLogger::log_to_console(record); ServerLogger::log_to_file(record); } } fn flush(&self) {} }