1use std::collections::VecDeque;
11use std::sync::{Arc, Mutex};
12use std::time::SystemTime;
13
14use log::{Level, LevelFilter, Metadata, Record};
15use simplelog::{Config, SharedLogger};
16use tokio::sync::broadcast;
17
18#[derive(Clone, Debug)]
24pub struct LogEntry {
25 pub time: SystemTime,
26 pub level: Level,
27 pub target: String,
29 pub message: String,
30}
31
32pub struct LogBuffer {
34 capacity: usize,
35 entries: VecDeque<LogEntry>,
36}
37
38impl LogBuffer {
39 fn new(capacity: usize) -> Self {
40 Self {
41 capacity,
42 entries: VecDeque::with_capacity(capacity),
43 }
44 }
45
46 fn push(&mut self, entry: LogEntry) {
47 if self.entries.len() == self.capacity {
48 self.entries.pop_front();
49 }
50 self.entries.push_back(entry);
51 }
52
53 pub fn snapshot(&self) -> Vec<LogEntry> {
55 self.entries.iter().cloned().collect()
56 }
57}
58
59pub struct LogState {
61 pub buffer: Mutex<LogBuffer>,
62 pub tx: broadcast::Sender<LogEntry>,
63}
64
65impl LogState {
66 fn new(capacity: usize) -> Arc<Self> {
67 let (tx, _) = broadcast::channel(4096);
68 Arc::new(Self {
69 buffer: Mutex::new(LogBuffer::new(capacity)),
70 tx,
71 })
72 }
73
74 pub fn subscribe(&self) -> broadcast::Receiver<LogEntry> {
76 self.tx.subscribe()
77 }
78}
79
80pub struct InMemoryLogger {
85 state: Arc<LogState>,
86 level: LevelFilter,
87}
88
89impl log::Log for InMemoryLogger {
90 fn enabled(&self, metadata: &Metadata) -> bool {
91 metadata.level() <= self.level
92 }
93
94 fn log(&self, record: &Record) {
95 if !self.enabled(record.metadata()) {
96 return;
97 }
98 let entry = LogEntry {
99 time: SystemTime::now(),
100 level: record.level(),
101 target: record.target().to_owned(),
102 message: record.args().to_string(),
103 };
104 if let Ok(mut buf) = self.state.buffer.lock() {
105 buf.push(entry.clone());
106 }
107 let _ = self.state.tx.send(entry);
109 }
110
111 fn flush(&self) {}
112}
113
114impl SharedLogger for InMemoryLogger {
115 fn level(&self) -> LevelFilter {
116 self.level
117 }
118
119 fn config(&self) -> Option<&Config> {
120 None
121 }
122
123 fn as_log(self: Box<Self>) -> Box<dyn log::Log> {
124 self
125 }
126}
127
128pub fn init_combined_logger(log_file: std::fs::File, level: LevelFilter) -> Arc<LogState> {
138 let state = LogState::new(10_000);
139
140 let file_logger = simplelog::WriteLogger::new(level, Config::default(), log_file);
141
142 let mem_logger = Box::new(InMemoryLogger {
143 state: state.clone(),
144 level,
145 });
146
147 simplelog::CombinedLogger::init(vec![file_logger, mem_logger])
148 .expect("logger already initialised");
149
150 state
151}