irox_log/
console.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3//
4
5//!
6//! Basic console log implementation.  Unconfigurable at the moment.
7//!
8//! Outputs in the format of: `[ThhMMssZ {LEVEL} {ThreadName} {Module}] {message}`
9
10// Allow uninlined is needed because RustRover doesn't yet detect that these are actually *used*
11// and will helpfully remove the imports during "optimization"
12#![allow(clippy::uninlined_format_args)]
13
14use std::io::Write;
15
16use log::{Level, Metadata, Record};
17
18use irox_time::datetime::UTCDateTime;
19use irox_time::format::iso8601::BASIC_TIME_OF_DAY;
20use irox_tools::ansi_colors::{
21    FORMAT_COLOR_FG_BLUE, FORMAT_COLOR_FG_CYAN, FORMAT_COLOR_FG_MAGENTA, FORMAT_COLOR_FG_RED,
22    FORMAT_COLOR_FG_YELLOW, FORMAT_RESET,
23};
24
25macro_rules! mk_log {
26    ($name:ident, $level:expr) => {
27        pub static $name: ConsoleLogger = ConsoleLogger { max_level: $level };
28    };
29}
30
31mk_log!(ERROR_LOGGER, Level::Error);
32mk_log!(WARN_LOGGER, Level::Warn);
33mk_log!(INFO_LOGGER, Level::Info);
34mk_log!(DEBUG_LOGGER, Level::Debug);
35mk_log!(TRACE_LOGGER, Level::Trace);
36
37///
38/// Basic console logger with a static format.
39pub struct ConsoleLogger {
40    max_level: Level,
41}
42
43impl log::Log for ConsoleLogger {
44    fn enabled(&self, metadata: &Metadata) -> bool {
45        self.max_level >= metadata.level()
46    }
47
48    fn log(&self, record: &Record) {
49        if !self.enabled(record.metadata()) {
50            return;
51        }
52        let level = match record.level() {
53            Level::Error => format!("{}ERROR{}", FORMAT_COLOR_FG_RED, FORMAT_RESET),
54            Level::Warn => format!("{}WARN{FORMAT_RESET}", FORMAT_COLOR_FG_YELLOW),
55            Level::Info => format!("{}INFO{FORMAT_RESET}", FORMAT_COLOR_FG_BLUE),
56            Level::Debug => format!("{}DEBUG{FORMAT_RESET}", FORMAT_COLOR_FG_MAGENTA),
57            Level::Trace => format!("{}TRACE{FORMAT_RESET}", FORMAT_COLOR_FG_CYAN),
58        };
59        let time = UTCDateTime::now().format(&BASIC_TIME_OF_DAY);
60        let thread = std::thread::current();
61        let thread = thread.name().unwrap_or("").split("::").last().unwrap_or("");
62        let module = record
63            .module_path()
64            .unwrap_or("")
65            .split("::")
66            .last()
67            .unwrap_or("");
68        if let Err(_e) = writeln!(
69            std::io::stderr(),
70            "[{time} {level} {thread} {module}] {}",
71            record.args()
72        ) {
73            // idk, lets eat it?
74        }
75    }
76
77    fn flush(&self) {
78        let _ign = std::io::stderr().flush();
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use log::*;
85
86    use crate::init_console_level;
87
88    #[test]
89    pub fn test() {
90        init_console_level(Level::Trace);
91
92        error!("Test Error!");
93        warn!("Test Warn!");
94        info!("Test Info!");
95        debug!("Test Debug!");
96        trace!("Test Trace!");
97    }
98}