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