log_rs/logger/
mod.rs

1mod enums;
2mod write_buffer;
3
4pub use enums::{Level, OutputKind};
5pub use write_buffer::WriteBuffer;
6
7use crate::timestamp::get_timestamp;
8use std::collections::HashMap;
9use std::fs::OpenOptions;
10use std::io;
11
12/// A logger that can log messages on different level to different output.  
13/// The output of each level can be configured to eventually be same for all, or unique.
14pub struct Logger<'o> {
15    file_options: OpenOptions,
16    level_outputs: HashMap<Level, OutputKind<'o>>,
17    write_buffers: HashMap<OutputKind<'o>, WriteBuffer>,
18    format: String,
19}
20
21impl<'o> Logger<'o> {
22    /// Create a new logger.
23    /// By default, `INFO` and `DEBUG` are writting to `stdout`,
24    /// and `WARNING` and `ERROR` write to `stderr`.
25    ///
26    /// # Return Value
27    ///
28    /// The new logger if the function succeeded, `None` otherwise.
29    pub fn new() -> Option<Self> {
30        let mut options = OpenOptions::new();
31        options.write(true).create(true).truncate(true);
32
33        let mut map_level = HashMap::new();
34        map_level.insert(Level::INFO, OutputKind::STDOUT);
35        map_level.insert(Level::DEBUG, OutputKind::STDOUT);
36        map_level.insert(Level::WARNING, OutputKind::STDERR);
37        map_level.insert(Level::ERROR, OutputKind::STDERR);
38
39        let mut stdout = if let Ok(buff) = WriteBuffer::new(&OutputKind::STDOUT, &options) {
40            buff
41        } else {
42            return None;
43        };
44        let mut stderr = if let Ok(buff) = WriteBuffer::new(&OutputKind::STDERR, &options) {
45            buff
46        } else {
47            return None;
48        };
49        stdout.increase_count();
50        stderr.increase_count();
51
52        let mut map_buffer = HashMap::new();
53        map_buffer.insert(OutputKind::STDOUT, stdout);
54        map_buffer.insert(OutputKind::STDERR, stderr);
55
56        let log = Logger {
57            file_options: options,
58            level_outputs: map_level,
59            write_buffers: map_buffer,
60            format: String::from("[%l](%t) - %m"),
61        };
62
63        return Some(log);
64    }
65
66    /// Configure the format of the log.
67    /// 
68    /// # Arguments
69    /// 
70    /// * `new_format` - The format to be used for logging message.  
71    /// Any occurrence of `%l` will be replaced by the level, `%t` by the timestamp, `%m` by the message.  
72    pub fn config_format<S: ToString + ?Sized>(&mut self, new_format: &S) {
73        self.format = new_format.to_string();
74
75    }
76
77    /// Configure the output of the given level.  
78    ///
79    /// # Arguments
80    ///
81    /// * `kind` - The new output kind to be used by the level.
82    /// * `level` - The level to configure.
83    ///
84    /// # Return Value
85    ///
86    /// The io error if something went wrong and the configuration was not possible.
87    fn config_output(&mut self, kind: OutputKind<'o>, level: Level) -> io::Result<()> {
88        match self.write_buffers.get_mut(&kind) {
89            None => {
90                let buffer = WriteBuffer::new(&kind, &self.file_options)?;
91                self.write_buffers.insert(kind.clone(), buffer);
92            }
93            Some(buffer) => buffer.increase_count(),
94        };
95        let output = self.level_outputs.get_mut(&level).unwrap();
96        if match self.write_buffers.get_mut(output) {
97            None => false,
98            Some(buffer) => buffer.decrease_count(),
99        } {
100            self.write_buffers.remove(output);
101        }
102        *output = kind;
103        Ok(())
104    }
105
106    /// Configure the output for `INFO`.  
107    ///
108    /// # Arguments
109    ///
110    /// * `kind` - The new output kind to be used by the info.
111    pub fn config_info(&mut self, kind: OutputKind<'o>) {
112        if let Err(err) = self.config_output(kind, Level::INFO) {
113            self.error(&format!("Failed to configure INFO: {}", err));
114        }
115    }
116
117    /// Configure the output for `DEBUG`.  
118    ///
119    /// # Arguments
120    ///
121    /// * `kind` - The new output kind to be used by the debug.
122    pub fn config_debug(&mut self, kind: OutputKind<'o>) {
123        if let Err(err) = self.config_output(kind, Level::DEBUG) {
124            self.error(&format!("Failed to configure DEBUG: {}", err));
125        }
126    }
127
128    /// Configure the output for `WARNING`.  
129    ///
130    /// # Arguments
131    ///
132    /// * `kind` - The new output kind to be used by the warning.
133    pub fn config_warning(&mut self, kind: OutputKind<'o>) {
134        if let Err(err) = self.config_output(kind, Level::WARNING) {
135            self.error(&format!("Failed to configure WARNING: {}", err));
136        }
137    }
138
139    /// Configure the output for `ERROR`.  
140    ///
141    /// # Arguments
142    ///
143    /// * `kind` - The new output kind to be used by the error.
144    pub fn config_error(&mut self, kind: OutputKind<'o>) {
145        if let Err(err) = self.config_output(kind, Level::ERROR) {
146            self.error(&format!("Failed to configure ERROR: {}", err));
147        }
148    }
149
150    /// Send a message on the given level.  
151    ///
152    /// # Arguments
153    ///
154    /// * `msg` - The message to write.
155    /// * `level` - The level to write on.
156    ///
157    /// # Return Value
158    ///
159    /// The io error if something went wrong and the logging was not possible.
160    fn write_log(&mut self, msg: &str, level: Level) {
161        if let Some(buffer) = self
162            .write_buffers
163            .get_mut(self.level_outputs.get(&level).unwrap())
164        {
165            let mut log_msg = self
166                .format
167                .replace("%l", &level.to_string())
168                .replace("%t", &get_timestamp().to_string())
169                .replace("%m", msg);
170            log_msg.push('\n');
171            if let Err(err) = buffer.log(log_msg) {
172                match level {
173                    Level::ERROR => {}
174                    _ => self.error(&format!("Failed to log to `{}`: {}", level, err)),
175                }
176            };
177        };
178    }
179
180    /// Send a message on `INFO`.
181    ///
182    /// # Arguments
183    ///
184    /// * `msg` - The message to write on `INFO`.
185    pub fn info(&mut self, msg: &str) {
186        self.write_log(msg, Level::INFO);
187    }
188
189    /// Send a message on `DEBUG`.
190    ///
191    /// # Arguments
192    ///
193    /// * `msg` - The message to write on `DEBUG`.
194    pub fn debug(&mut self, msg: &str) {
195        self.write_log(msg, Level::DEBUG);
196    }
197
198    /// Send a message on `WARNING`.
199    ///
200    /// # Arguments
201    ///
202    /// * `msg` - The message to write on `WARNING`.
203    pub fn warning(&mut self, msg: &str) {
204        self.write_log(msg, Level::WARNING);
205    }
206
207    /// Send a message on `ERROR`.
208    ///
209    /// # Arguments
210    ///
211    /// * `msg` - The message to write on `ERROR`.
212    pub fn error(&mut self, msg: &str) {
213        self.write_log(msg, Level::ERROR);
214    }
215}