Skip to content

Commit c17afdd

Browse files
committed
periodic stats log every 60s at info level
Tracks relay_calls, failures, bytes, coalesced requests, cache hit rate, and active scripts (total minus blacklisted). Logs only if there's been traffic since the last tick. Visible when running with RUST_LOG=info or log_level=info in config.
1 parent b3e0de5 commit c17afdd

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

src/domain_fronter.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ pub struct DomainFronter {
7373
inflight: Arc<Mutex<HashMap<String, broadcast::Sender<Vec<u8>>>>>,
7474
coalesced: AtomicU64,
7575
blacklist: Arc<std::sync::Mutex<HashMap<String, Instant>>>,
76+
relay_calls: AtomicU64,
77+
relay_failures: AtomicU64,
78+
bytes_relayed: AtomicU64,
7679
}
7780

7881
const BLACKLIST_COOLDOWN_SECS: u64 = 600;
@@ -138,9 +141,27 @@ impl DomainFronter {
138141
inflight: Arc::new(Mutex::new(HashMap::new())),
139142
coalesced: AtomicU64::new(0),
140143
blacklist: Arc::new(std::sync::Mutex::new(HashMap::new())),
144+
relay_calls: AtomicU64::new(0),
145+
relay_failures: AtomicU64::new(0),
146+
bytes_relayed: AtomicU64::new(0),
141147
})
142148
}
143149

150+
pub fn snapshot_stats(&self) -> StatsSnapshot {
151+
let bl = self.blacklist.lock().unwrap();
152+
StatsSnapshot {
153+
relay_calls: self.relay_calls.load(Ordering::Relaxed),
154+
relay_failures: self.relay_failures.load(Ordering::Relaxed),
155+
coalesced: self.coalesced.load(Ordering::Relaxed),
156+
bytes_relayed: self.bytes_relayed.load(Ordering::Relaxed),
157+
cache_hits: self.cache.hits(),
158+
cache_misses: self.cache.misses(),
159+
cache_bytes: self.cache.size(),
160+
blacklisted_scripts: bl.len(),
161+
total_scripts: self.script_ids.len(),
162+
}
163+
}
164+
144165
pub fn cache(&self) -> &ResponseCache {
145166
&self.cache
146167
}
@@ -287,6 +308,7 @@ impl DomainFronter {
287308
body: &[u8],
288309
cache_key_opt: Option<&str>,
289310
) -> Vec<u8> {
311+
self.relay_calls.fetch_add(1, Ordering::Relaxed);
290312
let bytes = match timeout(
291313
Duration::from_secs(REQUEST_TIMEOUT_SECS),
292314
self.do_relay_with_retry(method, url, headers, body),
@@ -295,14 +317,17 @@ impl DomainFronter {
295317
{
296318
Ok(Ok(bytes)) => bytes,
297319
Ok(Err(e)) => {
320+
self.relay_failures.fetch_add(1, Ordering::Relaxed);
298321
tracing::error!("Relay failed: {}", e);
299322
return error_response(502, &format!("Relay error: {}", e));
300323
}
301324
Err(_) => {
325+
self.relay_failures.fetch_add(1, Ordering::Relaxed);
302326
tracing::error!("Relay timeout");
303327
return error_response(504, "Relay timeout");
304328
}
305329
};
330+
self.bytes_relayed.fetch_add(bytes.len() as u64, Ordering::Relaxed);
306331

307332
if let Some(k) = cache_key_opt {
308333
if let Some(ttl) = parse_ttl(&bytes, url) {
@@ -767,6 +792,46 @@ fn parse_relay_json(body: &[u8]) -> Result<Vec<u8>, FronterError> {
767792
Ok(out)
768793
}
769794

795+
#[derive(Debug, Clone, Copy)]
796+
pub struct StatsSnapshot {
797+
pub relay_calls: u64,
798+
pub relay_failures: u64,
799+
pub coalesced: u64,
800+
pub bytes_relayed: u64,
801+
pub cache_hits: u64,
802+
pub cache_misses: u64,
803+
pub cache_bytes: usize,
804+
pub blacklisted_scripts: usize,
805+
pub total_scripts: usize,
806+
}
807+
808+
impl StatsSnapshot {
809+
pub fn hit_rate(&self) -> f64 {
810+
let total = self.cache_hits + self.cache_misses;
811+
if total == 0 {
812+
0.0
813+
} else {
814+
(self.cache_hits as f64 / total as f64) * 100.0
815+
}
816+
}
817+
818+
pub fn fmt_line(&self) -> String {
819+
format!(
820+
"stats: relay={} ({}KB) failures={} coalesced={} cache={}/{} ({:.0}% hit, {}KB) scripts={}/{} active",
821+
self.relay_calls,
822+
self.bytes_relayed / 1024,
823+
self.relay_failures,
824+
self.coalesced,
825+
self.cache_hits,
826+
self.cache_hits + self.cache_misses,
827+
self.hit_rate(),
828+
self.cache_bytes / 1024,
829+
self.total_scripts - self.blacklisted_scripts,
830+
self.total_scripts,
831+
)
832+
}
833+
}
834+
770835
fn should_blacklist(status: u16, body: &str) -> bool {
771836
if status == 429 || status == 403 {
772837
return true;

src/proxy_server.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,21 @@ impl ProxyServer {
121121
addr
122122
);
123123

124+
// Periodic stats log (every 60s at info level).
125+
let stats_fronter = self.fronter.clone();
126+
tokio::spawn(async move {
127+
let mut ticker = tokio::time::interval(std::time::Duration::from_secs(60));
128+
ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
129+
ticker.tick().await; // drop the immediate first tick
130+
loop {
131+
ticker.tick().await;
132+
let s = stats_fronter.snapshot_stats();
133+
if s.relay_calls > 0 || s.cache_hits > 0 {
134+
tracing::info!("{}", s.fmt_line());
135+
}
136+
}
137+
});
138+
124139
loop {
125140
let (sock, peer) = match listener.accept().await {
126141
Ok(x) => x,

0 commit comments

Comments
 (0)