use rat_logger::{Level, LevelFilter};
use rat_logger::LoggerBuilder;
use rat_logger::config::{FileConfig, NetworkConfig};
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use chrono::Local;
pub use rat_logger::{error, warn, info, debug, trace};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}
impl From<LogLevel> for Level {
fn from(level: LogLevel) -> Self {
match level {
LogLevel::Error => Level::Error,
LogLevel::Warn => Level::Warn,
LogLevel::Info => Level::Info,
LogLevel::Debug => Level::Debug,
LogLevel::Trace => Level::Trace,
}
}
}
impl From<LogLevel> for LevelFilter {
fn from(level: LogLevel) -> Self {
match level {
LogLevel::Error => LevelFilter::Error,
LogLevel::Warn => LevelFilter::Warn,
LogLevel::Info => LevelFilter::Info,
LogLevel::Debug => LevelFilter::Debug,
LogLevel::Trace => LevelFilter::Trace,
}
}
}
#[derive(Debug, Clone)]
pub enum LogOutput {
Terminal,
File {
log_dir: PathBuf,
max_file_size: u64,
max_compressed_files: u32,
},
Udp {
server_addr: String,
server_port: u16,
auth_token: String,
app_id: String,
},
}
#[derive(Debug, Clone)]
pub struct LogConfig {
pub enabled: bool,
pub level: LogLevel,
pub output: LogOutput,
pub use_colors: bool,
pub use_emoji: bool,
pub show_timestamp: bool,
pub show_module: bool,
}
impl Default for LogConfig {
fn default() -> Self {
LogConfig {
enabled: true,
level: LogLevel::Info,
output: LogOutput::Terminal,
use_colors: true,
use_emoji: true,
show_timestamp: true,
show_module: true,
}
}
}
impl LogConfig {
pub fn disabled() -> Self {
LogConfig {
enabled: false,
..Default::default()
}
}
pub fn file<P: Into<PathBuf>>(log_dir: P) -> Self {
LogConfig {
enabled: true,
level: LogLevel::Info,
output: LogOutput::File {
log_dir: log_dir.into(),
max_file_size: 10 * 1024 * 1024, max_compressed_files: 5,
},
use_colors: false, use_emoji: false, show_timestamp: true,
show_module: true,
}
}
pub fn udp(server_addr: String, server_port: u16, auth_token: String, app_id: String) -> Self {
LogConfig {
enabled: true,
level: LogLevel::Info,
output: LogOutput::Udp {
server_addr,
server_port,
auth_token,
app_id,
},
use_colors: false, use_emoji: false, show_timestamp: true,
show_module: true,
}
}
}
fn rat_engine_format(
buf: &mut dyn Write,
record: &rat_logger::config::Record
) -> std::io::Result<()> {
let level = record.metadata.level;
let (level_color, level_bg, level_icon) = match level {
Level::Error => ("\x1b[97m", "\x1b[41m", "❌"), Level::Warn => ("\x1b[30m", "\x1b[43m", "⚠️ "), Level::Info => ("\x1b[97m", "\x1b[44m", "🚀"), Level::Debug => ("\x1b[30m", "\x1b[46m", "🔧"), Level::Trace => ("\x1b[97m", "\x1b[45m", "🔍"), };
let timestamp_color = "\x1b[90m"; let message_color = "\x1b[37m"; let reset = "\x1b[0m";
let now = Local::now();
let timestamp = now.format("%H:%M:%S%.3f");
writeln!(
buf,
"{}{} {}{}{:5}{} {} {}{}{}",
timestamp_color, timestamp, level_color, level_bg, level, reset, level_icon, message_color, record.args, reset )
}
fn file_format(
buf: &mut dyn Write,
record: &rat_logger::config::Record
) -> std::io::Result<()> {
let now = Local::now();
let timestamp = now.format("%Y-%m-%d %H:%M:%S%.3f");
writeln!(
buf,
"[{}] [{}] [RAT-Engine] {}",
timestamp,
record.metadata.level,
record.args
)
}
fn udp_format(
buf: &mut dyn Write,
record: &rat_logger::config::Record
) -> std::io::Result<()> {
let now = Local::now();
let timestamp = now.format("%H:%M:%S%.3f");
writeln!(
buf,
"[{}] {} {}",
timestamp,
record.metadata.level,
record.args
)
}
pub struct Logger;
impl Logger {
pub fn init(config: LogConfig) -> Result<(), Box<dyn std::error::Error>> {
if !config.enabled {
return Ok(());
}
let mut builder = LoggerBuilder::new();
builder.filter(LevelFilter::from(config.level));
match &config.output {
LogOutput::Terminal => {
builder.format(rat_engine_format);
}
LogOutput::File { log_dir, max_file_size, max_compressed_files } => {
let mut file_config = FileConfig::new();
file_config.log_dir = log_dir.clone();
file_config.max_file_size = *max_file_size;
file_config.max_compressed_files = *max_compressed_files as usize;
builder.format(file_format);
builder.with_file_logging(file_config);
}
LogOutput::Udp { server_addr, server_port, auth_token, app_id } => {
let network_config = NetworkConfig {
server_addr: server_addr.clone(),
server_port: *server_port,
auth_token: Arc::from(auth_token.as_str()),
app_id: app_id.clone(),
};
builder.format(udp_format);
builder.with_udp_logging(network_config);
}
}
match builder.try_init() {
Ok(_) => Ok(()),
Err(e) => {
eprintln!("Logger init warning: {}", e);
Ok(())
}
}
}
pub fn init_default() -> Result<(), Box<dyn std::error::Error>> {
Self::init(LogConfig::default())
}
}
pub fn init_logger() -> Result<(), Box<dyn std::error::Error>> {
Logger::init_default()
}
pub fn init_logger_with_config(config: LogConfig) -> Result<(), Box<dyn std::error::Error>> {
Logger::init(config)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logger_init() {
let result = Logger::init_default();
match result {
Ok(_) => println!("Logger initialized successfully"),
Err(_) => println!("Logger already initialized"),
}
}
#[test]
fn test_log_levels() {
let _ = Logger::init_default();
error!("Test error message");
warn!("Test warning message");
info!("Test info message");
debug!("Test debug message");
trace!("Test trace message");
}
}