1use std::collections::VecDeque;
2use std::sync::Mutex;
3
4#[derive(Debug, Clone)]
6pub struct LogEntry {
7 pub timestamp: u64,
8 pub level: String,
9 pub target: String,
10 pub message: String,
11}
12
13pub struct LoggerBuffer {
18 buffer: Mutex<VecDeque<LogEntry>>,
19 max_entries: usize,
20 inner: Option<Box<dyn log::Log + Send + 'static>>,
21}
22
23impl LoggerBuffer {
24 pub fn new(max_entries: usize) -> Self {
25 Self {
26 buffer: Mutex::new(VecDeque::with_capacity(max_entries)),
27 max_entries,
28 inner: None,
29 }
30 }
31
32 pub fn set_inner(&mut self, inner: Box<dyn log::Log + Send + 'static>) {
35 self.inner = Some(inner);
36 }
37
38 pub fn drain(&self) -> Vec<LogEntry> {
40 let mut buf = self.buffer.lock().expect("log buffer poisoned");
41 buf.drain(..).collect()
42 }
43
44 pub fn snapshot(&self) -> Vec<LogEntry> {
46 let buf = self.buffer.lock().expect("log buffer poisoned");
47 buf.iter().cloned().collect()
48 }
49}
50
51impl log::Log for LoggerBuffer {
52 fn enabled(&self, metadata: &log::Metadata) -> bool {
53 self.inner
54 .as_ref()
55 .is_none_or(|inner| inner.enabled(metadata))
56 }
57
58 fn log(&self, record: &log::Record) {
59 if let Some(ref inner) = self.inner {
60 inner.log(record);
61 }
62 let entry = LogEntry {
63 timestamp: std::time::SystemTime::now()
64 .duration_since(std::time::UNIX_EPOCH)
65 .unwrap_or_default()
66 .as_millis() as u64,
67 level: record.level().to_string(),
68 target: record.target().to_string(),
69 message: record.args().to_string(),
70 };
71 let mut buf = self.buffer.lock().expect("log buffer poisoned");
72 if buf.len() >= self.max_entries {
73 buf.pop_front();
74 }
75 buf.push_back(entry);
76 }
77
78 fn flush(&self) {
79 if let Some(ref inner) = self.inner {
80 inner.flush();
81 }
82 }
83}