zlogger 0.0.1

A lightweight, configurable Rust logging library with color support and file rotation
Documentation
use crate::config::{Config, LogLevel, OutputTarget};
use crate::formatter::Formatter;
use crate::rotation::FileRotator;
use std::fmt::Arguments;
use std::io::{self, Write};
use std::sync::{Arc, Mutex, OnceLock};

static GLOBAL_LOGGER: OnceLock<Arc<Mutex<Logger>>> = OnceLock::new();

pub struct Logger {
    config: Config,
    formatter: Formatter,
    file_rotator: Option<FileRotator>,
}

impl Logger {
    pub fn new(config: Config) -> io::Result<Self> {
        let formatter = Formatter::new(&config);
        let file_rotator = if config.file_path.is_some() {
            Some(FileRotator::new(&config)?)
        } else {
            None
        };

        Ok(Logger {
            config,
            formatter,
            file_rotator,
        })
    }

    pub fn init(config: Config) -> io::Result<()> {
        let logger = Arc::new(Mutex::new(Logger::new(config)?));
        GLOBAL_LOGGER.set(logger).map_err(|_| {
            io::Error::new(io::ErrorKind::Other, "Logger already initialized")
        })?;
        Ok(())
    }

    pub fn log(&mut self, level: LogLevel, args: Arguments) -> io::Result<()> {
        if level < self.config.level {
            return Ok(());
        }

        let formatted_message = self.formatter.format(level, args);

        match &self.config.output {
            OutputTarget::Console => {
                self.write_to_console(&formatted_message)?;
            }
            OutputTarget::File => {
                self.write_to_file(&formatted_message)?;
            }
            OutputTarget::Both => {
                self.write_to_console(&formatted_message)?;
                self.write_to_file(&formatted_message)?;
            }
        }

        Ok(())
    }

    fn write_to_console(&self, message: &str) -> io::Result<()> {
        let mut stdout = io::stdout();
        stdout.write_all(message.as_bytes())?;
        stdout.write_all(b"\n")?;
        stdout.flush()?;
        Ok(())
    }

    fn write_to_file(&mut self, message: &str) -> io::Result<()> {
        if let Some(ref mut rotator) = self.file_rotator {
            rotator.write_log(message)?;
        }
        Ok(())
    }
}

pub fn log(level: LogLevel, args: Arguments) {
    if let Some(logger) = GLOBAL_LOGGER.get() {
        if let Ok(mut logger) = logger.lock() {
            let _ = logger.log(level, args);
        }
    }
}