1#![deny(warnings, clippy::all, clippy::pedantic, missing_docs)]
10#![forbid(unsafe_code)]
11
12use std::sync::{Arc, RwLock, Weak};
13
14use log::Log;
15
16#[derive(Debug)]
21pub struct LevelFilter<T> {
22 level: log::Level,
23 logger: T,
24}
25
26impl<T> LevelFilter<T> {
27 pub fn new(level: log::Level, logger: T) -> Self {
29 Self { level, logger }
30 }
31
32 pub fn level(&self) -> log::Level {
34 self.level
35 }
36
37 pub fn set_level(&mut self, level: log::Level) {
39 self.level = level;
40 }
41
42 fn level_passes(&self, metadata: &log::Metadata) -> bool {
43 metadata.level() <= self.level
44 }
45
46 pub fn inner(&self) -> &T {
48 &self.logger
49 }
50
51 pub fn set_inner(&mut self, logger: T) {
53 self.logger = logger;
54 }
55}
56
57impl<T: Log> log::Log for LevelFilter<T> {
58 fn enabled(&self, metadata: &log::Metadata) -> bool {
63 self.level_passes(metadata) && self.logger.enabled(metadata)
64 }
65
66 fn log(&self, record: &log::Record) {
68 if self.level_passes(record.metadata()) {
69 self.logger.log(record);
70 }
71 }
72
73 fn flush(&self) {
75 self.logger.flush();
76 }
77}
78
79#[derive(Debug)]
84pub struct ReloadLog<T> {
85 underlying: Arc<RwLock<T>>,
86}
87
88impl<T> ReloadLog<T> {
89 pub fn new(logger: T) -> Self {
91 Self {
92 underlying: Arc::new(RwLock::new(logger)),
93 }
94 }
95
96 #[must_use]
98 pub fn handle(&self) -> ReloadHandle<T> {
99 ReloadHandle {
100 underlying: Arc::downgrade(&self.underlying),
101 }
102 }
103}
104
105impl<T: Log> Log for ReloadLog<T> {
106 fn enabled(&self, metadata: &log::Metadata) -> bool {
112 self.underlying.read().is_ok_and(|l| l.enabled(metadata))
113 }
114
115 fn log(&self, record: &log::Record) {
121 let _ = self.underlying.read().map(|l| l.log(record));
123 }
124
125 fn flush(&self) {
131 let _ = self.underlying.read().map(|l| l.flush());
133 }
134}
135
136#[derive(Debug, Clone, Copy)]
138pub enum ReloadError {
139 Gone,
141 Poisoned,
147}
148
149impl std::fmt::Display for ReloadError {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 match self {
152 ReloadError::Gone => write!(f, "Referenced logger was dropped"),
153 ReloadError::Poisoned => write!(f, "Lock poisoned"),
154 }
155 }
156}
157
158impl std::error::Error for ReloadError {}
159
160#[derive(Debug, Clone)]
162pub struct ReloadHandle<T> {
163 underlying: Weak<RwLock<T>>,
164}
165
166impl<T> ReloadHandle<T> {
167 pub fn replace(&self, logger: T) -> Result<(), ReloadError> {
178 let lock = self.underlying.upgrade().ok_or(ReloadError::Gone)?;
179 let mut guard = lock.write().unwrap_or_else(|error| {
180 lock.clear_poison();
182 error.into_inner()
183 });
184 *guard = logger;
185 Ok(())
186 }
187
188 pub fn modify<F>(&self, f: F) -> Result<(), ReloadError>
203 where
204 F: FnOnce(&mut T),
205 {
206 let lock = self.underlying.upgrade().ok_or(ReloadError::Gone)?;
207 let mut guard = lock.write().map_err(|_| ReloadError::Poisoned)?;
210 f(&mut *guard);
211 Ok(())
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use crate::{LevelFilter, ReloadLog};
218 use log::{Log, Record};
219 use similar_asserts::assert_eq;
220 use std::sync::{Arc, Mutex};
221
222 struct CollectMessages {
223 messages: Mutex<Vec<String>>,
224 }
225
226 impl CollectMessages {
227 fn new() -> Self {
228 Self {
229 messages: Mutex::new(Vec::new()),
230 }
231 }
232 }
233
234 impl Log for CollectMessages {
235 fn enabled(&self, _metadata: &log::Metadata) -> bool {
236 true
237 }
238
239 fn log(&self, record: &log::Record) {
240 let mut guard = self.messages.try_lock().unwrap();
241 guard.push(format!("{}", record.args()));
242 }
243
244 fn flush(&self) {}
245 }
246
247 #[test]
248 fn sanity_check_log_level_ordering() {
249 use log::Level;
250
251 assert!(Level::Error <= Level::Warn);
252 assert!(Level::Warn <= Level::Warn);
253 assert!(Level::Debug >= Level::Warn);
254 }
255
256 #[test]
257 fn level_filter() {
258 let collect_logs = Arc::new(CollectMessages::new());
259 let mut filter = LevelFilter::new(log::Level::Warn, collect_logs.clone());
260
261 for level in log::Level::iter() {
262 filter.log(
263 &Record::builder()
264 .level(level)
265 .args(format_args!("{level}"))
266 .build(),
267 );
268 }
269 let mut messages = collect_logs.messages.try_lock().unwrap();
270 assert_eq!(*messages, vec!["ERROR", "WARN"]);
271 messages.clear();
272 drop(messages);
273
274 filter.set_level(log::Level::Debug);
275
276 for level in log::Level::iter() {
277 filter.log(
278 &Record::builder()
279 .level(level)
280 .args(format_args!("{level}"))
281 .build(),
282 );
283 }
284 let messages = collect_logs.messages.try_lock().unwrap();
285 assert_eq!(*messages, &["ERROR", "WARN", "INFO", "DEBUG"]);
286 }
287
288 #[test]
289 fn reloadlog_replace() {
290 let collect_logs_1 = Arc::new(CollectMessages::new());
291 let collect_logs_2 = Arc::new(CollectMessages::new());
292
293 let reload_log = ReloadLog::new(collect_logs_1.clone());
294 let reload_handle = reload_log.handle();
295
296 reload_log.log(&Record::builder().args(format_args!("Message 1")).build());
297
298 reload_handle.replace(collect_logs_2.clone()).unwrap();
299
300 reload_log.log(&Record::builder().args(format_args!("Message 2")).build());
301
302 let messages_1 = collect_logs_1.messages.try_lock().unwrap();
303 let messages_2 = collect_logs_2.messages.try_lock().unwrap();
304 assert_eq!(*messages_1, &["Message 1"]);
305 assert_eq!(*messages_2, &["Message 2"]);
306 }
307
308 #[test]
309 fn reloadlog_modify() {
310 let collect_logs = Arc::new(CollectMessages::new());
311
312 let reload_log = ReloadLog::new(collect_logs.clone());
313 let reload_handle = reload_log.handle();
314
315 reload_log.log(&Record::builder().args(format_args!("Message 1")).build());
316 let messages = collect_logs.messages.try_lock().unwrap();
317 assert_eq!(*messages, &["Message 1"]);
318 drop(messages);
319
320 reload_handle
322 .modify(|l| l.messages.try_lock().unwrap().clear())
323 .unwrap();
324
325 reload_log.log(&Record::builder().args(format_args!("Message 2")).build());
327 let messages = collect_logs.messages.try_lock().unwrap();
328 assert_eq!(*messages, &["Message 2"]);
329 }
330}