use std::collections::VecDeque;
use std::io;
use std::sync::{Arc, Mutex, OnceLock};
const CAP: usize = 500;
fn ring() -> &'static Arc<Mutex<VecDeque<String>>> {
static RING: OnceLock<Arc<Mutex<VecDeque<String>>>> = OnceLock::new();
RING.get_or_init(|| Arc::new(Mutex::new(VecDeque::with_capacity(CAP))))
}
pub fn snapshot(limit: usize) -> Vec<String> {
let g = ring().lock().unwrap();
g.iter()
.skip(g.len().saturating_sub(limit))
.cloned()
.collect()
}
pub fn clear() {
ring().lock().unwrap().clear();
}
pub fn push_line(line: &str) {
let line = line.trim_end();
if line.is_empty() {
return;
}
let mut g = ring().lock().unwrap();
if g.len() >= CAP {
g.pop_front();
}
g.push_back(line.to_string());
}
#[derive(Clone, Default)]
pub struct RingMakeWriter;
pub struct RingWriter {
buf: String,
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for RingMakeWriter {
type Writer = RingWriter;
fn make_writer(&'a self) -> Self::Writer {
RingWriter { buf: String::new() }
}
}
impl io::Write for RingWriter {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.buf.push_str(&String::from_utf8_lossy(data));
while let Some(idx) = self.buf.find('\n') {
let line: String = self.buf.drain(..=idx).collect();
push_line(&line);
}
Ok(data.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Drop for RingWriter {
fn drop(&mut self) {
if !self.buf.is_empty() {
push_line(&self.buf.clone());
}
}
}