log4rc 0.1.1

A log4j-like logging framework for Rust
Documentation
//! # 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<dyn Layout>,
}

impl ConsoleAppender {
    /// Create a new console appender with a specific layout
    pub fn new(layout: Box<dyn Layout>) -> Self {
        Self { layout }
    }
}

impl Appender for ConsoleAppender {
    fn append(&self, event: &LogEvent) {
        println!("{}", self.layout.format(event));
    }
}

static LOGGER: OnceLock<Mutex<Option<Logger>>> = OnceLock::new();

/// The logger struct that holds the current log level
pub struct Logger {
    level: LogLevel,
    appenders: Vec<Box<dyn Appender>>,
}
fn get_now_str(sys_time: time::SystemTime) -> String {
    use chrono::{Local, DateTime};
    let dt = DateTime::<Local>::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<std::sync::MutexGuard<'static, Option<Logger>>> {
        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)*));
            }
        }
    };
}