use once_cell::sync::Lazy; use prometheus::{ register_counter, register_counter_vec, register_gauge, register_histogram_vec, Counter, CounterVec, Encoder, Gauge, HistogramVec, TextEncoder, }; use std::time::Instant; // --- Counters --- pub static GRPC_REQUESTS_TOTAL: Lazy = Lazy::new(|| { register_counter_vec!( "furumi_grpc_requests_total", "Total number of gRPC requests", &["method", "status"] ) .unwrap() }); pub static BYTES_READ_TOTAL: Lazy = Lazy::new(|| { register_counter!( "furumi_bytes_read_total", "Total number of bytes read from disk and streamed to clients" ) .unwrap() }); pub static FILE_OPEN_ERRORS_TOTAL: Lazy = Lazy::new(|| { register_counter!( "furumi_file_open_errors_total", "Total number of file open errors (not found, permission denied, etc.)" ) .unwrap() }); pub static AUTH_FAILURES_TOTAL: Lazy = Lazy::new(|| { register_counter!( "furumi_auth_failures_total", "Total number of authentication failures" ) .unwrap() }); // --- Histogram --- pub static GRPC_REQUEST_DURATION: Lazy = Lazy::new(|| { register_histogram_vec!( "furumi_grpc_request_duration_seconds", "Duration of gRPC requests in seconds", &["method"], vec![0.0005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0] ) .unwrap() }); // --- Gauges --- pub static ACTIVE_STREAMS: Lazy = Lazy::new(|| { register_gauge!( "furumi_active_streams", "Number of currently active streaming connections (ReadFile/ReadDir)" ) .unwrap() }); /// Helper to track and record a gRPC call's duration and status. pub struct RequestTimer { method: &'static str, start: Instant, } impl RequestTimer { pub fn new(method: &'static str) -> Self { GRPC_REQUESTS_TOTAL .with_label_values(&[method, "started"]) .inc(); Self { method, start: Instant::now(), } } pub fn finish_ok(self) { let elapsed = self.start.elapsed().as_secs_f64(); GRPC_REQUEST_DURATION .with_label_values(&[self.method]) .observe(elapsed); GRPC_REQUESTS_TOTAL .with_label_values(&[self.method, "ok"]) .inc(); } pub fn finish_err(self) { let elapsed = self.start.elapsed().as_secs_f64(); GRPC_REQUEST_DURATION .with_label_values(&[self.method]) .observe(elapsed); GRPC_REQUESTS_TOTAL .with_label_values(&[self.method, "error"]) .inc(); } } /// An RAII guard that increments the ACTIVE_STREAMS gauge when created /// and decrements it when dropped. This ensures streams are correctly counted /// even if they terminate abruptly. pub struct ActiveStreamGuard; impl ActiveStreamGuard { pub fn new() -> Self { ACTIVE_STREAMS.inc(); Self } } impl Drop for ActiveStreamGuard { fn drop(&mut self) { ACTIVE_STREAMS.dec(); } } /// Render all registered metrics in Prometheus text format. pub fn render_metrics() -> String { let encoder = TextEncoder::new(); let metric_families = prometheus::gather(); let mut buffer = Vec::new(); encoder.encode(&metric_families, &mut buffer).unwrap(); String::from_utf8(buffer).unwrap() }