use std::time::{Duration, Instant};
use tracing::info;
#[derive(Debug, Clone)]
pub struct OperationMetrics {
pub operation_type: String,
pub bytes_processed: usize,
pub duration: Duration,
}
impl OperationMetrics {
pub fn new(operation_type: impl Into<String>, bytes_processed: usize, duration: Duration) -> Self {
Self {
operation_type: operation_type.into(),
bytes_processed,
duration,
}
}
pub fn log(&self) {
let throughput_mbps = if self.duration.as_secs_f64() > 0.0 {
(self.bytes_processed as f64 / (1024.0 * 1024.0)) / self.duration.as_secs_f64()
} else {
0.0
};
info!(
operation = %self.operation_type,
bytes = self.bytes_processed,
duration_ms = self.duration.as_millis(),
throughput_mbps = format!("{:.2}", throughput_mbps),
"operation completed"
);
}
pub fn throughput_mbps(&self) -> f64 {
if self.duration.as_secs_f64() > 0.0 {
(self.bytes_processed as f64 / (1024.0 * 1024.0)) / self.duration.as_secs_f64()
} else {
0.0
}
}
}
pub struct Timer {
start: Instant,
}
impl Timer {
pub fn start() -> Self {
Self {
start: Instant::now(),
}
}
pub fn stop(self) -> Duration {
self.start.elapsed()
}
pub fn stop_with_metrics(self, operation_type: impl Into<String>, bytes: usize) -> OperationMetrics {
OperationMetrics::new(operation_type, bytes, self.stop())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_throughput() {
let metrics = OperationMetrics::new(
"test_op",
1_000_000, Duration::from_millis(100),
);
let throughput = metrics.throughput_mbps();
assert!(throughput > 9.0 && throughput < 11.0); }
#[test]
fn test_timer() {
let timer = Timer::start();
std::thread::sleep(Duration::from_millis(10));
let duration = timer.stop();
assert!(duration.as_millis() >= 10);
}
}