//! # log4r - A log4j-like logging framework for Rust //! //! This crate provides a simple, extensible logging framework similar to log4j. //! //! ## Example //! //! ``` //! use log4r::{LogLevel, Logger}; //! //! Logger::init(LogLevel::Debug); //! //! log4r::trace!("This is a trace message"); //! log4r::debug!("This is a debug message"); //! log4r::info!("This is an info message"); //! log4r::warn!("This is a warning message"); //! log4r::error!("This is an error message"); //! ``` use std::{sync::{Mutex, OnceLock}, time}; /// Log levels compatible with log4j #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum LogLevel { /// The "trace" level. Trace, /// The "debug" level. Debug, /// The "info" level. Info, /// The "warn" level. Warn, /// The "error" level. Error, } impl LogLevel { /// Returns the string representation of the log level pub fn as_str(&self) -> &'static str { match self { LogLevel::Trace => "TRACE", LogLevel::Debug => "DEBUG", LogLevel::Info => "INFO", LogLevel::Warn => "WARN", LogLevel::Error => "ERROR", } } } /// Log event containing all information about a log message pub struct LogEvent<'a> { /// The level of the log event pub level: LogLevel, /// The message to log pub message: &'a str, /// The timestamp when the event occurred pub timestamp: std::time::SystemTime, } /// Trait for formatting log events into strings pub trait Layout: Send + Sync { /// Format a log event into a string fn format(&self, event: &LogEvent) -> String; } /// Simple layout that formats log events as "[LEVEL] message" pub struct SimpleLayout; impl Layout for SimpleLayout { fn format(&self, event: &LogEvent) -> String { format!("[{:<5}] {} {}", event.level.as_str(), get_now_str(event.timestamp), event.message) } } /// Trait for output targets (console, file, etc.) pub trait Appender: Send + Sync { /// Append a log event to the output target fn append(&self, event: &LogEvent); } /// Console appender that writes to stdout pub struct ConsoleAppender { layout: Box, } impl ConsoleAppender { /// Create a new console appender with a specific layout pub fn new(layout: Box) -> Self { Self { layout } } } impl Appender for ConsoleAppender { fn append(&self, event: &LogEvent) { println!("{}", self.layout.format(event)); } } static LOGGER: OnceLock>> = OnceLock::new(); /// The logger struct that holds the current log level pub struct Logger { level: LogLevel, appenders: Vec>, } fn get_now_str(sys_time: time::SystemTime) -> String { use chrono::{Local, DateTime}; let dt = DateTime::::from(sys_time); return dt.format("%Y-%m-%d %H:%M:%S").to_string(); } impl Logger { /// Initialize the logger with a specific log level pub fn init(level: LogLevel) { let console_appender = Box::new(ConsoleAppender::new(Box::new(SimpleLayout))); let logger = Logger { level, appenders: vec![console_appender], }; LOGGER.set(Mutex::new(Some(logger))).ok(); } /// Get the current logger instance pub fn get() -> Option>> { LOGGER.get().map(|logger| logger.lock().unwrap()) } /// Check if a message at the specified level should be logged fn should_log(&self, level: LogLevel) -> bool { level >= self.level } /// Log a message at the specified level pub fn log(&self, level: LogLevel, message: &str) { if self.should_log(level) { let event = LogEvent { level, message, timestamp: std::time::SystemTime::now(), }; for appender in &self.appenders { appender.append(&event); } } } } /// Macro for logging trace messages #[macro_export] macro_rules! trace { ($($arg:tt)*) => { if let Some(guard) = $crate::Logger::get() { if let Some(logger) = guard.as_ref() { logger.log($crate::LogLevel::Trace, &format!($($arg)*)); } } }; } /// Macro for logging debug messages #[macro_export] macro_rules! debug { ($($arg:tt)*) => { if let Some(guard) = $crate::Logger::get() { if let Some(logger) = guard.as_ref() { logger.log($crate::LogLevel::Debug, &format!($($arg)*)); } } }; } /// Macro for logging info messages #[macro_export] macro_rules! info { ($($arg:tt)*) => { if let Some(guard) = $crate::Logger::get() { if let Some(logger) = guard.as_ref() { logger.log($crate::LogLevel::Info, &format!($($arg)*)); } } }; } /// Macro for logging warning messages #[macro_export] macro_rules! warn { ($($arg:tt)*) => { if let Some(guard) = $crate::Logger::get() { if let Some(logger) = guard.as_ref() { logger.log($crate::LogLevel::Warn, &format!($($arg)*)); } } }; } /// Macro for logging error messages #[macro_export] macro_rules! error { ($($arg:tt)*) => { if let Some(guard) = $crate::Logger::get() { if let Some(logger) = guard.as_ref() { logger.log($crate::LogLevel::Error, &format!($($arg)*)); } } }; }