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