log/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5use alloc::fmt;
6use log_external::Log;
7use log_external::Metadata;
8use log_external::set_logger;
9use log_external::set_max_level;
10pub use log_external::{debug, error, info as information, trace, warn as warning};
11use synchronization::once_lock::OnceLock;
12
13const BOLD: &str = "\x1b[0;1m";
14const RED: &str = "\x1b[0;41m";
15const YELLOW: &str = "\x1b[0;43m";
16const BLUE: &str = "\x1b[0;46m";
17const GREEN: &str = "\x1b[0;42m";
18const GREY: &str = "\x1b[0;100m";
19const RESET: &str = "\x1b[0m";
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum Level {
23    Error = 1,
24    Warn,
25    Info,
26    Debug,
27    Trace,
28}
29
30pub struct Record<'a> {
31    pub level: Level,
32    pub target: &'a str,
33    pub arguments: fmt::Arguments<'a>,
34}
35
36impl<'a> Record<'a> {
37    fn from_log_external(record: &log_external::Record<'a>) -> Self {
38        Self {
39            level: Level::from(record.level()),
40            target: record.target(),
41            arguments: *record.args(),
42        }
43    }
44}
45
46impl From<log_external::Level> for Level {
47    fn from(level: log_external::Level) -> Self {
48        match level {
49            log_external::Level::Error => Level::Error,
50            log_external::Level::Warn => Level::Warn,
51            log_external::Level::Info => Level::Info,
52            log_external::Level::Debug => Level::Debug,
53            log_external::Level::Trace => Level::Trace,
54        }
55    }
56}
57
58pub trait LoggerTrait: Send + Sync {
59    fn enabled(&self, level: Level) -> bool;
60
61    fn write(&self, arguments: fmt::Arguments);
62
63    fn log(&self, record: &Record) {
64        let (letter, color) = match record.level {
65            Level::Error => ("E", RED),
66            Level::Warn => ("W", YELLOW),
67            Level::Info => ("I", BLUE),
68            Level::Debug => ("D", GREEN),
69            Level::Trace => ("T", GREY),
70        };
71
72        self.write(format_args!(
73            "{} {} {} | {}{}{} | {}",
74            color, letter, RESET, BOLD, record.target, RESET, record.arguments
75        ))
76    }
77
78    fn flush(&self) {
79        // Implement flush logic if needed, e.g., flushing buffers to a file or console
80    }
81}
82
83struct Logger(&'static dyn LoggerTrait);
84
85impl Log for Logger {
86    fn enabled(&self, metadata: &Metadata) -> bool {
87        self.0.enabled(Level::from(metadata.level()))
88    }
89
90    fn log(&self, record: &log_external::Record) {
91        if !self.enabled(record.metadata()) {
92            return;
93        }
94        self.0.log(&Record::from_log_external(record));
95    }
96
97    fn flush(&self) {
98        self.0.flush();
99    }
100}
101
102static LOGGER_INSTANCE: OnceLock<Logger> = OnceLock::new();
103
104pub fn initialize(logger: &'static dyn LoggerTrait) -> Result<(), log_external::SetLoggerError> {
105    let log = LOGGER_INSTANCE.get_or_init(|| Logger(logger));
106
107    if set_logger(log).is_err() {
108        logger.log(&Record {
109            level: Level::Error,
110            target: "log",
111            arguments: format_args!("Logger has already been initialized."),
112        });
113    }
114
115    set_max_level(log_external::LevelFilter::Trace);
116    Ok(())
117}
118
119pub fn is_initialized() -> bool {
120    LOGGER_INSTANCE.try_get().is_some()
121}
122
123pub fn test_write(logger: &impl LoggerTrait) {
124    for i in 0..5 {
125        logger.write(format_args!("This is a test message number {i}."));
126    }
127}
128
129pub fn test_log(logger: &impl LoggerTrait) {
130    logger.log(&Record {
131        level: Level::Info,
132        target: "test_target",
133        arguments: format_args!("This is a test log message."),
134    });
135    logger.log(&Record {
136        level: Level::Warn,
137        target: "test_target",
138        arguments: format_args!("This is a test warning message."),
139    });
140    logger.log(&Record {
141        level: Level::Error,
142        target: "test_target",
143        arguments: format_args!("This is a test error message."),
144    });
145    logger.log(&Record {
146        level: Level::Debug,
147        target: "test_target",
148        arguments: format_args!("This is a test debug message."),
149    });
150    logger.log(&Record {
151        level: Level::Trace,
152        target: "test_target",
153        arguments: format_args!("This is a test trace message."),
154    });
155}
156
157pub fn test_flush(logger: &impl LoggerTrait) {
158    logger.flush();
159}