1use crate::engine::ENGINE;
12use crate::log::Log;
13use crate::log_format::LogFormat;
14use crate::log_level::LogLevel;
15
16#[must_use]
18pub const fn map_log_level(level: log::Level) -> LogLevel {
19 match level {
20 log::Level::Error => LogLevel::ERROR,
21 log::Level::Warn => LogLevel::WARN,
22 log::Level::Info => LogLevel::INFO,
23 log::Level::Debug => LogLevel::DEBUG,
24 log::Level::Trace => LogLevel::TRACE,
25 }
26}
27
28#[must_use]
30pub const fn to_log_level_filter(level: LogLevel) -> log::LevelFilter {
31 match level {
32 LogLevel::ALL | LogLevel::TRACE => log::LevelFilter::Trace,
33 LogLevel::DEBUG => log::LevelFilter::Debug,
34 LogLevel::VERBOSE | LogLevel::INFO => log::LevelFilter::Info,
35 LogLevel::WARN => log::LevelFilter::Warn,
36 LogLevel::ERROR | LogLevel::FATAL | LogLevel::CRITICAL => {
37 log::LevelFilter::Error
38 }
39 LogLevel::NONE | LogLevel::DISABLED => log::LevelFilter::Off,
40 }
41}
42
43#[derive(Debug, Clone, Copy)]
45pub struct RlgLogger {
46 format: LogFormat,
47}
48
49impl RlgLogger {
50 #[must_use]
52 pub const fn new(format: LogFormat) -> Self {
53 Self { format }
54 }
55}
56
57impl log::Log for RlgLogger {
58 fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
59 map_log_level(metadata.level()).to_numeric()
60 >= ENGINE.filter_level()
61 }
62
63 fn log(&self, record: &log::Record<'_>) {
64 if !self.enabled(record.metadata()) {
65 return;
66 }
67
68 let level = map_log_level(record.level());
69 let mut entry = Log::build(level, &record.args().to_string());
70 entry.component =
71 std::borrow::Cow::Owned(record.target().to_string());
72 entry.format = self.format;
73
74 if let Some(file) = record.file() {
75 entry = entry.with("file", file);
76 }
77 if let Some(line) = record.line() {
78 entry = entry.with("line", line);
79 }
80 if let Some(module) = record.module_path() {
81 entry = entry.with("module", module);
82 }
83
84 entry.fire();
85 }
86
87 fn flush(&self) {
88 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_map_log_level_all_variants() {
98 assert_eq!(map_log_level(log::Level::Error), LogLevel::ERROR);
99 assert_eq!(map_log_level(log::Level::Warn), LogLevel::WARN);
100 assert_eq!(map_log_level(log::Level::Info), LogLevel::INFO);
101 assert_eq!(map_log_level(log::Level::Debug), LogLevel::DEBUG);
102 assert_eq!(map_log_level(log::Level::Trace), LogLevel::TRACE);
103 }
104
105 #[test]
106 fn test_to_log_level_filter_all_variants() {
107 assert_eq!(
108 to_log_level_filter(LogLevel::ALL),
109 log::LevelFilter::Trace
110 );
111 assert_eq!(
112 to_log_level_filter(LogLevel::TRACE),
113 log::LevelFilter::Trace
114 );
115 assert_eq!(
116 to_log_level_filter(LogLevel::DEBUG),
117 log::LevelFilter::Debug
118 );
119 assert_eq!(
120 to_log_level_filter(LogLevel::VERBOSE),
121 log::LevelFilter::Info
122 );
123 assert_eq!(
124 to_log_level_filter(LogLevel::INFO),
125 log::LevelFilter::Info
126 );
127 assert_eq!(
128 to_log_level_filter(LogLevel::WARN),
129 log::LevelFilter::Warn
130 );
131 assert_eq!(
132 to_log_level_filter(LogLevel::ERROR),
133 log::LevelFilter::Error
134 );
135 assert_eq!(
136 to_log_level_filter(LogLevel::FATAL),
137 log::LevelFilter::Error
138 );
139 assert_eq!(
140 to_log_level_filter(LogLevel::CRITICAL),
141 log::LevelFilter::Error
142 );
143 assert_eq!(
144 to_log_level_filter(LogLevel::NONE),
145 log::LevelFilter::Off
146 );
147 assert_eq!(
148 to_log_level_filter(LogLevel::DISABLED),
149 log::LevelFilter::Off
150 );
151 }
152
153 #[test]
154 fn test_rlg_logger_new() {
155 let logger = RlgLogger::new(LogFormat::JSON);
156 assert_eq!(format!("{logger:?}"), "RlgLogger { format: JSON }");
157 }
158
159 #[test]
160 fn test_rlg_logger_clone_copy() {
161 let logger = RlgLogger::new(LogFormat::MCP);
162 let cloned = logger;
163 let _ = format!("{logger:?}");
165 let _ = format!("{cloned:?}");
166 }
167
168 #[test]
169 fn test_rlg_logger_enabled() {
170 let logger = RlgLogger::new(LogFormat::JSON);
171 let metadata = log::MetadataBuilder::new()
173 .level(log::Level::Trace)
174 .build();
175 assert!(log::Log::enabled(&logger, &metadata));
176
177 let metadata = log::MetadataBuilder::new()
178 .level(log::Level::Error)
179 .build();
180 assert!(log::Log::enabled(&logger, &metadata));
181 }
182
183 #[test]
184 #[cfg_attr(miri, ignore)]
185 fn test_rlg_logger_log_with_metadata() {
186 let logger = RlgLogger::new(LogFormat::JSON);
187
188 let record = log::RecordBuilder::new()
190 .args(format_args!("test log message"))
191 .level(log::Level::Info)
192 .target("test_target")
193 .file(Some("test_file.rs"))
194 .line(Some(42))
195 .module_path(Some("test_module"))
196 .build();
197
198 log::Log::log(&logger, &record);
199 }
200
201 #[test]
202 #[cfg_attr(miri, ignore)]
203 fn test_rlg_logger_log_without_metadata() {
204 let logger = RlgLogger::new(LogFormat::MCP);
205
206 let record = log::RecordBuilder::new()
208 .args(format_args!("minimal message"))
209 .level(log::Level::Warn)
210 .target("minimal_target")
211 .build();
212
213 log::Log::log(&logger, &record);
214 }
215
216 #[test]
217 #[cfg_attr(miri, ignore)]
218 fn test_rlg_logger_log_all_levels() {
219 let logger = RlgLogger::new(LogFormat::JSON);
220
221 for level in &[
222 log::Level::Error,
223 log::Level::Warn,
224 log::Level::Info,
225 log::Level::Debug,
226 log::Level::Trace,
227 ] {
228 let record = log::RecordBuilder::new()
229 .args(format_args!("level test"))
230 .level(*level)
231 .target("level_test")
232 .build();
233 log::Log::log(&logger, &record);
234 }
235 }
236
237 #[test]
238 fn test_rlg_logger_flush() {
239 let logger = RlgLogger::new(LogFormat::JSON);
240 log::Log::flush(&logger); }
242}