lean_ctx/core/
proxy_metrics.rs1use std::sync::atomic::{AtomicU64, Ordering};
6
7use serde::{Deserialize, Serialize};
8
9static REQUESTS_TOTAL: AtomicU64 = AtomicU64::new(0);
10static TOKENS_SAVED_TOTAL: AtomicU64 = AtomicU64::new(0);
11static BYTES_COMPRESSED: AtomicU64 = AtomicU64::new(0);
12
13const PROXY_METRICS_FILE: &str = "proxy_metrics.json";
18
19const PERSIST_EVERY: u64 = 4;
23
24pub fn record_request(tokens_saved: u64, bytes_compressed: u64) {
25 let n = REQUESTS_TOTAL.fetch_add(1, Ordering::Relaxed) + 1;
26 TOKENS_SAVED_TOTAL.fetch_add(tokens_saved, Ordering::Relaxed);
27 BYTES_COMPRESSED.fetch_add(bytes_compressed, Ordering::Relaxed);
28 if n == 1 || n.is_multiple_of(PERSIST_EVERY) {
29 persist();
30 }
31}
32
33#[derive(Debug, Clone, Default, Serialize, Deserialize)]
34pub struct ProxyMetrics {
35 pub requests_total: u64,
36 pub tokens_saved_total: u64,
37 pub bytes_compressed: u64,
38}
39
40pub fn snapshot() -> ProxyMetrics {
41 ProxyMetrics {
42 requests_total: REQUESTS_TOTAL.load(Ordering::Relaxed),
43 tokens_saved_total: TOKENS_SAVED_TOTAL.load(Ordering::Relaxed),
44 bytes_compressed: BYTES_COMPRESSED.load(Ordering::Relaxed),
45 }
46}
47
48fn metrics_path() -> Option<std::path::PathBuf> {
49 crate::core::data_dir::lean_ctx_data_dir()
50 .ok()
51 .map(|d| d.join(PROXY_METRICS_FILE))
52}
53
54pub fn persist() {
58 let Some(path) = metrics_path() else {
59 return;
60 };
61 let Ok(json) = serde_json::to_string(&snapshot()) else {
62 return;
63 };
64 let tmp = path.with_extension("json.tmp");
65 if std::fs::write(&tmp, json).is_ok() {
66 let _ = std::fs::rename(&tmp, &path);
67 }
68}
69
70pub fn load_persisted() -> Option<ProxyMetrics> {
73 let path = metrics_path()?;
74 let data = std::fs::read_to_string(path).ok()?;
75 serde_json::from_str(&data).ok()
76}