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 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(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/// Used for printing logs to `stderr`.
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/// Used to output logs to a file.
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/// Used for storing logs in a buffer for later use.
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 whether 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 whether 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 whether 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 whether 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}