prettylogger/
output.rs

1//! Provides log stream implementations for directing log output to various
2//! destinations, such as files, standard error, or a log buffer.
3
4/// Provides log stream implementations for directing log output to various
5/// destinations, such as files, standard error, or a log buffer.
6use std::{fs::OpenOptions, sync::Mutex};
7
8use serde::{Serialize, Deserialize};
9
10use crate::{
11    Error,
12    config::{LogStruct, OnDropPolicy},
13    format::LogFormatter,
14    fileio::{append_to_file, overwrite_file},
15};
16
17/// Common trait for toggleable objects.
18pub trait Toggleable {
19    /// Enables the object.
20    fn enable(&mut self);
21    /// Disables the object.
22    fn disable(&mut self);
23    /// Returns whether the object is enabled.
24    fn is_enabled(&self) -> &bool;
25}
26
27/// Wraps `StderrStream`, `BufferStream` and `FileStream` in one object used
28/// internally by `Logger`.
29///
30/// # Examples
31///
32/// Printing log to `stderr`:
33/// ```
34/// # use prettylogger::{
35/// #     output::LogOutput,
36/// #     format::LogFormatter,
37/// #     config::LogStruct,
38/// # };
39/// // Required by `LogOutput` for parsing logs
40/// let mut formatter = LogFormatter::default();
41///
42/// // By default, only output to `stderr` is enabled
43/// let mut log_output = LogOutput::default();
44///
45/// // Print "Hello, World!" in a neat log format
46/// log_output.out(&LogStruct::debug("Hello, World!"), &mut formatter);
47/// ```
48#[derive(Debug, Serialize, Deserialize)]
49pub struct LogOutput {
50    /// The `stderr` output stream.
51    pub stderr_output: StderrStream,
52    /// File output stream for writing logs to a file.
53    pub file_output: Mutex<FileStream>,
54    /// Buffer stream for storing log messages.
55    pub buffer_output: Mutex<BufferStream>,
56
57    enabled: bool,
58}
59
60/// Used for printing logs to `stderr`.
61///
62/// # Examples
63///
64/// Printing a log to `stderr`:
65/// ```
66/// # use prettylogger::{
67/// #     output::StderrStream,
68/// #     format::LogFormatter,
69/// #     config::LogStruct,
70/// # };
71/// // Required by `StderrStream` for parsing logs
72/// let mut formatter = LogFormatter::default();
73///
74/// // Enabled by default
75/// let mut stderr_output = StderrStream::default();
76///
77/// // Print "Hello, World!" in a neat log format
78/// stderr_output.out(&LogStruct::debug("Hello, World!"), &mut formatter);
79/// ```
80#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize,
81    Deserialize)]
82pub struct StderrStream {
83    enabled: bool,
84}
85
86/// Used to output logs to a file.
87///
88/// # Examples
89///
90/// Writing a log to a file:
91/// ```
92/// # use prettylogger::{
93/// #     output::{FileStream, Toggleable},
94/// #     format::LogFormatter,
95/// #     config::LogStruct,
96/// # };
97/// # let mut path = std::env::temp_dir();
98/// # path.push("libprettylogger-tests/fo-struct-doc.log");
99/// # let path = &path.to_str().unwrap().to_string();
100/// // Required by `FileStream` for parsing logs
101/// let mut formatter = LogFormatter::default();
102///
103/// let mut file_output = FileStream::default();
104///
105/// // Set the log file path **first**
106/// file_output.set_log_file_path(&path)
107///     .expect("Failed to set the log file path!");
108///
109/// // Enable the output
110/// file_output.enable()
111///     .expect("Failed to enable the output!");
112///
113/// // Write to the log file buffer
114/// file_output.out(&LogStruct::debug("Hello from file!"), &mut formatter)
115///     .expect("Failed to write to the buffer!");
116///
117/// // Flush the logs from the buffer to the log file
118/// file_output.flush();
119/// ```
120#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize,
121    Deserialize)]
122pub struct FileStream {
123    enabled: bool,
124    max_buffer_size: Option<usize>,
125    on_drop_policy: OnDropPolicy,
126
127    #[serde(skip)]
128    lock_enabled: bool,
129    #[serde(skip)]
130    log_file_path: String,
131    #[serde(skip)]
132    log_buffer: Vec<String>,
133}
134
135/// Used for storing logs in a buffer for later use.
136///
137/// # Examples
138/// ```
139/// # use prettylogger::{
140/// #     output::{BufferStream, Toggleable},
141/// #     config::LogStruct,
142/// # };
143/// let mut buffer_output = BufferStream::default();
144///
145/// // `BufferStream` is disabled by default
146/// buffer_output.enable();
147///
148/// // A formatter is not needed since `BufferStream` stores raw logs
149/// buffer_output.out(&LogStruct::debug("Hello from buffer!"));
150///
151/// // Obtain a reference to the log buffer
152/// let buffer = buffer_output.get_log_buffer();
153/// ````
154#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize,
155    Deserialize, Default)]
156pub struct BufferStream {
157    enabled: bool,
158
159    #[serde(skip)]
160    pub(crate) log_buffer: Vec<LogStruct>,
161}
162
163impl Drop for FileStream {
164    fn drop(&mut self) {
165        let _ = self.internal_flush(true);
166    }
167}
168
169impl PartialEq for LogOutput {
170    fn eq(&self, other: &Self) -> bool {
171        return self.enabled == other.enabled &&
172            self.stderr_output == other.stderr_output;
173    }
174}
175
176impl Default for LogOutput {
177    fn default() -> Self {
178        LogOutput {
179            enabled: true,
180            stderr_output: StderrStream::default(),
181            file_output: FileStream::default().into(),
182            buffer_output: BufferStream::default().into(),
183        }
184    }
185}
186
187impl Default for StderrStream {
188    fn default() -> Self {
189        StderrStream {
190            enabled: true,
191        }
192    }
193}
194
195impl Default for FileStream {
196    fn default() -> Self {
197        FileStream {
198            enabled: false,
199            max_buffer_size: Some(128),
200            on_drop_policy: OnDropPolicy::default(),
201
202            lock_enabled: false,
203            log_file_path: String::from(""),
204            log_buffer: Vec::new(),
205        }
206    }
207}
208
209impl Toggleable for LogOutput {
210    /// Enables the output.
211    fn enable(&mut self) {
212        self.enabled = true;
213    }
214
215    /// Disables the output.
216    fn disable(&mut self) {
217        self.enabled = false;
218    }
219
220    /// Returns whether the output is enabled.
221    fn is_enabled(&self) -> &bool {
222        return &self.enabled;
223    }
224}
225
226impl Toggleable for StderrStream {
227    /// Enables the output.
228    fn enable(&mut self) {
229        self.enabled = true;
230    }
231
232    /// Disables the output.
233    fn disable(&mut self) {
234        self.enabled = false;
235    }
236
237    /// Returns whether the output is enabled.
238    fn is_enabled(&self) -> &bool {
239        return &self.enabled;
240    }
241}
242
243impl Toggleable for BufferStream {
244    /// Enables the output.
245    fn enable(&mut self) {
246        self.enabled = true;
247    }
248
249    /// Disables the output.
250    fn disable(&mut self) {
251        self.enabled = false;
252    }
253
254    /// Returns whether the output is enabled.
255    fn is_enabled(&self) -> &bool {
256        return &self.enabled;
257    }
258}
259
260impl LogOutput {
261    /// Passes the log and its formatter to child streams for processing.
262    pub fn out(&self, log: &LogStruct, formatter: &mut LogFormatter) {
263        if self.enabled {
264            self.stderr_output.out(log, formatter);
265            let _ = self.file_output.lock().unwrap().out(log, formatter);
266            self.buffer_output.lock().unwrap().out(log);
267        }
268    }
269}
270
271impl StderrStream {
272    /// Formats the given log using a formatter and prints it to `stderr`.
273    pub fn out(self, log: &LogStruct, formatter: &mut LogFormatter) {
274        if self.enabled {
275            eprint!("{}", formatter.format_log(log));
276        }
277    }
278}
279
280impl FileStream {
281    fn push_to_buffer(&mut self, log: String) -> Result<(), Error> {
282        if !self.enabled {
283            return Err(Error::new("Output disabled!"));
284        }
285
286        self.log_buffer.push(log);
287
288        match self.max_buffer_size {
289            Some(size) => {
290                if self.log_buffer.len() >= size {
291                    return self.internal_flush(false);
292                }
293                else {
294                    return Ok(());
295                }
296            },
297            None => Ok(()),
298        }
299    }
300
301    /// Write contents of the log buffer to the log file and clear the buffer.
302    fn append_to_log_file(&mut self) -> Result<(), Error> {
303        let buf = self.log_buffer.join("");
304        self.log_buffer = Vec::new();
305        return append_to_file(&self.log_file_path, &buf);
306    }
307
308    /// Handle flushing logic internally.
309    pub(crate) fn internal_flush(&mut self, is_drop_flush: bool) -> Result<(), Error> {
310        if !self.enabled {
311            return Err(Error::new("Output not enabled!"));
312        }
313
314        if self.log_buffer.is_empty() {
315            return Err(Error::new("Log buffer is empty!"));
316        }
317
318        if is_drop_flush {
319            if self.lock_enabled {
320                if self.on_drop_policy == OnDropPolicy::IgnoreLogFileLock {
321                    return self.append_to_log_file();
322                }
323                else {
324                    return Err(Error::new(
325                        &format!("Lock is enabled and on drop policy se to '{}'!",
326                        self.on_drop_policy)));
327                }
328            }
329            else {
330                return self.append_to_log_file();
331            }
332        }
333
334        if self.lock_enabled {
335            return Err(Error::new("Lock is enabled."));
336        }
337        else {
338            return self.append_to_log_file();
339        }
340    }
341
342    pub(crate) fn drop_flush(&mut self) {
343        let _ = self.internal_flush(true);
344    }
345
346    /// Sets the log file path.
347    ///
348    /// # Examples
349    /// ```
350    /// # use prettylogger::{
351    /// #     output::{FileStream, Toggleable},
352    /// #     format::LogFormatter,
353    /// #     config::LogStruct,
354    /// # };
355    /// # let mut path = std::env::temp_dir();
356    /// # path.push("libprettylogger-tests/fo-set_log_file_path-doc.log");
357    /// # let path = &path.to_str().unwrap().to_string();
358    /// # let formatter = LogFormatter::default();
359    /// # let mut file_output = FileStream::default();
360    ///
361    /// // Set the log file path **first**
362    /// file_output.set_log_file_path(&path)
363    ///     .expect("Failed to set the log file path!");
364    ///
365    /// // And then enable the output
366    /// file_output.enable()
367    ///     .expect("Failed to enable the output!");
368    /// ```
369    pub fn set_log_file_path(&mut self, path: &str) -> Result<(), Error> {
370        match OpenOptions::new().write(true).create(true).truncate(true).open(path) {
371            Ok(_) => {
372                self.log_file_path = path.to_string();
373                match overwrite_file(path, "") {
374                    Ok(_) => Ok(()),
375                    Err(e) => {
376                        return Err(Error::new(&e.message));
377                    }
378                }
379            },
380            Err(e) => Err(Error::new(&format!("{}", e))),
381        }
382    }
383
384    /// Formats the given log using a formatter and stores it in a buffer until
385    /// it is flushed.
386    ///
387    /// # Examples
388    /// ```
389    /// # use prettylogger::{
390    /// #     output::{FileStream, Toggleable},
391    /// #     format::LogFormatter,
392    /// #     config::LogStruct,
393    /// # };
394    /// # let mut path = std::env::temp_dir();
395    /// # path.push("libprettylogger-tests/fo-out-doc.log");
396    /// # let path = &path.to_str().unwrap().to_string();
397    /// # let mut formatter = LogFormatter::default();
398    /// # let mut file_output = FileStream::default();
399    ///
400    /// // Set the log file path **first**
401    /// file_output.set_log_file_path(&path)
402    ///     .expect("Failed to set the log file path!");
403    ///
404    /// // And then enable the output
405    /// file_output.enable()
406    ///     .expect("Failed to enable the output!");
407    ///
408    /// // Write to the buffer 100 times
409    /// for i in 0..100 {
410    ///     file_output.out(&LogStruct::debug(&format!("Log number {}", i)),
411    ///         &mut formatter).expect("Failed to write to the buffer!");
412    /// }
413    ///
414    /// // Write the log buffer contents to the log file
415    /// file_output.flush();
416    /// ```
417    pub fn out(&mut self, log: &LogStruct, formatter: &mut LogFormatter)
418        -> Result<(), Error> {
419        return self.push_to_buffer(formatter.format_log(log));
420    }
421
422    /// Flush the contents of the log buffer to the log file.
423    ///
424    /// # Examples
425    /// ```
426    /// # use prettylogger::{
427    /// #     output::{FileStream, Toggleable},
428    /// #     format::LogFormatter,
429    /// #     config::LogStruct,
430    /// # };
431    /// # let mut path = std::env::temp_dir();
432    /// # path.push("libprettylogger-tests/fo-out-doc.log");
433    /// # let path = &path.to_str().unwrap().to_string();
434    /// # let mut formatter = LogFormatter::default();
435    /// # let mut file_output = FileStream::default();
436    ///
437    /// // Set the log file path **first**
438    /// file_output.set_log_file_path(&path)
439    ///     .expect("Failed to set the log file path!");
440    ///
441    /// // And then enable the output
442    /// file_output.enable()
443    ///     .expect("Failed to enable the output!");
444    ///
445    /// file_output.out(&LogStruct::debug(&format!("Hello from file!")),
446    ///     &mut formatter).expect("Failed to write to the buffer!");
447    ///
448    /// // Write the log buffer contents to the log file
449    /// file_output.flush();
450    /// ```
451    pub fn flush(&mut self) -> Result<(), Error> {
452        return self.internal_flush(false);
453    }
454
455    /// Sets the maximum size of the log buffer.
456    ///
457    /// When the buffer exceeds this size, its contents are written to a file
458    /// and then cleared.
459    ///
460    /// # Examples
461    /// ```
462    /// # use prettylogger::{
463    /// #     output::{FileStream, Toggleable},
464    /// #     format::LogFormatter,
465    /// #     config::LogStruct,
466    /// # };
467    /// # let mut path = std::env::temp_dir();
468    /// # path.push("libprettylogger-tests/fo-set_max_buffer_size-doc.log");
469    /// # let path = &path.to_str().unwrap().to_string();
470    /// # let mut formatter = LogFormatter::default();
471    /// # let mut file_output = FileStream::default();
472    /// // Set the log file path **first**
473    /// file_output.set_log_file_path(&path)
474    ///     .expect("Failed to set the log file path!");
475    ///
476    /// // And then enable the output
477    /// file_output.enable()
478    ///     .expect("Failed to enable the output!");
479    ///
480    /// // Define the maximum buffer size
481    /// let max_size = 128;
482    /// file_output.set_max_buffer_size(Some(max_size));
483    /// for i in 0..max_size {
484    ///     // Write to the buffer
485    ///     file_output.out(&LogStruct::debug(&format!("Log number {}", i)),
486    ///         &mut formatter).expect("Failed to write to the buffer!");
487    /// }
488    /// // Here the buffer will be flushed to the log file.
489    /// ```
490    pub fn set_max_buffer_size<I: Into<Option<usize>>>(&mut self, size: I) {
491        self.max_buffer_size = size.into();
492    }
493
494    /// Enables the output.
495    ///
496    /// Returns an error if the log file is not writable.
497    ///
498    /// # Examples
499    /// ```
500    /// # use prettylogger::{
501    /// #     output::{FileStream, Toggleable},
502    /// #     format::LogFormatter,
503    /// #     config::LogStruct,
504    /// # };
505    /// # let mut path = std::env::temp_dir();
506    /// # path.push("libprettylogger-tests/fo-enable-doc.log");
507    /// # let path = &path.to_str().unwrap().to_string();
508    /// # let formatter = LogFormatter::default();
509    /// # let mut file_output = FileStream::default();
510    ///
511    /// // Set the log file path **first**
512    /// file_output.set_log_file_path(&path)
513    ///     .expect("Failed to set the log file path!");
514    ///
515    /// // And then enable the output
516    /// file_output.enable()
517    ///     .expect("Failed to enable the output!");
518    /// ```
519    pub fn enable(&mut self) -> Result<(), Error> {
520        if self.enabled {
521            return Ok(());
522        }
523        else {
524            match OpenOptions::new().write(true).create(true).truncate(true)
525            .open(&self.log_file_path) {
526                Ok(_) => {
527                    self.enabled = true;
528                    return Ok(());
529                },
530                Err(e) => Err(Error::new(&format!("{}", e))),
531            }
532        }
533    }
534
535    /// Disables the output.
536    pub fn disable(&mut self) {
537        self.enabled = false;
538    }
539
540    /// Sets the policy for handling the log buffer lock when the stream is
541    /// dropped.
542    pub fn set_on_drop_policy<I: Into<OnDropPolicy>>(&mut self, policy: I) {
543        self.on_drop_policy = policy.into();
544    }
545
546    /// Locks the log file, preventing it from being written to.
547    pub fn lock_file(&mut self) {
548        self.lock_enabled = true;
549    }
550
551    /// Unlocks the log file, allowing the stream to write to it.
552    pub fn unlock_file(&mut self) {
553        self.lock_enabled = false;
554    }
555
556    /// Returns whether the output is enabled.
557    pub fn is_enabled(&self) -> &bool {
558        return &self.enabled;
559    }
560}
561
562impl BufferStream {
563    /// Formats the given log using a formatter and stores it in a buffer.
564    pub fn out(&mut self, log: &LogStruct) {
565        if self.enabled {
566            self.log_buffer.push(log.clone());
567        }
568    }
569
570    /// Returns a reference to the internal log struct buffer.
571    pub fn get_log_buffer(&self) -> &Vec<LogStruct> {
572        return &self.log_buffer;
573    }
574
575    /// Clears the log buffer.
576    pub fn clear(&mut self) {
577        self.log_buffer = Vec::new();
578    }
579}