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 #[serial_test::serial(engine_filter)]
170 fn test_rlg_logger_enabled() {
171 let prior = ENGINE.filter_level();
175 ENGINE.set_filter(LogLevel::ALL.to_numeric());
176
177 let logger = RlgLogger::new(LogFormat::JSON);
178 let metadata = log::MetadataBuilder::new()
179 .level(log::Level::Trace)
180 .build();
181 assert!(log::Log::enabled(&logger, &metadata));
182
183 let metadata = log::MetadataBuilder::new()
184 .level(log::Level::Error)
185 .build();
186 assert!(log::Log::enabled(&logger, &metadata));
187
188 ENGINE.set_filter(prior);
189 }
190
191 #[test]
192 #[cfg_attr(miri, ignore)]
193 #[serial_test::serial(engine_filter)]
194 fn test_rlg_logger_log_below_filter_short_circuits() {
195 let prior = ENGINE.filter_level();
198 ENGINE.set_filter(LogLevel::ERROR.to_numeric());
199 let logger = RlgLogger::new(LogFormat::JSON);
200 let record = log::RecordBuilder::new()
201 .args(format_args!("filtered"))
202 .level(log::Level::Info)
203 .target("filter_test")
204 .build();
205 log::Log::log(&logger, &record);
206 ENGINE.set_filter(prior);
208 }
209
210 #[test]
211 #[cfg_attr(miri, ignore)]
212 fn test_rlg_logger_log_with_metadata() {
213 let logger = RlgLogger::new(LogFormat::JSON);
214
215 let record = log::RecordBuilder::new()
217 .args(format_args!("test log message"))
218 .level(log::Level::Info)
219 .target("test_target")
220 .file(Some("test_file.rs"))
221 .line(Some(42))
222 .module_path(Some("test_module"))
223 .build();
224
225 log::Log::log(&logger, &record);
226 }
227
228 #[test]
229 #[cfg_attr(miri, ignore)]
230 fn test_rlg_logger_log_without_metadata() {
231 let logger = RlgLogger::new(LogFormat::MCP);
232
233 let record = log::RecordBuilder::new()
235 .args(format_args!("minimal message"))
236 .level(log::Level::Warn)
237 .target("minimal_target")
238 .build();
239
240 log::Log::log(&logger, &record);
241 }
242
243 #[test]
244 #[cfg_attr(miri, ignore)]
245 fn test_rlg_logger_log_all_levels() {
246 let logger = RlgLogger::new(LogFormat::JSON);
247
248 for level in &[
249 log::Level::Error,
250 log::Level::Warn,
251 log::Level::Info,
252 log::Level::Debug,
253 log::Level::Trace,
254 ] {
255 let record = log::RecordBuilder::new()
256 .args(format_args!("level test"))
257 .level(*level)
258 .target("level_test")
259 .build();
260 log::Log::log(&logger, &record);
261 }
262 }
263
264 #[test]
265 fn test_rlg_logger_flush() {
266 let logger = RlgLogger::new(LogFormat::JSON);
267 log::Log::flush(&logger); }
269}