Skip to main content

rust_serv/metrics/
gauge.rs

1//! Gauge metric - value that can go up or down
2
3use std::sync::atomic::{AtomicI64, Ordering};
4
5/// A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
6#[derive(Debug)]
7pub struct Gauge {
8    value: AtomicI64,
9    name: String,
10    help: String,
11}
12
13impl Gauge {
14    /// Create a new gauge with name and help text
15    pub fn new(name: impl Into<String>, help: impl Into<String>) -> Self {
16        Self {
17            value: AtomicI64::new(0),
18            name: name.into(),
19            help: help.into(),
20        }
21    }
22
23    /// Set the gauge to a specific value
24    pub fn set(&self, value: i64) {
25        self.value.store(value, Ordering::Relaxed);
26    }
27
28    /// Increment the gauge by 1
29    pub fn inc(&self) {
30        self.value.fetch_add(1, Ordering::Relaxed);
31    }
32
33    /// Decrement the gauge by 1
34    pub fn dec(&self) {
35        self.value.fetch_sub(1, Ordering::Relaxed);
36    }
37
38    /// Add a value to the gauge
39    pub fn add(&self, delta: i64) {
40        self.value.fetch_add(delta, Ordering::Relaxed);
41    }
42
43    /// Subtract a value from the gauge
44    pub fn sub(&self, delta: i64) {
45        self.value.fetch_sub(delta, Ordering::Relaxed);
46    }
47
48    /// Get the current value
49    pub fn get(&self) -> i64 {
50        self.value.load(Ordering::Relaxed)
51    }
52
53    /// Get the gauge name
54    pub fn name(&self) -> &str {
55        &self.name
56    }
57
58    /// Get the help text
59    pub fn help(&self) -> &str {
60        &self.help
61    }
62}
63
64impl Clone for Gauge {
65    fn clone(&self) -> Self {
66        Self {
67            value: AtomicI64::new(self.value.load(Ordering::Relaxed)),
68            name: self.name.clone(),
69            help: self.help.clone(),
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_gauge_creation() {
80        let gauge = Gauge::new("active_connections", "Number of active connections");
81        assert_eq!(gauge.get(), 0);
82        assert_eq!(gauge.name(), "active_connections");
83        assert_eq!(gauge.help(), "Number of active connections");
84    }
85
86    #[test]
87    fn test_gauge_set() {
88        let gauge = Gauge::new("test", "test gauge");
89        gauge.set(42);
90        assert_eq!(gauge.get(), 42);
91        
92        gauge.set(100);
93        assert_eq!(gauge.get(), 100);
94    }
95
96    #[test]
97    fn test_gauge_inc_dec() {
98        let gauge = Gauge::new("test", "test gauge");
99        gauge.inc();
100        assert_eq!(gauge.get(), 1);
101        
102        gauge.inc();
103        assert_eq!(gauge.get(), 2);
104        
105        gauge.dec();
106        assert_eq!(gauge.get(), 1);
107    }
108
109    #[test]
110    fn test_gauge_add_sub() {
111        let gauge = Gauge::new("test", "test gauge");
112        gauge.add(10);
113        assert_eq!(gauge.get(), 10);
114        
115        gauge.sub(3);
116        assert_eq!(gauge.get(), 7);
117    }
118
119    #[test]
120    fn test_gauge_negative_values() {
121        let gauge = Gauge::new("test", "test gauge");
122        gauge.set(-5);
123        assert_eq!(gauge.get(), -5);
124        
125        gauge.add(10);
126        assert_eq!(gauge.get(), 5);
127        
128        gauge.sub(20);
129        assert_eq!(gauge.get(), -15);
130    }
131
132    #[test]
133    fn test_gauge_clone() {
134        let gauge = Gauge::new("test", "test gauge");
135        gauge.set(50);
136        
137        let cloned = gauge.clone();
138        assert_eq!(cloned.get(), 50);
139        assert_eq!(cloned.name(), "test");
140    }
141
142    #[test]
143    fn test_gauge_concurrent_access() {
144        use std::sync::Arc;
145        use std::thread;
146
147        let gauge = Arc::new(Gauge::new("test", "test gauge"));
148        let mut handles = vec![];
149
150        for _ in 0..5 {
151            let gauge_clone = Arc::clone(&gauge);
152            handles.push(thread::spawn(move || {
153                gauge_clone.inc();
154            }));
155        }
156
157        for _ in 0..3 {
158            let gauge_clone = Arc::clone(&gauge);
159            handles.push(thread::spawn(move || {
160                gauge_clone.dec();
161            }));
162        }
163
164        for handle in handles {
165            handle.join().unwrap();
166        }
167
168        // 5 inc - 3 dec = 2
169        assert_eq!(gauge.get(), 2);
170    }
171}