1use std::collections::VecDeque;
4use std::fmt::{self, Write as _};
5use std::sync::{Arc, Mutex};
6
7use tracing::field::{Field, Visit};
8use tracing::{Event, Level, Subscriber};
9use tracing_subscriber::layer::{Context, Layer};
10
11const KEPT: usize = 30;
13
14#[derive(Clone, Debug, Default)]
28#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
29pub struct RecentErrors(Arc<Mutex<VecDeque<String>>>);
30
31#[cfg_attr(feature = "live", ravenrs_live::expose)]
32impl RecentErrors {
33 pub fn lines(&self) -> Vec<String> {
36 let home = std::env::var("HOME").or_else(|_| std::env::var("USERPROFILE")).unwrap_or_default();
37 let Ok(lines) = self.0.lock() else {
38 return Vec::new();
39 };
40 lines
41 .iter()
42 .map(|line| if home.is_empty() { line.clone() } else { line.replace(&home, "~") })
43 .collect()
44 }
45}
46
47#[derive(Default)]
49struct Line {
50 message: String,
51 fields: String,
52}
53
54impl Visit for Line {
55 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
56 if field.name() == "message" {
57 let _ = write!(self.message, "{value:?}");
58 } else {
59 let _ = write!(self.fields, " {}={value:?}", field.name());
60 }
61 }
62
63 fn record_str(&mut self, field: &Field, value: &str) {
64 if field.name() == "message" {
65 self.message.push_str(value);
66 } else {
67 let _ = write!(self.fields, " {}={value}", field.name());
68 }
69 }
70}
71
72impl<S: Subscriber> Layer<S> for RecentErrors {
73 fn on_event(&self, event: &Event<'_>, _context: Context<'_, S>) {
74 if *event.metadata().level() > Level::WARN {
75 return;
76 }
77 let mut line = Line::default();
78 event.record(&mut line);
79 if let Ok(mut lines) = self.0.lock() {
81 if lines.len() == KEPT {
82 lines.pop_front();
83 }
84 lines.push_back(line.message + &line.fields);
85 }
86 }
87}