User customize log has empty output in log file

use std::io::Write;
use log::{LevelFilter, Log, Metadata, Record};
use simplelog::{Config, WriteLogger};
use termcolor::WriteColor;
use chrono::Local;
pub struct Logger;

impl Log for Logger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= LevelFilter::Debug
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
            let mut stdout = termcolor::StandardStream::stdout(termcolor::ColorChoice::Always);
            let color_spec = match record.level() {
                log::Level::Error => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Red));
                    spec
                }
                log::Level::Warn => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Yellow));
                    spec
                }
                log::Level::Info => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Green));
                    spec
                }
                log::Level::Debug => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Blue));
                    spec
                }
                log::Level::Trace => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Magenta));
                    spec
                }
            };

            stdout
                .set_color(&color_spec)
                .expect("Failed to set color on stdout");
            writeln!(&mut stdout, "{} - {} - {}", timestamp, record.level(), record.args())
                .expect("Failed to write to stdout");
            stdout
                .reset()
                .expect("Failed to reset color on stdout");
        }
    }

    fn flush(&self) {}
}

pub fn init_logger() {
    let log_level = LevelFilter::Debug;
    let log_file = std::fs::OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open("src/logs/client.log")
        .expect("Failed to open log file");

    let file_logger = WriteLogger::new(log_level, Config::default(), log_file);
    log::set_logger(&Logger).expect("Failed to set logger");
    log::set_max_level(log_level);
    file_logger.flush();
}

after writing customize log logic ,
terminal output is ok , but client.log file is empty .

also tested this one

use std::io::Write;
use log::{LevelFilter, Log, Metadata, Record};
use simplelog::{Config, WriteLogger};
use termcolor::WriteColor;
use chrono::Local;


pub struct Logger;
static MY_LOGGER: Logger = Logger;
impl Log for Logger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= LevelFilter::Debug
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
            let mut stdout = termcolor::StandardStream::stdout(termcolor::ColorChoice::Always);
            let color_spec = match record.level() {
                log::Level::Error => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Red));
                    spec
                }
                log::Level::Warn => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Yellow));
                    spec
                }
                log::Level::Info => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Green));
                    spec
                }
                log::Level::Debug => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Blue));
                    spec
                }
                log::Level::Trace => {
                    let mut spec = termcolor::ColorSpec::new();
                    spec.set_fg(Some(termcolor::Color::Magenta));
                    spec
                }
            };

            stdout
                .set_color(&color_spec)
                .expect("Failed to set color on stdout");
            writeln!(&mut stdout, "{} - {} - {}", timestamp, record.level(), record.args())
                .expect("Failed to write to stdout");
            stdout
                .reset()
                .expect("Failed to reset color on stdout");
        }
    }

    fn flush(&self) {}
}

pub fn init_logger() {
    let log_level = LevelFilter::Debug;
    let log_file = std::fs::OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open("src/logs/client.log")
        .expect("Failed to open log file");
    let l = Logger;
    let file_logger = WriteLogger::new(log_level, Config::default(), log_file);
    log::set_logger(&MY_LOGGER).expect("Failed to set logger");
    log::set_max_level(log_level);
    file_logger.flush();
}

client.log still empty

you created file_logger, but you called set_logger() with &Logger, you prabably want to set_logger(&file_logger), which need the file_logger be 'static. so you can either use a static OnceCell, or you just box it up then leak it.

2 Likes

unfortunately file_logger is type Box<WriteLogger> , set_logger needs type &'static dyn Log

You can use CombinedLogger in simplelog - Rust :

+ impl SharedLogger for Logger { ... }

-log::set_logger(&MY_LOGGER).expect("Failed to set logger");
+    log::set_logger(Box::leak(CombinedLogger::new(vec![
+        file_logger,
+        Box::new(Logger),
+    ])))

demo


Update: to be clear, Box<WriteLogger> is also a Box<dyn Log> since impl Log for WriteLogger. So as CombinedLogger here, but CombinedLogger accepts multiple logger sources like a Stdout and File in your case. And Box<dyn Log> is in some sense a &'static dyn Log if you leak it.

1 Like

thanks and great code . you just made my day