use file::FileLogger;
use log::{
self,
LogLevelFilter,
LogMetadata,
LogRecord,
SetLoggerError
};
use stdout::StdoutLogger;
use std::path::PathBuf;
bitflags! {
#[derive(Debug)]
flags Loggers: u32 {
const STDOUT = 0b00000001,
const FILE = 0b00000010,
const LOCAL = STDOUT.bits | FILE.bits,
}
}
pub struct MultiLogger {
loggers: Loggers,
file: Option<FileLogger>,
stdout: Option<StdoutLogger>,
}
impl MultiLogger {
pub fn new() -> MultiLogger {
MultiLogger {
loggers: Loggers::empty(),
file: None,
stdout: None,
}
}
pub fn toggle_stdout(&mut self) -> &mut MultiLogger {
self.loggers.toggle(STDOUT);
self.stdout = Some(StdoutLogger);
self
}
pub fn toggle_file(&mut self, path: PathBuf) -> &mut MultiLogger {
self.loggers.toggle(FILE);
self.file = Some(FileLogger::new(path));
self
}
}
impl log::Log for MultiLogger {
fn enabled(&self, _: &LogMetadata) -> bool {
true
}
fn log(&self, record: &LogRecord) {
if self.enabled(record.metadata()) {
if self.loggers.contains(FILE) {
match self.file {
Some(ref f) => { f.log(record); },
None => {},
}
}
if self.loggers.contains(STDOUT) {
match self.stdout {
Some(ref s) => { s.log(record); },
None => {},
}
}
}
}
}
pub fn init_chained_logger(lvl: LogLevelFilter,
cl: MultiLogger) -> Result<(), SetLoggerError> {
log::set_logger(|max_log_level| {
max_log_level.set(lvl);
Box::new(cl)
})
}
#[cfg(test)]
mod test {
use super::*;
use std::env;
use log::LogLevelFilter;
#[test]
fn test_loggers() {
let mut loggers = STDOUT | FILE;
assert_eq!(loggers.bits, 3);
loggers.toggle(STDOUT);
assert_eq!(loggers.bits, 2);
loggers.toggle(FILE);
loggers.toggle(STDOUT);
assert_eq!(loggers.bits, 1);
loggers.toggle(STDOUT);
assert_eq!(loggers.bits, 0);
loggers = LOCAL;
assert_eq!(loggers.bits, 3);
}
#[test]
fn test_chained() {
let mut tmp_log = env::temp_dir();
tmp_log.push("log.log");
let mut cl = MultiLogger::new();
cl.toggle_stdout();
cl.toggle_file(tmp_log);
assert!(init_chained_logger(LogLevelFilter::Debug, cl).is_ok());
error!("TEST");
}
}