use std::sync::Arc;
use std::time::{Duration, Instant};
use crate::metrics::ServiceMetrics;
#[derive(Debug)]
pub struct TimingGuard {
metrics: Arc<ServiceMetrics>,
service_name: String,
method: String,
start: Instant,
armed: bool,
}
impl TimingGuard {
pub fn new(
metrics: Arc<ServiceMetrics>,
service_name: impl Into<String>,
method: impl Into<String>,
) -> Self {
Self {
metrics,
service_name: service_name.into(),
method: method.into(),
start: Instant::now(),
armed: true,
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn record(mut self) -> Duration {
let duration = self.elapsed();
self.metrics
.request_duration
.with_label_values(&[&self.service_name, &self.method])
.observe(duration.as_secs_f64());
self.armed = false;
duration
}
}
impl Drop for TimingGuard {
fn drop(&mut self) {
if !self.armed {
return;
}
let duration = self.elapsed();
self.metrics
.request_duration
.with_label_values(&[&self.service_name, &self.method])
.observe(duration.as_secs_f64());
}
}
pub fn start_timing(
metrics: Arc<ServiceMetrics>,
service_name: impl Into<String>,
method: impl Into<String>,
) -> TimingGuard {
TimingGuard::new(metrics, service_name, method)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timing_guard_elapsed() {
let metrics = Arc::new(ServiceMetrics::new("test-service"));
let guard = TimingGuard::new(metrics, "test-service", "test-method");
std::thread::sleep(Duration::from_millis(10));
assert!(guard.elapsed() >= Duration::from_millis(10));
}
#[test]
fn test_timing_guard_record() {
let metrics = Arc::new(ServiceMetrics::new("test-service"));
let guard = TimingGuard::new(metrics, "test-service", "test-method");
let duration = guard.record();
assert!(duration >= Duration::ZERO);
}
#[test]
fn test_start_timing() {
let metrics = Arc::new(ServiceMetrics::new("test-service"));
let guard = start_timing(metrics, "test-service", "test-method");
assert!(guard.elapsed() >= Duration::ZERO);
}
}