1use std::time::{Duration, Instant};
6use tracing::info;
7
8#[derive(Debug, Clone)]
10pub struct OperationMetrics {
11 pub operation_type: String,
12 pub bytes_processed: usize,
13 pub duration: Duration,
14}
15
16impl OperationMetrics {
17 pub fn new(operation_type: impl Into<String>, bytes_processed: usize, duration: Duration) -> Self {
19 Self {
20 operation_type: operation_type.into(),
21 bytes_processed,
22 duration,
23 }
24 }
25
26 pub fn log(&self) {
28 let throughput_mbps = if self.duration.as_secs_f64() > 0.0 {
29 (self.bytes_processed as f64 / (1024.0 * 1024.0)) / self.duration.as_secs_f64()
30 } else {
31 0.0
32 };
33
34 info!(
35 operation = %self.operation_type,
36 bytes = self.bytes_processed,
37 duration_ms = self.duration.as_millis(),
38 throughput_mbps = format!("{:.2}", throughput_mbps),
39 "operation completed"
40 );
41 }
42
43 pub fn throughput_mbps(&self) -> f64 {
45 if self.duration.as_secs_f64() > 0.0 {
46 (self.bytes_processed as f64 / (1024.0 * 1024.0)) / self.duration.as_secs_f64()
47 } else {
48 0.0
49 }
50 }
51}
52
53pub struct Timer {
55 start: Instant,
56}
57
58impl Timer {
59 pub fn start() -> Self {
61 Self {
62 start: Instant::now(),
63 }
64 }
65
66 pub fn stop(self) -> Duration {
68 self.start.elapsed()
69 }
70
71 pub fn stop_with_metrics(self, operation_type: impl Into<String>, bytes: usize) -> OperationMetrics {
73 OperationMetrics::new(operation_type, bytes, self.stop())
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_metrics_throughput() {
83 let metrics = OperationMetrics::new(
84 "test_op",
85 1_000_000, Duration::from_millis(100),
87 );
88
89 let throughput = metrics.throughput_mbps();
90 assert!(throughput > 9.0 && throughput < 11.0); }
92
93 #[test]
94 fn test_timer() {
95 let timer = Timer::start();
96 std::thread::sleep(Duration::from_millis(10));
97 let duration = timer.stop();
98
99 assert!(duration.as_millis() >= 10);
100 }
101}