1use std::collections::BTreeMap;
2use std::io::Write;
3use std::sync::Mutex;
4use std::sync::OnceLock;
5
6static METRICS: Metrics = Metrics::new();
7
8struct Metrics(OnceLock<MetricsInner>);
9
10struct MetricsInner(Mutex<(u64, BTreeMap<&'static str, u64>)>);
13
14pub struct MetricsHandle<'a>(&'a MetricsInner);
15
16pub fn handle() -> MetricsHandle<'static> {
17 let metrics = METRICS
18 .0
19 .get_or_init(|| MetricsInner(Mutex::new((0, BTreeMap::new()))));
20 let mut guard = metrics.0.lock().unwrap();
21 let (count, _map) = &mut *guard;
22 *count += 1;
23
24 MetricsHandle(metrics)
25}
26
27impl Metrics {
28 pub const fn new() -> Self {
29 Self(OnceLock::new())
30 }
31}
32
33impl MetricsHandle<'_> {
34 pub fn add(&self, name: &'static str, value: u64) {
35 let mut guard = self.0 .0.lock().unwrap();
36 let (_count, map) = &mut *guard;
37 map.insert(name, value);
38 }
39}
40
41impl Drop for MetricsHandle<'_> {
42 fn drop(&mut self) {
43 use std::fmt::Write;
44
45 let mut guard = self.0 .0.lock().unwrap();
46 let (count, map) = &mut *guard;
47 *count -= 1;
48 if *count == 0 {
49 let mut file = std::fs::OpenOptions::new()
50 .create(true)
51 .truncate(true)
52 .write(true)
53 .open("metrics.csv")
54 .unwrap();
55 let csv = map.iter_mut().fold(String::new(), |mut acc, (k, v)| {
56 writeln!(acc, "{k},{v}").unwrap();
57 acc
58 });
59 file.write_all(csv.as_bytes()).unwrap();
60 }
61 }
62}