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}