1use chrono::Local;
18use die::die;
19use std::fs::OpenOptions;
20use std::io::prelude::*;
21use std::sync::mpsc::{sync_channel, SyncSender};
22use std::sync::Arc;
23use std::sync::Barrier;
24use std::thread;
25
26pub struct LogFast {
28 pub filename: String,
30 src: Option<SyncSender<String>>,
31 barrier: Arc<Barrier>,
32}
33
34impl LogFast {
35 pub fn new(filename: &str, buffer_size: usize) -> Result<LogFast, String> {
50 if buffer_size == 0 {
51 return Err("Buffer size needs to be bigger than 0".to_string());
52 }
53
54 let mut log_file = match OpenOptions::new().create(true).append(true).open(filename) {
55 Ok(log_file) => log_file,
56 Err(err) => return Err(format!("Error opening log file '{}': {}", filename, err)),
57 };
58
59 let (src, dest) = sync_channel::<String>(buffer_size);
60 let barrier = Arc::new(Barrier::new(2));
61
62 {
63 let barrier_moved = barrier.clone();
64 let filename_moved = filename.to_string();
65
66 thread::spawn(move || {
67 while let Ok(msg) = dest.recv() {
68 writeln!(log_file, "{}", msg).unwrap_or_else(|err| {
69 die!("Error writing to log file '{}': {}", filename_moved, err)
70 })
71 }
72
73 barrier_moved.wait();
74 })
75 };
76
77 Ok(LogFast {
78 filename: filename.to_string(),
79 src: Some(src),
80 barrier,
81 })
82 }
83
84 pub fn log(&mut self, msg: &str) {
96 self.src
97 .as_ref()
98 .unwrap_or_else(|| {
99 die!(
100 "Error talking to logging thread for log file '{}'",
101 self.filename
102 )
103 })
104 .send(format!("{}: {}", Local::now().naive_local(), &msg))
105 .unwrap_or_else(|err| {
106 die!(
107 "Error talking to logging thread for log file '{}': {}",
108 self.filename,
109 err
110 )
111 })
112 }
113}
114
115impl Drop for LogFast {
116 fn drop(&mut self) {
117 self.src = None;
118 self.barrier.wait();
119 }
120}