use lazy_static::lazy_static;
use parking_lot::RwLock;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use crate::handler::Handler;
use crate::level::LogLevel;
use crate::record::Record;
use crate::AsyncLoggerBuilder;
use crate::AsyncLoggerHandle;
#[macro_export]
macro_rules! debug_println {
($($arg:tt)*) => {
#[cfg(feature = "debug_logging")]
println!(
"log_sync: record level = {:?}, logger level = {:?}",
record.level(),
self.level
);
};
}
#[derive(Debug)]
pub struct Logger {
level: LogLevel,
handlers: Vec<Arc<RwLock<dyn Handler>>>,
async_mode: bool,
async_handle: Option<AsyncLoggerHandle>,
active: Arc<AtomicBool>,
}
impl Logger {
pub fn new(level: LogLevel) -> Self {
Self {
level,
handlers: Vec::new(),
async_mode: false,
async_handle: None,
active: Arc::new(AtomicBool::new(true)),
}
}
pub fn level(&self) -> LogLevel {
self.level
}
pub fn set_level(&mut self, level: LogLevel) -> &mut Self {
self.level = level;
self
}
pub fn add_handler(&mut self, handler: Arc<RwLock<dyn Handler>>) -> &mut Self {
self.handlers.push(handler);
self
}
pub fn remove_handler(&mut self, handler: Arc<RwLock<dyn Handler>>) -> &mut Self {
self.handlers.retain(|h| !Arc::ptr_eq(h, &handler));
self
}
pub fn set_async(&mut self, enable: bool, queue_size: Option<usize>) -> &mut Self {
if self.async_mode == enable {
return self;
}
if enable {
let builder = AsyncLoggerBuilder::new()
.with_queue_size(queue_size.unwrap_or(10000))
.with_handlers(self.handlers.clone())
.with_level(self.level);
self.async_handle = Some(builder.build());
self.async_mode = true;
} else {
if let Some(handle) = self.async_handle.take() {
handle.shutdown();
}
self.async_mode = false;
}
self
}
pub fn set_worker_threads(&mut self, count: usize) -> &mut Self {
if self.async_mode && self.async_handle.is_some() {
self.set_async(false, None);
let builder = AsyncLoggerBuilder::new()
.with_queue_size(10000)
.with_handlers(self.handlers.clone())
.with_level(self.level)
.with_workers(count);
self.async_handle = Some(builder.build());
self.async_mode = true;
}
self
}
pub fn log(&self, record: &Record) -> bool {
if record.level() >= self.level() && self.active.load(Ordering::Relaxed) {
if self.async_mode {
if let Some(handle) = &self.async_handle {
return handle.log(record.clone());
}
}
return self.log_sync(record);
}
false
}
fn log_sync(&self, record: &Record) -> bool {
let mut any_handled = false;
for handler in &self.handlers {
let guard = handler.write();
if guard.is_enabled() && record.level() >= guard.level() && guard.handle(record).is_ok()
{
any_handled = true;
}
}
any_handled
}
pub fn log_message(&self, level: LogLevel, message: impl Into<String>) -> bool {
let record = Record::new(level, message, None::<String>, None::<String>, None);
self.log(&record)
}
pub fn debug(&self, message: impl Into<String>) -> bool {
self.log_message(LogLevel::Debug, message)
}
pub fn info(&self, message: impl Into<String>) -> bool {
self.log_message(LogLevel::Info, message)
}
pub fn warn(&self, message: impl Into<String>) -> bool {
self.log_message(LogLevel::Warning, message)
}
pub fn error(&self, message: impl Into<String>) -> bool {
self.log_message(LogLevel::Error, message)
}
pub fn set_enabled(&self, enabled: bool) -> &Self {
self.active.store(enabled, Ordering::Relaxed);
self
}
pub fn is_enabled(&self) -> bool {
self.active.load(Ordering::Relaxed)
}
pub fn handlers(&self) -> &[Arc<RwLock<dyn Handler>>] {
&self.handlers
}
pub fn is_async(&self) -> bool {
self.async_mode
}
}
impl Clone for Logger {
fn clone(&self) -> Self {
Self {
level: self.level,
handlers: self.handlers.clone(),
async_mode: self.async_mode,
async_handle: self.async_handle.clone(),
active: Arc::new(AtomicBool::new(self.active.load(Ordering::Relaxed))),
}
}
}
lazy_static! {
static ref GLOBAL_LOGGER: RwLock<Logger> = RwLock::new(Logger::new(LogLevel::Info));
}
pub fn init(logger: Logger) -> Logger {
let mut global = GLOBAL_LOGGER.write();
global.set_level(logger.level());
global.set_enabled(logger.is_enabled());
global.handlers.clear();
for handler in logger.handlers() {
global.add_handler(handler.clone());
}
logger
}
pub fn global() -> &'static RwLock<Logger> {
&GLOBAL_LOGGER
}
pub fn log(record: &Record) -> bool {
GLOBAL_LOGGER.read().log(record)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::handler::NullHandler;
#[test]
fn test_logger_creation() {
let logger = Logger::new(LogLevel::Info);
assert_eq!(logger.level(), LogLevel::Info);
assert!(logger.handlers.is_empty());
}
#[test]
fn test_logger_level() {
let mut logger = Logger::new(LogLevel::Info);
logger.set_level(LogLevel::Debug);
assert_eq!(logger.level(), LogLevel::Debug);
}
#[test]
fn test_logger_handler() {
let mut logger = Logger::new(LogLevel::Info);
let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
logger.add_handler(handler.clone());
assert_eq!(logger.handlers.len(), 1);
logger.remove_handler(handler);
assert!(logger.handlers.is_empty());
}
#[test]
fn test_logger_log() {
let mut logger = Logger::new(LogLevel::Info);
let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
logger.add_handler(handler);
assert!(!logger.debug("Debug message")); assert!(logger.info("Info message")); assert!(logger.warn("Warning message")); assert!(logger.error("Error message")); }
#[test]
fn test_logger_level_filtering() {
let mut logger = Logger::new(LogLevel::Warning);
let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
logger.add_handler(handler);
assert!(!logger.debug("Debug message")); assert!(!logger.info("Info message")); assert!(logger.warn("Warning message")); assert!(logger.error("Error message")); }
#[test]
fn test_logger_handler_filtering() {
let mut logger = Logger::new(LogLevel::Info);
let mut handler = NullHandler::new(LogLevel::Info);
handler.set_level(LogLevel::Warning);
let handler = Arc::new(RwLock::new(handler));
logger.add_handler(handler);
assert!(!logger.debug("Debug message")); assert!(!logger.info("Info message")); assert!(logger.warn("Warning message")); assert!(logger.error("Error message")); }
#[test]
fn test_logger_disabled_handler() {
let mut logger = Logger::new(LogLevel::Info);
let mut handler = NullHandler::new(LogLevel::Info);
handler.set_enabled(false);
let handler = Arc::new(RwLock::new(handler));
logger.add_handler(handler);
assert!(!logger.info("Info message")); }
#[test]
fn test_logger_init() {
let logger = Logger::new(LogLevel::Info);
let logger = init(logger);
assert_eq!(logger.level(), LogLevel::Info);
}
#[test]
fn test_logger_enabled() {
let logger = Logger::new(LogLevel::Info);
assert!(logger.is_enabled());
logger.set_enabled(false);
assert!(!logger.is_enabled());
logger.set_enabled(true);
assert!(logger.is_enabled());
}
#[test]
fn test_async_logging() {
let mut logger = Logger::new(LogLevel::Info);
let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
logger.add_handler(handler);
assert!(logger.info("Sync message"));
assert!(!logger.is_async());
logger.set_async(true, Some(1000));
assert!(logger.is_async());
assert!(logger.info("Async message"));
logger.set_worker_threads(2);
assert!(logger.info("Message with 2 workers"));
logger.set_async(false, None);
assert!(!logger.is_async());
assert!(logger.info("Sync message again"));
}
#[test]
fn test_compile_time_filtering() {
let logger = Logger::new(LogLevel::Info);
if !crate::compile_time_level_enabled!(LogLevel::Debug) {
assert!(!logger.debug("Debug message"));
}
if crate::compile_time_level_enabled!(LogLevel::Info) {
let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
let mut logger = Logger::new(LogLevel::Info);
logger.add_handler(handler);
assert!(logger.info("Info message"));
}
}
}