nonblocking-logger 0.3.0

A high-performance library with format string support
Documentation
use crate::Logger;
use crate::enums::log_level::LogLevel;
use std::env;
use std::io;

/// Parse log level from environment variables
///
/// This function checks for log level configuration in the following order:
/// 1. RUST_LOG environment variable (Rust convention)
/// 2. LOG_LEVEL environment variable (fallback)
///
/// Returns the parsed log level or the default (Info) if not found or invalid.
pub(crate) fn parse_log_level_from_env() -> LogLevel {
    if let Ok(rust_log) = env::var("RUST_LOG") {
        if let Ok(level) = rust_log.parse::<LogLevel>() {
            return level;
        }
    }

    if let Ok(log_level) = env::var("LOG_LEVEL") {
        if let Ok(level) = log_level.parse::<LogLevel>() {
            return level;
        }
    }

    LogLevel::Info
}

/// Create a logger configured from environment variables
///
/// This function creates a logger with log level determined by environment variables.
/// It checks RUST_LOG and LOG_LEVEL environment variables in that order.
pub fn logger_from_env() -> Logger {
    let level = parse_log_level_from_env();
    Logger::with_level(level)
}

/// Create a logger configured from environment variables with stdout target
pub fn stdout_logger_from_env() -> Logger {
    logger_from_env().stdout()
}

/// Create a logger configured from environment variables with stderr target
pub fn stderr_logger_from_env() -> Logger {
    logger_from_env().stderr()
}

/// Create a logger configured from environment variables with file target
pub fn file_logger_from_env(path: &str) -> io::Result<Logger> {
    logger_from_env().file(path)
}

/// Create a simple logger that writes to stdout
pub fn stdout_logger() -> Logger {
    Logger::new().stdout()
}

/// Create a simple logger that writes to stderr
pub fn stderr_logger() -> Logger {
    Logger::new().stderr()
}

/// Create a logger that writes to a file
pub fn file_logger(path: &str) -> io::Result<Logger> {
    Logger::new().file(path)
}

/// Create multiple loggers that write to both stdout and stderr
pub fn console_loggers() -> Vec<Logger> {
    vec![Logger::new().stdout(), Logger::new().stderr()]
}

/// Create a logger with custom time format
pub fn custom_time_logger(time_format: &str) -> Logger {
    Logger::new().time_format(time_format).stdout()
}

/// Log a message to multiple loggers
///
/// This function takes a vector of Logger instances and logs the message to all of them.
pub fn log_to_multiple(loggers: &[Logger], level: LogLevel, message: &str) -> io::Result<()> {
    for logger in loggers {
        logger.log_with_level(level, message)?;
    }
    Ok(())
}

/// Log a string message to multiple loggers
///
/// This function takes a vector of Logger instances and logs the string message to all of them.
pub fn log_str_to_multiple(loggers: &[Logger], level: LogLevel, message: &str) -> io::Result<()> {
    for logger in loggers {
        logger.log_with_level(level, message)?;
    }
    Ok(())
}


#[cfg(test)]
mod tests {
    use super::*;
    use std::env;

    #[test]
    fn test_parse_log_level_from_env() {
        unsafe {
            env::remove_var("RUST_LOG");
            env::remove_var("LOG_LEVEL");
        }

        unsafe {
            env::set_var("RUST_LOG", "debug");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Debug);

        unsafe {
            env::set_var("RUST_LOG", "error");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Error);

        unsafe {
            env::set_var("RUST_LOG", "trace");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Trace);

        unsafe {
            env::remove_var("RUST_LOG");
            env::remove_var("LOG_LEVEL");
            env::set_var("LOG_LEVEL", "warning");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Warning);

        unsafe {
            env::remove_var("LOG_LEVEL");
            env::set_var("RUST_LOG", "DEBUG");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Debug);

        unsafe {
            env::set_var("RUST_LOG", "WARN");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Warning);

        unsafe {
            env::remove_var("LOG_LEVEL");
            env::set_var("RUST_LOG", "invalid");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Info); // Default fallback

        unsafe {
            env::remove_var("RUST_LOG");
            env::remove_var("LOG_LEVEL");
        }
        assert_eq!(parse_log_level_from_env(), LogLevel::Info);

        unsafe {
            env::remove_var("RUST_LOG");
            env::remove_var("LOG_LEVEL");
        }

        unsafe {
            env::remove_var("RUST_LOG");
        }

        unsafe {
            env::remove_var("RUST_LOG");
            env::set_var("RUST_LOG", "debug");
        }

        unsafe {
            env::remove_var("RUST_LOG");
            env::set_var("RUST_LOG", " myapp = debug , hyper = info ");
        }

        unsafe {
            env::remove_var("RUST_LOG");
        }

        unsafe {
            env::remove_var("RUST_LOG");
            env::remove_var("LOG_LEVEL");
        }

        unsafe {
            env::set_var("RUST_LOG", "debug");
        }
        let logger = logger_from_env();
        assert_eq!(logger.get_level(), LogLevel::Debug);

        unsafe {
            env::remove_var("RUST_LOG");
            env::set_var("LOG_LEVEL", "error");
        }
        let logger = logger_from_env();
        assert_eq!(logger.get_level(), LogLevel::Error);

        unsafe {
            env::remove_var("RUST_LOG");
            env::remove_var("LOG_LEVEL");
        }
    }

    #[test]
    fn test_stdout_logger() -> io::Result<()> {
        let logger = stdout_logger();
        assert_eq!(logger.level(), LogLevel::Info);

        logger.info("Test message")?;

        Ok(())
    }

    #[test]
    fn test_console_loggers() -> io::Result<()> {
        let loggers = console_loggers();
        assert_eq!(loggers.len(), 2);

        log_str_to_multiple(&loggers, LogLevel::Info, "Console message")?;

        for logger in &loggers {
            assert_eq!(logger.level(), LogLevel::Info);
        }

        Ok(())
    }

    #[test]
    fn test_log_to_multiple() -> io::Result<()> {
        let loggers = vec![
            Logger::new().no_time_prefix(),
            Logger::new().no_time_prefix(),
        ];

        log_str_to_multiple(&loggers, LogLevel::Info, "Multi-target message")?;

        for logger in &loggers {
            assert_eq!(logger.level(), LogLevel::Info);
        }

        Ok(())
    }
}