use std::collections::VecDeque;
use std::sync::Mutex;
#[derive(Debug, Clone)]
pub struct LogEntry {
pub timestamp: u64,
pub level: String,
pub target: String,
pub message: String,
}
pub struct LoggerBuffer {
buffer: Mutex<VecDeque<LogEntry>>,
max_entries: usize,
inner: Option<Box<dyn log::Log + Send + 'static>>,
}
impl LoggerBuffer {
pub fn new(max_entries: usize) -> Self {
Self {
buffer: Mutex::new(VecDeque::with_capacity(max_entries)),
max_entries,
inner: None,
}
}
pub fn set_inner(&mut self, inner: Box<dyn log::Log + Send + 'static>) {
self.inner = Some(inner);
}
pub fn drain(&self) -> Vec<LogEntry> {
let mut buf = self.buffer.lock().expect("log buffer poisoned");
buf.drain(..).collect()
}
pub fn snapshot(&self) -> Vec<LogEntry> {
let buf = self.buffer.lock().expect("log buffer poisoned");
buf.iter().cloned().collect()
}
}
impl log::Log for LoggerBuffer {
fn enabled(&self, metadata: &log::Metadata) -> bool {
self.inner
.as_ref()
.is_none_or(|inner| inner.enabled(metadata))
}
fn log(&self, record: &log::Record) {
if let Some(ref inner) = self.inner {
inner.log(record);
}
let entry = LogEntry {
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64,
level: record.level().to_string(),
target: record.target().to_string(),
message: record.args().to_string(),
};
let mut buf = self.buffer.lock().expect("log buffer poisoned");
if buf.len() >= self.max_entries {
buf.pop_front();
}
buf.push_back(entry);
}
fn flush(&self) {
if let Some(ref inner) = self.inner {
inner.flush();
}
}
}