use chrono::Local;
use die::die;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::sync::mpsc::{sync_channel, SyncSender};
use std::sync::Arc;
use std::sync::Barrier;
use std::thread;
pub struct LogFast {
pub filename: String,
src: Option<SyncSender<String>>,
barrier: Arc<Barrier>,
}
impl LogFast {
pub fn new(filename: &str, buffer_size: usize) -> Result<LogFast, String> {
if buffer_size == 0 {
return Err("Buffer size needs to be bigger than 0".to_string());
}
let mut log_file = match OpenOptions::new().create(true).append(true).open(filename) {
Ok(log_file) => log_file,
Err(err) => return Err(format!("Error opening log file '{}': {}", filename, err)),
};
let (src, dest) = sync_channel::<String>(buffer_size);
let barrier = Arc::new(Barrier::new(2));
{
let barrier_moved = barrier.clone();
let filename_moved = filename.to_string();
thread::spawn(move || {
while let Ok(msg) = dest.recv() {
writeln!(log_file, "{}", msg).unwrap_or_else(|err| {
die!("Error writing to log file '{}': {}", filename_moved, err)
})
}
barrier_moved.wait();
})
};
Ok(LogFast {
filename: filename.to_string(),
src: Some(src),
barrier,
})
}
pub fn log(&mut self, msg: &str) {
self.src
.as_ref()
.unwrap_or_else(|| {
die!(
"Error talking to logging thread for log file '{}'",
self.filename
)
})
.send(format!("{}: {}", Local::now().naive_local(), &msg))
.unwrap_or_else(|err| {
die!(
"Error talking to logging thread for log file '{}': {}",
self.filename,
err
)
})
}
}
impl Drop for LogFast {
fn drop(&mut self) {
self.src = None;
self.barrier.wait();
}
}