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 }
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}