datasynth_server/observability/
metrics.rs1use std::sync::atomic::{AtomicU64, Ordering};
8use std::sync::Arc;
9use std::time::Instant;
10
11#[derive(Clone)]
17pub struct ServerMetrics {
18 pub entries_total: Arc<AtomicU64>,
20 pub errors_total: Arc<AtomicU64>,
22 pub active_sessions: Arc<AtomicU64>,
24}
25
26impl Default for ServerMetrics {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl ServerMetrics {
33 pub fn new() -> Self {
34 Self {
35 entries_total: Arc::new(AtomicU64::new(0)),
36 errors_total: Arc::new(AtomicU64::new(0)),
37 active_sessions: Arc::new(AtomicU64::new(0)),
38 }
39 }
40
41 pub fn record_entries(&self, count: u64) {
43 self.entries_total.fetch_add(count, Ordering::Relaxed);
44 }
45
46 pub fn record_error(&self) {
48 self.errors_total.fetch_add(1, Ordering::Relaxed);
49 }
50
51 pub fn session_started(&self) {
53 self.active_sessions.fetch_add(1, Ordering::Relaxed);
54 }
55
56 pub fn session_ended(&self) {
58 self.active_sessions.fetch_sub(1, Ordering::Relaxed);
59 }
60}
61
62pub struct DurationTimer {
64 start: Instant,
65 label: String,
66}
67
68impl DurationTimer {
69 pub fn new(label: impl Into<String>) -> Self {
70 Self {
71 start: Instant::now(),
72 label: label.into(),
73 }
74 }
75
76 pub fn finish(self) -> u64 {
78 let duration_ms = self.start.elapsed().as_millis() as u64;
79 tracing::debug!(
80 label = %self.label,
81 duration_ms = duration_ms,
82 "Operation completed"
83 );
84 duration_ms
85 }
86}
87
88#[cfg(test)]
89#[allow(clippy::unwrap_used)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_server_metrics_default() {
95 let metrics = ServerMetrics::new();
96 assert_eq!(metrics.entries_total.load(Ordering::Relaxed), 0);
97 assert_eq!(metrics.errors_total.load(Ordering::Relaxed), 0);
98 assert_eq!(metrics.active_sessions.load(Ordering::Relaxed), 0);
99 }
100
101 #[test]
102 fn test_record_entries() {
103 let metrics = ServerMetrics::new();
104 metrics.record_entries(100);
105 metrics.record_entries(50);
106 assert_eq!(metrics.entries_total.load(Ordering::Relaxed), 150);
107 }
108
109 #[test]
110 fn test_record_errors() {
111 let metrics = ServerMetrics::new();
112 metrics.record_error();
113 metrics.record_error();
114 assert_eq!(metrics.errors_total.load(Ordering::Relaxed), 2);
115 }
116
117 #[test]
118 fn test_session_tracking() {
119 let metrics = ServerMetrics::new();
120 metrics.session_started();
121 metrics.session_started();
122 assert_eq!(metrics.active_sessions.load(Ordering::Relaxed), 2);
123 metrics.session_ended();
124 assert_eq!(metrics.active_sessions.load(Ordering::Relaxed), 1);
125 }
126
127 #[test]
128 fn test_duration_timer() {
129 let timer = DurationTimer::new("test_op");
130 std::thread::sleep(std::time::Duration::from_millis(10));
131 let duration = timer.finish();
132 assert!(duration >= 10);
133 }
134}