flogging/logger/mod.rs
1//
2// File Name: mod.rs
3// Project Name: flogging
4//
5// Copyright (C) 2025 Bradley Willcott
6//
7// SPDX-License-Identifier: GPL-3.0-or-later
8//
9// This library (crate) is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// This library (crate) is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17// GNU General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this library (crate). If not, see <https://www.gnu.org/licenses/>.
21//
22
23//!
24//! # Logger
25//!
26//! `Logger` is the work-horse of the crate.
27//!
28//! It contains the primary functions to both create a logger instance, and has
29//! the methods to add log messages at various log levels.
30//!
31
32#![allow(clippy::needless_doctest_main)]
33
34mod builder;
35mod level;
36mod log_entry;
37
38use anyhow::{Context, Error, Result};
39use std::cell::{LazyCell, RefCell};
40use std::collections::hash_map::IterMut;
41use std::collections::{HashMap, HashSet};
42use std::f32::consts;
43use std::fmt;
44use std::fs::{File, exists};
45use std::io::Write;
46use std::marker::PhantomData;
47use std::module_path;
48use std::ops::DerefMut;
49use std::path::Path;
50use std::sync::mpsc::Sender;
51use std::sync::{Arc, MutexGuard, PoisonError, mpsc};
52use std::thread;
53
54use crate::handlers::{
55 handler::{self, Handler, HandlerTrait},
56 mock_handler::MockHandler,
57};
58pub use crate::logger::builder::*;
59pub use crate::logger::level::Level;
60pub use crate::logger::log_entry::LogEntry;
61
62///
63/// This is the work-horse, providing the primary methods of the crate.
64///
65pub struct Logger {
66 ///
67 /// Identify the source of log messages passed to this logger.
68 ///
69 /// This would ideally be the **mod** path.
70 ///
71 mod_path: String,
72
73 ///
74 /// The name of the function/method inside which the log message
75 /// is generated.
76 ///
77 fn_name: String,
78
79 ///
80 /// Default level used by `log(msg)`.
81 ///
82 level: Level,
83
84 ///
85 /// Holds the handlers associated with this logger.
86 ///
87 handlers: RefCell<HashMap<Handler, Box<dyn HandlerTrait>>>,
88}
89
90impl Logger {
91 ///
92 /// Create a new Logger instance.
93 ///
94 /// Logging level is set to it's default setting (INFO).
95 ///
96 /// No `handlers` are set. Use the various methods of
97 /// [`LoggerBuilder`] to configure the new `Logger`.
98 ///
99 /// ## Parameters
100 /// - `mod_path` - The module path. Can be set with: [`module_path!()`]
101 ///
102 /// Returns a `LoggerBuilder` for further configuring.
103 ///
104 /// ## Examples
105 /// ```
106 /// extern crate flogging;
107 /// use flogging::*;
108 ///
109 /// let mut log = Logger::builder(module_path!())
110 /// .add_console_handler()
111 /// .build();
112 /// ```
113 ///
114 pub fn builder(mod_path: &str) -> LoggerBuilder {
115 LoggerBuilder::create(mod_path.to_string())
116 }
117
118 ///
119 /// Log a CONFIG message.
120 ///
121 /// If the logger is currently enabled for the CONFIG message level
122 /// then the given message is forwarded to all the registered output
123 /// Handler objects.
124 ///
125 /// ## Parameters
126 /// - `msg` - The string message.
127 ///
128 /// ## Examples
129 /// ```
130 /// extern crate flogging;
131 /// use flogging::*;
132 ///
133 /// let mut log = Logger::console_logger(module_path!());
134 /// log.set_level(Level::CONFIG);
135 /// log.config("Some text to store.");
136 /// ```
137 ///
138 pub fn config(&mut self, msg: &str) {
139 self.log(Level::CONFIG, &self.fn_name(), msg);
140 }
141
142 ///
143 /// Create new Logger instance, with a `ConsoleHandler`.
144 ///
145 /// Logging level is set to it's default setting (INFO).
146 ///
147 /// ## Parameters
148 /// - `mod_path`- The module path. Suggest using [`module_path!()`].
149 ///
150 /// Returns a configured `Logger`.
151 ///
152 /// ## Examples
153 /// ```
154 /// extern crate flogging;
155 /// use flogging::*;
156 ///
157 /// let mut log = Logger::console_logger(module_path!());
158 /// log.set_fn_name("main");
159 ///
160 /// log.warning("Don't over do it.");
161 /// ```
162 /// Output:
163 /// ```text
164 /// |flogging->main| [WARNING] Don't over do it.
165 /// ```
166 ///
167 pub fn console_logger(mod_path: &str) -> Logger {
168 Logger::builder(mod_path).add_console_handler().build()
169 }
170
171 ///
172 /// Create new Logger instance, with a custom handler.
173 ///
174 /// ## Parameters
175 /// - `mod_path`- The module path. Suggest using [`module_path!()`].
176 /// - `label` - Unique label for this custom handler.
177 /// - `custom` - The boxed custom handler.
178 ///
179 /// Returns a configured `Logger`.
180 ///
181 /// ## Examples
182 /// ```
183 /// extern crate flogging;
184 /// use flogging::*;
185 ///
186 /// let mut log = Logger::custom_logger(
187 /// module_path!(),
188 /// "MockHandler",
189 /// Box::new(MockHandler::create("Some text").unwrap()),
190 /// );
191 /// log.set_fn_name("main");
192 ///
193 /// log.warning("Don't over do it.");
194 /// ```
195 /// [`MockHandler`] doesn't publish anything, as [`publish()`][MockHandler::publish()] is a **NoOp** method.
196 /// It is used here to make the example work.
197 ///
198 /// However, it is expected that _your_ custom handler will do a little more.
199 ///
200 pub fn custom_logger(mod_path: &str, label: &str, custom: Box<dyn HandlerTrait>) -> Logger {
201 Logger::builder(mod_path)
202 .add_custom_handler(label, custom)
203 .build()
204 }
205
206 ///
207 /// Log a method entry.
208 ///
209 /// This is a convenience method that can be used to log entry to a method.
210 /// A `LogEntry` with message "Entry" and log level FINER, is logged.
211 ///
212 /// ## Examples
213 /// ```
214 /// mod my_mod {
215 /// extern crate flogging;
216 /// use flogging::*;
217 /// use std::cell::{LazyCell, RefCell};
218 ///
219 /// // Setting up the module level logger.
220 /// const LOGGER: LazyCell<RefCell<Logger>> = LazyCell::new(|| {
221 /// RefCell::new({
222 /// Logger::builder(module_path!())
223 /// .add_console_handler()
224 /// .set_level(Level::FINEST)
225 /// .build()
226 /// })
227 /// });
228 ///
229 /// pub fn my_func(data: &str) {
230 /// let binding = LOGGER;
231 /// let mut log = binding.borrow_mut();
232 /// log.set_fn_name("my_func");
233 ///
234 /// log.entering();
235 /// }
236 /// }
237 ///
238 /// fn main() {
239 /// let data = "Some data";
240 /// my_mod::my_func(data);
241 /// }
242 /// ```
243 /// Output:
244 /// ```text
245 /// |flogging::my_mod->my_func| [FINER ] Entry
246 /// ```
247 ///
248 pub fn entering(&mut self) {
249 self.log(Level::FINER, &self.fn_name(), "Entry");
250 }
251
252 ///
253 /// Log a method entry.
254 ///
255 /// This is a convenience method that can be used to log entry to a method.
256 /// A `LogEntry` with message "Entry" and log level FINER, is logged.
257 ///
258 /// ## Parameters
259 /// - `msg` - The string message.
260 ///
261 /// ## Examples
262 /// ```
263 /// mod my_mod {
264 /// extern crate flogging;
265 /// use flogging::*;
266 /// use std::cell::{LazyCell, RefCell};
267 ///
268 /// // Setting up the module level logger.
269 /// const LOGGER: LazyCell<RefCell<Logger>> = LazyCell::new(|| {
270 /// RefCell::new({
271 /// Logger::builder(module_path!())
272 /// .add_console_handler()
273 /// .set_level(Level::FINEST)
274 /// .build()
275 /// })
276 /// });
277 ///
278 /// pub fn my_func(data: &str) {
279 /// let binding = LOGGER;
280 /// let mut log = binding.borrow_mut();
281 /// log.set_fn_name("my_func");
282 ///
283 /// log.entering_with(&format!("data: \"{data}\""));
284 /// }
285 /// }
286 ///
287 /// fn main() {
288 /// let data = "Some data";
289 /// my_mod::my_func(data);
290 /// }
291 /// ```
292 /// Output:
293 /// ```text
294 /// |flogging::my_mod->my_func| [FINER ] Entry: (data: "Some data")
295 /// ```
296 ///
297 pub fn entering_with(&mut self, msg: &str) {
298 self.log(
299 Level::FINER,
300 &self.fn_name(),
301 &("Entry: (".to_string() + msg + ")"),
302 );
303 }
304
305 ///
306 /// Log a method return.
307 ///
308 /// This is a convenience method that can be used to log returning from a method.
309 /// A `LogEntry` with message "Return" and log level FINER, is logged.
310 ///
311 /// ## Examples
312 /// ```
313 /// mod my_mod {
314 /// extern crate flogging;
315 /// use flogging::*;
316 /// use std::cell::{LazyCell, RefCell};
317 ///
318 /// // Setting up the module level logger.
319 /// const LOGGER: LazyCell<RefCell<Logger>> = LazyCell::new(|| {
320 /// RefCell::new({
321 /// Logger::builder(module_path!())
322 /// .add_console_handler()
323 /// .set_level(Level::FINEST)
324 /// .build()
325 /// })
326 /// });
327 ///
328 /// pub fn my_func(data: &str) {
329 /// let binding = LOGGER;
330 /// let mut log = binding.borrow_mut();
331 /// log.set_fn_name("my_func");
332 ///
333 /// log.exiting();
334 /// }
335 /// }
336 ///
337 /// fn main() {
338 /// let data = "Some data";
339 /// my_mod::my_func(data);
340 /// }
341 /// ```
342 /// Output:
343 /// ```text
344 /// |flogging::my_mod->my_func| [FINER ] Return
345 /// ```
346 ///
347 pub fn exiting(&mut self) {
348 self.log(Level::FINER, &self.fn_name(), "Return");
349 }
350
351 ///
352 /// Log a method return.
353 ///
354 /// This is a convenience method that can be used to log returning from a method.
355 /// A `LogEntry` with message "Return" and log level FINER, is logged.
356 ///
357 /// ## Parameters
358 /// - `msg` - The string message.
359 ///
360 /// ## Examples
361 /// ```
362 /// mod my_mod {
363 /// extern crate flogging;
364 /// use flogging::*;
365 /// use std::cell::{LazyCell, RefCell};
366 ///
367 /// // Setting up the module level logger.
368 /// const LOGGER: LazyCell<RefCell<Logger>> = LazyCell::new(|| {
369 /// RefCell::new({
370 /// Logger::builder(module_path!())
371 /// .add_console_handler()
372 /// .set_level(Level::FINEST)
373 /// .build()
374 /// })
375 /// });
376 ///
377 /// pub fn my_func(data: &str) -> bool {
378 /// let binding = LOGGER;
379 /// let mut log = binding.borrow_mut();
380 /// log.set_fn_name("my_func");
381 ///
382 /// let rtn = true;
383 /// log.exiting_with(&format!("rtn: {rtn}"));
384 /// rtn
385 /// }
386 /// }
387 ///
388 /// fn main() {
389 /// let data = "Some data";
390 /// my_mod::my_func(data);
391 /// }
392 /// ```
393 /// Output:
394 /// ```text
395 /// |flogging::my_mod->my_func| [FINER ] Return: (rtn: true)
396 /// ```
397 ///
398 pub fn exiting_with(&mut self, msg: &str) {
399 self.log(
400 Level::FINER,
401 &self.fn_name(),
402 &("Return: (".to_string() + msg + ")"),
403 );
404 }
405
406 ///
407 /// Create new Logger instance, with a `FileHandler`.
408 ///
409 /// Logging level is set to it's default setting (INFO).
410 ///
411 /// ## Parameters
412 /// - `mod_path`- The module path. Suggest using [`std::module_path`][mp].
413 /// - `filename` - The name of the log file to use. Will be created
414 /// if it doesn't exist.
415 ///
416 /// Returns a configured `Logger`.
417 ///
418 /// ## Examples
419 /// ```
420 /// extern crate flogging;
421 /// use flogging::Logger;
422 ///
423 /// let mut log = Logger::file_logger(module_path!(), "test.log");
424 /// log.set_fn_name("main");
425 ///
426 /// log.info("Some text to store.");
427 /// ```
428 /// Output:
429 /// ```text
430 /// 2025-07-18T12:14:47.322720683+08:00 |flogging->main| [INFO ] Some text to store.
431 /// ```
432 ///
433 /// [mp]: https://doc.rust-lang.org/std/macro.module_path.html
434 pub fn file_logger(mod_path: &str, filename: &str) -> Logger {
435 Logger::builder(mod_path).add_file_handler(filename).build()
436 }
437
438 ///
439 /// Log a FINE message.
440 ///
441 /// If the logger is currently enabled for the FINE message level
442 /// then the given message is forwarded to all the registered output
443 /// Handler objects.
444 ///
445 /// ## Parameters
446 /// - `msg` - The string message.
447 ///
448 /// ## Examples
449 /// ```
450 /// extern crate flogging;
451 /// use flogging::*;
452 ///
453 /// let mut log = Logger::console_logger(module_path!());
454 /// log.set_level(Level::FINEST);
455 /// log.set_fn_name("main");
456 ///
457 /// log.fine("Some text to store.");
458 /// ```
459 /// Output:
460 /// ```text
461 /// |flogging->main| [FINE ] Some text to store.
462 /// ```
463 ///
464 pub fn fine(&mut self, msg: &str) {
465 self.log(Level::FINE, &self.fn_name(), msg);
466 }
467
468 ///
469 /// Log a FINER message.
470 ///
471 /// If the logger is currently enabled for the FINER message level
472 /// then the given message is forwarded to all the registered output
473 /// Handler objects.
474 ///
475 /// ## Parameters
476 /// - `msg` - The string message.
477 ///
478 /// ## Examples
479 /// ```
480 /// extern crate flogging;
481 /// use flogging::*;
482 ///
483 /// let mut log = Logger::console_logger(module_path!());
484 /// log.set_level(Level::FINEST);
485 /// log.set_fn_name("main");
486 ///
487 /// log.finer("Some text to store.");
488 /// ```
489 /// Output:
490 /// ```text
491 /// |flogging->main| [FINER ] Some text to store.
492 /// ```
493 ///
494 pub fn finer(&mut self, msg: &str) {
495 self.log(Level::FINER, &self.fn_name(), msg);
496 }
497
498 ///
499 /// Log a FINEST message.
500 ///
501 /// If the logger is currently enabled for the FINEST message level
502 /// then the given message is forwarded to all the registered output
503 /// Handler objects.
504 ///
505 /// ## Parameters
506 /// - `msg` - The string message.
507 ///
508 /// ## Examples
509 /// ```
510 /// extern crate flogging;
511 /// use flogging::*;
512 ///
513 /// let mut log = Logger::console_logger(module_path!());
514 /// log.set_level(Level::FINEST);
515 /// log.set_fn_name("main");
516 ///
517 /// log.finest("Some text to store.");
518 /// ```
519 /// Output:
520 /// ```text
521 /// |flogging->main| [FINEST ] Some text to store.
522 /// ```
523 ///
524 pub fn finest(&mut self, msg: &str) {
525 self.log(Level::FINEST, &self.fn_name(), msg);
526 }
527
528 ///
529 /// Get the current function/method name.
530 ///
531 pub fn fn_name(&self) -> String {
532 self.fn_name.clone()
533 }
534
535 ///
536 /// Get required `Handler`.
537 ///
538 /// ## Parameters
539 /// - `handler` - The enum of the required handler.
540 ///
541 /// Returns Some boxed handler, or None.
542 ///
543 /// ## Examples
544 /// ```
545 /// extern crate flogging;
546 /// use flogging::*;
547 ///
548 /// let mut log = Logger::string_logger(module_path!());
549 /// log.set_fn_name("get_handler");
550 ///
551 /// log.info("Some text to store.");
552 ///
553 /// let h = log.get_handler(Handler::String).unwrap();
554 /// println!("{h}");
555 /// ```
556 pub fn get_handler(&mut self, handler: Handler) -> Option<Box<&mut dyn HandlerTrait>> {
557 match self.handlers.get_mut().get_mut(&handler) {
558 Some(val) => Some(Box::new(&mut **val)),
559 None => None,
560 }
561 }
562
563 ///
564 /// Check if the required `Handler` has been added to this `Logger`.
565 ///
566 /// ## Parameters
567 /// - `handler` - The enum of the required handler.
568 ///
569 /// Returns `true` if it exists, `false` otherwise.
570 ///
571 /// ## Examples
572 /// ```
573 /// extern crate flogging;
574 /// use flogging::*;
575 ///
576 /// let mut log = Logger::string_logger(module_path!());
577 /// log.info("Some text to store.");
578 ///
579 /// println!("This logger has a 'StringHandler': {}", log.has_handler(Handler::String));
580 /// ```
581 pub fn has_handler(&self, handler: Handler) -> bool {
582 self.handlers.borrow().contains_key(&handler)
583 }
584 ///
585 /// Log a INFO message.
586 ///
587 /// If the logger is currently enabled for the INFO message level
588 /// then the given message is forwarded to all the registered output
589 /// Handler objects.
590 ///
591 /// ## Parameters
592 /// - `msg` - The string message.
593 ///
594 /// ## Examples
595 /// ```
596 /// extern crate flogging;
597 /// use flogging::*;
598 ///
599 /// let mut log = Logger::console_logger(module_path!());
600 /// log.set_level(Level::FINEST);
601 /// log.set_fn_name("main");
602 ///
603 /// log.info("Some text to store.");
604 /// ```
605 /// Output:
606 /// ```text
607 /// |flogging->main| [INFO ] Some text to store.
608 /// ```
609 ///
610 pub fn info(&mut self, msg: &str) {
611 self.log(Level::INFO, &self.fn_name(), msg);
612 }
613
614 ///
615 /// Check if a message of the given level would actually be logged by this logger.
616 ///
617 /// ## Parameters
618 /// - `level` - The level to compare with.
619 ///
620 /// Returns `true` if it is loggable, `false` otherwise.
621 ///
622 fn is_loggable(&self, level: &Level) -> bool {
623 *level >= self.level
624 }
625
626 ///
627 /// Obtain the current default logging level for this Log instance.
628 ///
629 pub fn level(&self) -> &Level {
630 &self.level
631 }
632
633 ///
634 /// Log a `LogEntry`.
635 ///
636 /// All the other logging methods in this class call through this method to actually
637 /// perform any logging.
638 ///
639 /// ## Parameters
640 /// - `entry` - The `LogEntry` to be published.
641 ///
642 fn _log(&mut self, entry: &mut LogEntry) {
643 entry.set_mod_path(self.mod_path.clone());
644
645 for handler in self.handlers.get_mut() {
646 handler.1.publish(entry);
647 }
648 }
649
650 ///
651 /// Log a message, with no arguments.
652 ///
653 /// If the logger is currently enabled for the given message level then the given
654 /// message is forwarded to all the registered output `Handler` objects.
655 ///
656 /// ## Parameters
657 /// - `level` - One of the message level identifiers, e.g., SEVERE.
658 /// - `fn_name` - The name of the function/method from-which this method
659 /// was called.
660 /// - `msg` - The string message.
661 ///
662 fn log(&mut self, level: Level, fn_name: &str, msg: &str) {
663 if !self.is_loggable(&level) {
664 return;
665 }
666
667 // build LogEntry
668 let mut log_entry = LogEntry::create(level, fn_name.to_string(), msg.to_string());
669 // Send LogEntry
670 self._log(&mut log_entry);
671 }
672
673 ///
674 /// Reset this `Logger` instance's default logging level.
675 ///
676 /// Returns itself for chaining purposes.
677 ///
678 /// See [Level]
679 ///
680 pub fn reset_level(&mut self) -> &mut Self {
681 self.level = Level::default();
682 self
683 }
684
685 ///
686 /// Set the current function/method name.
687 ///
688 /// ## Parameters
689 /// - `fn_name` - The name of the function/method in which you are
690 /// logging.
691 ///
692 /// Returns itself for chaining purposes.
693 ///
694 pub fn set_fn_name(&mut self, fn_name: &str) -> &mut Self {
695 self.fn_name = fn_name.to_string();
696 self
697 }
698
699 ///
700 /// Set default logging level for this Log instance.
701 ///
702 /// ## Parameters
703 /// - `level` - The new logging level to set.
704 ///
705 /// Returns itself for chaining purposes.
706 ///
707 pub fn set_level(&mut self, level: Level) -> &mut Self {
708 self.level = level;
709 self
710 }
711
712 ///
713 /// Log a SEVERE message.
714 ///
715 /// If the logger is currently enabled for the SEVERE message level
716 /// then the given message is forwarded to all the registered output
717 /// Handler objects.
718 ///
719 /// ## Parameters
720 /// - `msg` - The string message.
721 ///
722 /// ## Examples
723 /// ```
724 /// extern crate flogging;
725 /// use flogging::*;
726 ///
727 /// let mut log = Logger::console_logger(module_path!());
728 /// log.set_level(Level::FINEST);
729 /// log.set_fn_name("main");
730 ///
731 /// log.severe("Some text to store.");
732 /// ```
733 /// Output:
734 /// ```text
735 /// |flogging->main| [SEVERE ] Some text to store.
736 /// ```
737 ///
738 pub fn severe(&mut self, msg: &str) {
739 self.log(Level::SEVERE, &self.fn_name(), msg);
740 }
741
742 ///
743 /// Create new Logger instance, with a `ConsoleHandler`.
744 ///
745 /// Logging level is set to it's default setting (INFO).
746 ///
747 /// I expect this will be primarily used during unit testing of
748 /// the logging output. Though, any requirement to pass-on the log entry,
749 /// perhaps for further processing, would also be a valid use case.
750 ///
751 /// ## Parameters
752 /// - `mod_path`- The module path. Suggest using [`module_path!()`].
753 ///
754 /// Returns a configured `Logger`.
755 ///
756 /// ## Examples
757 /// ```
758 /// extern crate flogging;
759 /// use flogging::*;
760 ///
761 /// let mut log = Logger::string_logger(module_path!());
762 /// log.set_fn_name("main");
763 ///
764 /// log.warning("Don't over do it.");
765 ///
766 /// let log_str = log.get_handler(Handler::String).unwrap().get_log();
767 ///
768 /// println!("{log_str}");
769 /// ```
770 /// Output:
771 /// ```text
772 /// |flogging->main| [WARNING] Don't over do it.
773 /// ```
774 ///
775 pub fn string_logger(mod_path: &str) -> Logger {
776 Logger::builder(mod_path).add_string_handler().build()
777 }
778
779 ///
780 /// Log a WARNING message.
781 ///
782 /// If the logger is currently enabled for the WARNING message level
783 /// then the given message is forwarded to all the registered output
784 /// Handler objects.
785 ///
786 /// ## Parameters
787 /// - `msg` - The string message.
788 ///
789 /// ## Examples
790 /// ```
791 /// extern crate flogging;
792 /// use flogging::*;
793 ///
794 /// let mut log = Logger::console_logger(module_path!());
795 /// log.set_level(Level::FINEST);
796 /// log.set_fn_name("main");
797 ///
798 /// log.warning("Some text to store.");
799 /// ```
800 /// Output:
801 /// ```text
802 /// |flogging->main| [WARNING] Some text to store.
803 /// ```
804 ///
805 pub fn warning(&mut self, msg: &str) {
806 self.log(Level::WARNING, &self.fn_name(), msg);
807 }
808}
809
810impl fmt::Display for Logger {
811 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812 let mut buf = String::new();
813
814 for elem in self.handlers.borrow().iter() {
815 let s = format!("{}: {}\n", elem.0, elem.1);
816 buf.push_str(&s);
817 }
818
819 writeln!(
820 f,
821 "{}::{} - [{}]\n\n{}",
822 self.mod_path, self.fn_name, self.level, buf
823 )
824 }
825}
826
827#[cfg(test)]
828mod tests;