如何实现附加到文件的记录器?

How to implement a logger that appends to a file?

我正在尝试实现 log::Log,以便对 log() 的调用将消息附加到文件中。这是我目前的记录器:

pub struct MyLogger {
    loglevel: LogLevelFilter,
    logfile: Option<File>,
}

并执行log::Log:

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

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let msg = format!("{}\t| {}\t| {}", record.level(), record.target(), record.args());
            self.logfile.write_all(msg.as_bytes()).unwrap();
        }
    }
}

可以理解,这失败了,因为 log() 没有采用可变引用。我不能采用可变引用,因为那样我就无法正确实现类型,那么实现这一目标的惯用方法是什么?

每当你需要呈现一个不可变的界面但在幕后进行变异时,你可以使用内部可变性。这样做的常用方法是使用 std::cell 中的内容。文档指出了这个特定的用例:

because you must employ mutation to implement a trait method that was originally defined to take &self.

具体来说,我会尝试在这种情况下使用 RefCell

不幸的是,Log requires the implementer to also be Sync + Send, but cells are not. That means we need to upgrade to something that can handle multiple threads. That something is Mutex

extern crate log;

use std::fs::File;
use std::io::Write;
use std::sync::Mutex;
use log::{LogLevelFilter,LogMetadata,LogRecord,Log};

pub struct FileLogger {
    loglevel: LogLevelFilter,
    logfile: Option<Mutex<File>>,
}

impl Log for FileLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.loglevel
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let msg = format!("{}\t| {}\t| {}", record.level(), record.target(), record.args());
            self.logfile.as_ref().map(|f| {
                f.lock().unwrap().write_all(msg.as_bytes()).unwrap()
            });
        }
    }
}

#[test]
fn it_works() {
}