rusty_logger/
logger.rs

1use crate::modules::{self, Module};
2use crate::options::Options;
3use crate::timer::Timer;
4use crate::types::{LogType, TimeUnit};
5use std::{fmt::Display, io::stderr, time::Duration};
6
7/// A modular and powerful logger
8///
9/// # Example
10///
11/// ```
12/// use rusty_logger::Logger;
13/// use rusty_logger::types;
14///
15/// let mut logger = Logger::new("EXAMPLE", std::io::stdout());
16///
17/// logger.options.time_unit = types::TimeUnit::Nanoseconds;
18///
19/// logger.info("We will be timing a for loop");
20///
21/// logger.timer_start("for loop");
22///
23/// for i in 0..1000 {
24///     // Do something...
25/// }
26///
27/// logger.timer_log_and_stop("for loop");
28///
29/// logger.info("The for loop ended successfully");
30/// ```
31pub struct Logger<T: std::io::Write> {
32    pub options: Options,
33    timer: Timer,
34    modules: Vec<Box<dyn Module>>,
35    output: T,
36}
37
38pub struct LogMessage<'a> {
39    pub content: &'a str,
40    pub msg_type: LogType,
41    pub options: &'a Options,
42}
43
44impl<T: std::io::Write> Logger<T> {
45    /// Creates a new Logger with default values and the given name
46    ///
47    /// # Example
48    ///
49    /// ```
50    /// use rusty_logger::Logger;
51    ///
52    /// let mut logger = Logger::new("name", std::io::stdout());
53    ///
54    /// logger.info("This is a new logger named 'name' !");
55    /// ```
56    pub fn new(s: &str, output: T) -> Self {
57        let modules: Vec<Box<dyn Module>> = vec![
58            Box::new(modules::Name {}),
59            Box::new(modules::Time {}),
60            Box::new(modules::Type {}),
61        ];
62        Logger {
63            options: Options::new(s),
64            timer: Timer::new(),
65            modules,
66            output,
67        }
68    }
69
70    /// Creates a new Logger with custom options and modules
71    ///
72    /// # Example
73    ///
74    /// ```
75    /// use rusty_logger::{
76    ///     Logger,
77    ///     Module,
78    ///     Options,
79    ///     modules,
80    /// };
81    ///
82    /// let options = Options::new("homemade logger");
83    ///
84    /// let modules: Vec<Box<dyn Module>> = vec!(
85    ///     Box::new(modules::Name{}),
86    ///     Box::new(modules::Time{}),
87    ///     Box::new(modules::Type{})
88    /// );
89    ///
90    /// let mut logger = Logger::homemade(std::io::stdout(), options, modules);
91    ///
92    /// logger.info("This is a new logger named 'name' which has 'options' as its options !");
93    /// ```
94    pub fn homemade(output: T, options: Options, modules: Vec<Box<dyn Module>>) -> Logger<T> {
95        Logger {
96            options,
97            timer: Timer::new(),
98            modules,
99            output,
100        }
101    }
102
103    /// Formats the message with the current logger options
104    /// and then prints it. It is a private function and is
105    /// only used by the other internal functions to print
106    /// the message.
107    ///
108    /// It goes through the options of the logger it is
109    /// called on and formats the message accordingly.
110    fn log(&mut self, msg: String, msg_type: LogType) {
111        let mut output = String::new();
112
113        let lm = LogMessage {
114            content: &msg[..],
115            msg_type,
116            options: &self.options,
117        };
118
119        self.modules.iter_mut().for_each(|module| {
120            output.push_str(&module.print(&lm));
121        });
122
123        match writeln!(self.output, "{}: {}", output, msg) {
124            std::result::Result::Err(err) => Logger::static_log(err, LogType::Error, stderr()),
125            std::result::Result::Ok(_) => {}
126        }
127    }
128
129    /// Static implementation for the log function
130    ///
131    /// Mainly used to print internal error messages
132    /// but can also be used by an end user
133    ///
134    /// # Example
135    ///
136    /// ```
137    /// use rusty_logger::{Logger, types::LogType};
138    ///
139    /// Logger::static_log("Printing on the fly", LogType::Info, std::io::stdout());
140    /// ```
141    pub fn static_log<D: Display>(msg: D, msg_type: LogType, output: T) {
142        let mut logger = Logger::new("STATIC", output);
143        logger.log(msg.to_string(), msg_type);
144    }
145
146    /// Logs the given message to the output
147    /// as an information
148    ///
149    /// # Example
150    ///
151    /// ```
152    /// use rusty_logger::Logger;
153    ///
154    /// let mut logger = Logger::new("name", std::io::stdout());
155    ///
156    /// logger.info("Here is some information I want to log");
157    /// ```
158    pub fn info<D: Display>(&mut self, msg: D) {
159        self.log(msg.to_string(), LogType::Info);
160    }
161
162    /// Logs the given message to the output
163    /// as a warning
164    ///
165    /// # Example
166    ///
167    /// ```
168    /// use rusty_logger::Logger;
169    ///
170    /// let mut logger = Logger::new("name", std::io::stdout());
171    ///
172    /// logger.warn("Here is a warning");
173    /// ```
174    pub fn warn<D: Display>(&mut self, msg: D) {
175        self.log(msg.to_string(), LogType::Warning);
176    }
177
178    /// Logs the given message to the output
179    /// as an error
180    ///
181    /// # Example
182    ///
183    /// ```
184    /// use rusty_logger::Logger;
185    ///
186    /// let mut logger = Logger::new("name", std::io::stdout());
187    ///
188    /// logger.info("Some error happened");
189    /// ```
190    pub fn error<D: Display>(&mut self, msg: D) {
191        self.log(msg.to_string(), LogType::Error);
192    }
193
194    /// Starts a new timer with the given name
195    /// and the actual time
196    pub fn timer_start(&mut self, msg: &str) {
197        self.timer.start(msg);
198    }
199
200    /// Gets the duration from when the timer
201    /// with the given name was started
202    pub fn timer_get(&self, msg: &str) -> Option<Duration> {
203        self.timer.get(msg)
204    }
205
206    /// Stops the timer with the given name
207    /// and returns it as a duration
208    pub fn timer_stop(&mut self, msg: &str) -> Option<Duration> {
209        self.timer.stop(msg)
210    }
211
212    /// Resets the timer with the given name
213    /// and returns the duration of the timer
214    /// before being reset as a duration
215    pub fn timer_reset(&mut self, msg: &str) -> Option<Duration> {
216        self.timer.reset(msg)
217    }
218
219    /// Pauses the timer with the given name
220    /// and returns the duration of the timer
221    /// before pausing it as a duration
222    pub fn timer_pause(&mut self, msg: &str) -> Option<Duration> {
223        self.timer.pause(msg)
224    }
225
226    /// Resumes the timer with the given name
227    pub fn timer_resume(&mut self, msg: &str) -> Option<Duration> {
228        self.timer.resume(msg)
229    }
230
231    /// Logs the time elapsed between the
232    /// start/reset of the timer and now
233    /// **without** reseting it
234    ///
235    /// # Example
236    ///
237    /// ```
238    /// use rusty_logger::Logger;
239    ///
240    /// let mut logger = Logger::new("name", std::io::stdout());
241    ///
242    /// logger.timer_start("new_timer");
243    ///
244    /// std::thread::sleep(std::time::Duration::from_millis(1));
245    ///
246    /// logger.timer_log("new_timer");
247    /// ```
248    pub fn timer_log(&mut self, msg: &str) -> Option<Duration> {
249        if let Some(time) = self.timer.get(msg) {
250            match self.options.time_unit {
251                TimeUnit::Nanoseconds => {
252                    self.log(format!("{} - {}ns", msg, time.as_nanos()), LogType::Time);
253                }
254                TimeUnit::Microseconds => {
255                    self.log(format!("{} - {}μs", msg, time.as_micros()), LogType::Time);
256                }
257                TimeUnit::Milliseconds => {
258                    self.log(format!("{} - {}ms", msg, time.as_millis()), LogType::Time);
259                }
260            }
261            Some(time)
262        } else {
263            Logger::static_log(msg, LogType::Error, stderr());
264            None
265        }
266    }
267
268    /// Logs the time elapsed between the
269    /// start/reset of the timer and now
270    /// and then stops it
271    ///
272    /// # Example
273    ///
274    /// ```
275    /// use rusty_logger::Logger;
276    ///
277    /// let mut logger = Logger::new("name", std::io::stdout());
278    ///
279    /// logger.timer_start("new_timer");
280    ///
281    /// std::thread::sleep(std::time::Duration::from_millis(1));
282    ///
283    /// logger.timer_log_and_stop("new_timer");
284    /// ```
285    pub fn timer_log_and_stop(&mut self, msg: &str) -> Option<Duration> {
286        self.timer_log(msg);
287        self.timer_stop(msg)
288    }
289
290    /// Logs the time elapsed between the
291    /// start/reset of the timer and now
292    /// and then resets it
293    ///
294    /// # Example
295    ///
296    /// ```
297    /// use rusty_logger::Logger;
298    ///
299    /// let mut logger = Logger::new("name", std::io::stdout());
300    ///
301    /// logger.timer_start("new_timer");
302    ///
303    /// std::thread::sleep(std::time::Duration::from_millis(1));
304    ///
305    /// logger.timer_log_and_reset("new_timer");
306    ///
307    /// std::thread::sleep(std::time::Duration::from_millis(1));
308    ///
309    /// logger.timer_log_and_stop("new_timer");
310    /// ```
311    pub fn timer_log_and_reset(&mut self, msg: &str) -> Option<Duration> {
312        self.timer_log(msg);
313        self.timer_reset(msg)
314    }
315}