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