emabee / flexi_logger

A flexible logger for rust programs that can write to stderr, stdout, and/or to log files
Apache License 2.0
315 stars 55 forks source link

Writing to `std::fs::File` (or any type implementing `std::io::Write` trait) #124

Open bobmoretti opened 2 years ago

bobmoretti commented 2 years ago

I have a use case where I'd like to write to an existing std::fs::File object.

In particular, I have used the Win32 function AllocConsole() to create a console window for my code, which runs inside of a DLL called from a GUI application. I would like to log to this console. E.g., I have the following function

fn create_console() -> windows::core::Result<File> {
    unsafe {
        Console::AllocConsole();
        let h_stdout = Console::GetStdHandle(Console::STD_OUTPUT_HANDLE)?;
        Ok(File::from_raw_handle(h_stdout.0 as *mut libc::c_void))
    }
}

I want to log to the File object returned by this function.

I see that there is a duplicate_to_stdout() method in flexi_logger, but nothing to duplicate to an arbitrary file. It looks like there's a writers module but that seems to require implementing an entire trait. Is there a straightforward way to tell flexi_logger to log to a given Write sink?

emabee commented 2 years ago

Wrapping your file object like this would make it usable for Logger::log_to_writer:

use flexi_logger::writers::LogWriter;
use std::{
    io::{Error, ErrorKind},
    sync::{Arc, Mutex},
};

struct MyWriter<F> {
    file: Arc<Mutex<F>>,
}

impl<F: std::io::Write + Send + Sync> LogWriter for MyWriter<F> {
    fn write(
        &self,
        now: &mut flexi_logger::DeferredNow,
        record: &flexi_logger::Record,
    ) -> std::io::Result<()> {
        let mut file = self
            .file
            .lock()
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
        flexi_logger::default_format(&mut *file, now, record)
    }

    fn flush(&self) -> std::io::Result<()> {
        let mut file = self
            .file
            .lock()
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
        file.flush()
    }
}
bobmoretti commented 2 years ago

Thanks! Do you think providing this directly in flexi_logger would be useful? As in, provide a method a bit like log_to_file() but much more general?

If you think so I can take a stab at implementing it.

emabee commented 2 years ago

If I may formulate a wish-list: it should be a reasonably documented little class of its own, like above, with some decent constructor(s), that also allows choosing the format function, in module writers. We then can link to it from Logger::log_to_writer.