chromalog 0.0.2

A customizable logger with dynamic color coding and file logging.
Documentation
// use log::{Log, Metadata, Record, Level, LevelFilter, SetLoggerError};
// use colored::*; // For coloring log messages
// use chrono::Local; // For getting the current local timestamp

// pub struct ColorConfig {
//     pub error_color: Color,
//     pub warn_color: Color,
//     pub info_color: Color,
//     pub debug_color: Color,
//     pub trace_color: Color,
//     pub target_color: Color, // Color for target, same across all levels
//     pub datetime_color: Option<Color>
// }

// impl Default for ColorConfig {
//     fn default() -> Self {
//         ColorConfig {
//             error_color: Color::Red,
//             warn_color: Color::Yellow,
//             info_color: Color::Green,
//             debug_color: Color::Blue,
//             trace_color: Color::Magenta,
//             target_color: Color::White,
//             datetime_color: Some(Color::BrightWhite), // Use `Some` for default datetime color
//         }
//     }
// }

// pub struct ChromaLog {
//     level: LevelFilter,     // Logging level
//     color_config: ColorConfig, // Color configuration for log levels
// }

// impl ChromaLog {
//     // Function to initialize the logger with a color configuration
//     pub fn init(level: LevelFilter, color_config: ColorConfig) -> Result<(), SetLoggerError> {
//         let logger = ChromaLog { level, color_config };
//         log::set_boxed_logger(Box::new(logger))?;
//         log::set_max_level(level);
//         Ok(())
//     }
// }

// // Implement the `Log` trait for your custom logger
// impl Log for ChromaLog {
//     fn enabled(&self, metadata: &Metadata) -> bool {
//         metadata.level() <= self.level
//     }

//     fn log(&self, record: &Record) {
//         if self.enabled(record.metadata()) {
//             let level = record.level();
//             let target = record.target();
//             let args = record.args();

//             let now = Local::now().format("%Y-%m-%d %H:%M:%S");

//             // Handle datetime coloring, ensuring that both branches return a `ColoredString`
//             let colored_now = if let Some(color) = self.color_config.datetime_color {
//                 now.to_string().color(color)  // Apply datetime color if specified
//             } else {
//                 now.to_string().normal()  // Default color (no coloring)
//             };

//             // Apply dynamic colors based on the level and user configuration
//             let colorized_log = match level {
//                 Level::Error => format!(
//                     "[{}] {:<5} {} - {}",
//                     colored_now,  // Datetime with color if applicable
//                     "ERROR".color(self.color_config.error_color),  // Error level color
//                     target.color(self.color_config.target_color),  // Target color
//                     args  // Arguments (no extra color applied here)
//                 ),
//                 Level::Warn => format!(
//                     "[{}] {:<5} {} - {}",
//                     colored_now,  // Datetime with color if applicable
//                     "WARN".color(self.color_config.warn_color),  // Warning level color
//                     target.color(self.color_config.target_color),  // Target color
//                     args  // Arguments (no extra color applied here)
//                 ),
//                 Level::Info => format!(
//                     "[{}] {:<5} {} - {}",
//                     colored_now,  // Datetime with color if applicable
//                     "INFO".color(self.color_config.info_color),  // Info level color
//                     target.color(self.color_config.target_color),  // Target color
//                     args  // Arguments (no extra color applied here)
//                 ),
//                 Level::Debug => format!(
//                     "[{}] {:<5} {} - {}",
//                     colored_now,  // Datetime with color if applicable
//                     "DEBUG".color(self.color_config.debug_color),  // Debug level color
//                     target.color(self.color_config.target_color),  // Target color
//                     args  // Arguments (no extra color applied here)
//                 ),
//                 Level::Trace => format!(
//                     "[{}] {:<5} {} - {}",
//                     colored_now,  // Datetime with color if applicable
//                     "TRACE".color(self.color_config.trace_color),  // Trace level color
//                     target.color(self.color_config.target_color),  // Target color
//                     args  // Arguments (no extra color applied here)
//                 ),
//             };

//             // Print colored log message
//             match level {
//                 Level::Error | Level::Warn => eprintln!("{}", colorized_log),
//                 _ => println!("{}", colorized_log),
//             }
//         }
//     }

//     fn flush(&self) {}
// }


use log::{Log, Metadata, Record, Level, SetLoggerError};
use colored::*; // For coloring log messages
use chrono::Local; // For getting the current local timestamp
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::sync::{Arc, Mutex};

// Re-export common types from log and colored
pub use log::{error, warn, info, debug, trace, LevelFilter};
pub use colored::Color; // Re-export Color for use in ColorConfig

pub enum ArgColor {
    Fixed(Color),  // Use a fixed color
    LogLevel,      // Match the log level color
    None,          // No color applied
}

pub struct ColorConfig {
    pub error_color: Color,
    pub warn_color: Color,
    pub info_color: Color,
    pub debug_color: Color,
    pub trace_color: Color,
    pub arg_color: ArgColor,
    pub target_color: Color, // Color for target, same across all levels
    pub datetime_color: Option<Color>,
}

impl Default for ColorConfig {
    fn default() -> Self {
        ColorConfig {
            error_color: Color::Red,
            warn_color: Color::Yellow,
            info_color: Color::Green,
            debug_color: Color::Blue,
            trace_color: Color::Magenta,
            arg_color: ArgColor::None,
            target_color: Color::White,
            datetime_color: Some(Color::BrightWhite), // Use `Some` for default datetime color
        }
    }
}

pub struct ChromaLog {
    level: LevelFilter,         // Logging level
    color_config: ColorConfig,  // Color configuration for log levels
    log_file: Option<Arc<Mutex<File>>>, // Optional file for saving logs
}

impl ChromaLog {
    // Function to initialize the logger with a color configuration and optional log file path
    pub fn init(level: LevelFilter, color_config: ColorConfig, log_file_path: Option<&str>) -> Result<(), SetLoggerError> {
        // Open the file if a path is specified
        let log_file = log_file_path.map(|path| {
            Arc::new(Mutex::new(
                OpenOptions::new()
                    .create(true)
                    .write(true)
                    .append(true)
                    .open(path)
                    .expect("Failed to open log file"),
            ))
        });

        let logger = ChromaLog { level, color_config, log_file };
        log::set_boxed_logger(Box::new(logger))?;
        log::set_max_level(level);
        Ok(())
    }
}

// Implement the `Log` trait for your custom logger
impl Log for ChromaLog {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            let level = record.level();
            let target = record.target();
            let args = record.args();

            let now = Local::now().format("%Y-%m-%d %H:%M:%S%.3f");

            // Handle datetime coloring, ensuring both branches return a `ColoredString`
            let colored_now = if let Some(color) = self.color_config.datetime_color {
                now.to_string().color(color)  // Apply datetime color if specified
            } else {
                now.to_string().normal()  // Default color (no coloring)
            };

            // let args_color = match self.color_config.arg_color {
            //     Some(arg_color) => format!("{}", args).color(arg_color),  // Use the explicitly defined arg color
            //     None => format!("{}", args).normal(),  // No color applied, but ensure it's a ColoredString
            // };

            let args_color = match self.color_config.arg_color {
                ArgColor::Fixed(arg_color) => format!("{}", args).color(arg_color),  // Use the explicitly defined arg color
                ArgColor::LogLevel => match level {
                    Level::Error => format!("{}", args).color(self.color_config.error_color),
                    Level::Warn => format!("{}", args).color(self.color_config.warn_color),
                    Level::Info => format!("{}", args).color(self.color_config.info_color),
                    Level::Debug => format!("{}", args).color(self.color_config.debug_color),
                    Level::Trace => format!("{}", args).color(self.color_config.trace_color),
                },
                ArgColor::None => format!("{}", args).normal(),  // No color applied
            };


            // Apply dynamic colors based on the level and user configuration
            let colorized_log = match level {
                Level::Error => format!(
                    "[{}] {:<5} {}: {}",
                    colored_now,  // Datetime with color if applicable
                    "ERROR".color(self.color_config.error_color),  // Error level color
                    target.color(self.color_config.target_color),  // Target color
                    args_color  // Arguments colored based on `arg_color` or log level color
                ),
                Level::Warn => format!(
                    "[{}] {:<5} {}: {}",
                    colored_now,  // Datetime with color if applicable
                    "WARN".color(self.color_config.warn_color),  // Warning level color
                    target.color(self.color_config.target_color),  // Target color
                    args_color  // Arguments colored based on `arg_color` or log level color
                ),
                Level::Info => format!(
                    "[{}] {:<5} {}: {}",
                    colored_now,  // Datetime with color if applicable
                    "INFO".color(self.color_config.info_color),  // Info level color
                    target.color(self.color_config.target_color),  // Target color
                    args_color  // Arguments colored based on `arg_color` or log level color
                ),
                Level::Debug => format!(
                    "[{}] {:<5} {}: {}",
                    colored_now,  // Datetime with color if applicable
                    "DEBUG".color(self.color_config.debug_color),  // Debug level color
                    target.color(self.color_config.target_color),  // Target color
                    args_color  // Arguments colored based on `arg_color` or log level color
                ),
                Level::Trace => format!(
                    "[{}] {:<5} {}: {}",
                    colored_now,  // Datetime with color if applicable
                    "TRACE".color(self.color_config.trace_color),  // Trace level color
                    target.color(self.color_config.target_color),  // Target color
                    args_color  // Arguments colored based on `arg_color` or log level color
                ),
            };


            // // Apply dynamic colors based on the level and user configuration
            // let colorized_log = match level {
            //     Level::Error => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "ERROR".color(self.color_config.error_color),  // Error level color
            //         target.color(self.color_config.target_color),  // Target color
            //         args  // Arguments (no extra color applied here)
            //     ),
            //     Level::Warn => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "WARN".color(self.color_config.warn_color),  // Warning level color
            //         target.color(self.color_config.target_color),  // Target color
            //         args  // Arguments (no extra color applied here)
            //     ),
            //     Level::Info => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "INFO".color(self.color_config.info_color),  // Info level color
            //         target.color(self.color_config.target_color),  // Target color
            //         args  // Arguments (no extra color applied here)
            //     ),
            //     Level::Debug => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "DEBUG".color(self.color_config.debug_color),  // Debug level color
            //         target.color(self.color_config.target_color),  // Target color
            //         args  // Arguments (no extra color applied here)
            //     ),
            //     Level::Trace => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "TRACE".color(self.color_config.trace_color),  // Trace level color
            //         target.color(self.color_config.target_color),  // Target color
            //         args
            //     ),
            // };

            // Determine argument color (based on `arg_color` or fallback to log level color)
            // let args_color = match self.color_config.arg_color {
            //     Some(arg_color) => format!("{}", args).color(arg_color),  // Use the explicitly defined arg color
            //     None => match level {
            //         Level::Error => format!("{}", args).color(self.color_config.error_color),  // Fallback to log level color
            //         Level::Warn => format!("{}", args).color(self.color_config.warn_color),
            //         Level::Info => format!("{}", args).color(self.color_config.info_color),
            //         Level::Debug => format!("{}", args).color(self.color_config.debug_color),
            //         Level::Trace => format!("{}", args).color(self.color_config.trace_color),
            //     },
            // };

            // // Apply dynamic colors based on the level and user configuration
            // let colorized_log = match level {
            //     Level::Error => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "ERROR".color(self.color_config.error_color),  // Error level color
            //         target.color(self.color_config.target_color),  // Target color
            //         format!("{}", args).color(self.color_config.error_color),  // Arguments colored as Error
            //     ),
            //     Level::Warn => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "WARN".color(self.color_config.warn_color),  // Warning level color
            //         target.color(self.color_config.target_color),  // Target color
            //         format!("{}", args).color(self.color_config.warn_color),  // Arguments colored as Warning
            //     ),
            //     Level::Info => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "INFO".color(self.color_config.info_color),  // Info level color
            //         target.color(self.color_config.target_color),  // Target color
            //         format!("{}", args).color(self.color_config.info_color),  // Arguments colored as Info
            //     ),
            //     Level::Debug => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "DEBUG".color(self.color_config.debug_color),  // Debug level color
            //         target.color(self.color_config.target_color),  // Target color
            //         format!("{}", args).color(self.color_config.debug_color),  // Arguments colored as Debug
            //     ),
            //     Level::Trace => format!(
            //         "[{}] {:<5} {}: {}",
            //         colored_now,  // Datetime with color if applicable
            //         "TRACE".color(self.color_config.trace_color),  // Trace level color
            //         target.color(self.color_config.target_color),  // Target color
            //         format!("{}", args).color(self.color_config.trace_color),  // Arguments colored as Trace
            //     ),
            // };


            // Print the log message to the console
            match level {
                Level::Error | Level::Warn => eprintln!("{}", colorized_log),
                _ => println!("{}", colorized_log),
            }

            // If log file is specified, write the log message to the file
            if let Some(file) = &self.log_file {
                let mut file = file.lock().unwrap();
                writeln!(file, "[{}] {:<5}: {}", now, level, args).expect("Failed to write to log file");
            }
        }
    }

    fn flush(&self) {}
}