logfast/
lib.rs

1//! LogFast - Insanely fast logging with a simple interface
2//!
3//! # Example
4//!
5//! ```ignore
6//! use logfast::LogFast;
7//!
8//! fn main() {
9//!   let buffer_size = 100;
10//!   let mut lf = LogFast::new("my.log", buffer_size).unwrap();
11//!
12//!   lf.log("Here's a test log line");
13//!   lf.log("And here's another");
14//! }
15//! ```
16
17use 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
26/// Holds the LogFast housekeeping
27pub struct LogFast {
28    /// filename that the LogFast thread is writing to
29    pub filename: String,
30    src: Option<SyncSender<String>>,
31    barrier: Arc<Barrier>,
32}
33
34impl LogFast {
35    /// Creates a new `LogFast` thread logger
36    ///
37    /// # Parameters
38    ///
39    /// `filename` is the path to the file we want to write logs to
40    ///
41    /// `buffer_size` is the size of the buffer to hold yet-to-be-flushed messages
42    ///
43    /// # Example
44    ///
45    /// ```ignore
46    /// let buffer_size = 100;
47    /// let lf = match LogFast::new("my.log", buffer_size).unwrap();
48    /// ```
49    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    /// Send a new log message to the `LogFast` thread, to be flushed ASAP
85    ///
86    /// # Parameters
87    ///
88    /// `msg` is the log message you want to eventually be written
89    ///
90    /// # Example
91    ///
92    /// ```ignore
93    /// lf.log("Here's a test log line");
94    /// ```
95    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}