205 lines
5.4 KiB
Rust
205 lines
5.4 KiB
Rust
//! # 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)*));
|
|
}
|
|
}
|
|
};
|
|
} |