use log::{self, LogRecord, LogLevel, LogLevelFilter, LogMetadata, SetLoggerError};
use regex::Regex;
use std::collections::HashMap;
use std::io::prelude::*;
use std::fs::{File, OpenOptions};
use std::io::BufWriter;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock};
pub type LogFunction = fn(&LogRecord, &mut BufWriter<File>);
pub struct FileLogger {
rwlck: RwLock<BufWriter<File>>,
filters: HashMap<usize, Regex>,
outputfn: Arc<Mutex<LogFunction>>,
}
impl FileLogger {
pub fn new(path: PathBuf, ofn: LogFunction) -> FileLogger {
let mut opts = OpenOptions::new();
opts.create(true);
opts.write(true);
opts.append(true);
let file = match opts.open(&path) {
Ok(f) => f,
Err(e) => panic!("Unable to use file for logging! {}", e),
};
FileLogger {
rwlck: RwLock::new(BufWriter::new(file)),
filters: HashMap::new(),
outputfn: Arc::new(Mutex::new(ofn)),
}
}
pub fn add_filter(&mut self, lvl: LogLevel, r: Regex) -> &mut FileLogger {
self.filters.insert(lvl as usize, r);
self
}
}
impl log::Log for FileLogger {
fn enabled(&self, meta: &LogMetadata) -> bool {
match self.filters.get(&(meta.level() as usize)) {
Some(re) => !re.is_match(meta.target()),
None => true,
}
}
fn log(&self, record: &LogRecord) {
if self.enabled(record.metadata()) {
match self.rwlck.write() {
Ok(ref mut w) => {
match self.outputfn.lock() {
Ok(ref mut f) => {
f(record, w.deref_mut());
}
Err(e) => {
println!("Unable to acquire lock! {}", e);
}
};
}
Err(e) => {
println!("Unable to acquire write lock! {:?}", e);
}
}
}
}
}
pub fn init_file_logger(level: LogLevelFilter, logger: FileLogger) -> Result<(), SetLoggerError> {
log::set_logger(|max_log_level| {
max_log_level.set(level);
Box::new(logger)
})
}