use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Default)]
pub struct Metrics {
pub messages_sent: AtomicU64,
pub messages_received: AtomicU64,
pub messages_dropped: AtomicU64,
pub connections_total: AtomicU64,
pub connections_active: AtomicU64,
pub reconnections: AtomicU64,
pub send_errors: AtomicU64,
}
impl Metrics {
pub fn new() -> Self {
Self::default()
}
pub fn snapshot(&self) -> MetricsSnapshot {
MetricsSnapshot {
messages_sent: self.messages_sent.load(Ordering::Relaxed),
messages_received: self.messages_received.load(Ordering::Relaxed),
messages_dropped: self.messages_dropped.load(Ordering::Relaxed),
connections_total: self.connections_total.load(Ordering::Relaxed),
connections_active: self.connections_active.load(Ordering::Relaxed),
reconnections: self.reconnections.load(Ordering::Relaxed),
send_errors: self.send_errors.load(Ordering::Relaxed),
}
}
#[inline]
pub fn inc_messages_sent(&self) {
self.messages_sent.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn inc_messages_received(&self) {
self.messages_received.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn inc_messages_dropped(&self) {
self.messages_dropped.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn inc_connections_total(&self) {
self.connections_total.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn inc_connections_active(&self) {
self.connections_active.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn dec_connections_active(&self) {
let _ = self
.connections_active
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
if v > 0 {
Some(v - 1)
} else {
None
}
});
}
#[inline]
pub fn inc_reconnections(&self) {
self.reconnections.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn inc_send_errors(&self) {
self.send_errors.fetch_add(1, Ordering::Relaxed);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetricsSnapshot {
pub messages_sent: u64,
pub messages_received: u64,
pub messages_dropped: u64,
pub connections_total: u64,
pub connections_active: u64,
pub reconnections: u64,
pub send_errors: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_default_zero() {
let m = Metrics::new();
let s = m.snapshot();
assert_eq!(s.messages_sent, 0);
assert_eq!(s.messages_received, 0);
assert_eq!(s.messages_dropped, 0);
assert_eq!(s.connections_total, 0);
assert_eq!(s.connections_active, 0);
assert_eq!(s.reconnections, 0);
assert_eq!(s.send_errors, 0);
}
#[test]
fn test_metrics_increment() {
let m = Metrics::new();
m.inc_messages_sent();
m.inc_messages_sent();
m.inc_messages_received();
m.inc_connections_total();
m.inc_connections_active();
m.inc_send_errors();
m.inc_reconnections();
m.inc_messages_dropped();
let s = m.snapshot();
assert_eq!(s.messages_sent, 2);
assert_eq!(s.messages_received, 1);
assert_eq!(s.messages_dropped, 1);
assert_eq!(s.connections_total, 1);
assert_eq!(s.connections_active, 1);
assert_eq!(s.reconnections, 1);
assert_eq!(s.send_errors, 1);
}
#[test]
fn test_metrics_dec_connections_active() {
let m = Metrics::new();
m.inc_connections_active();
m.inc_connections_active();
m.dec_connections_active();
assert_eq!(m.snapshot().connections_active, 1);
}
#[test]
fn test_snapshot_clone() {
let m = Metrics::new();
m.inc_messages_sent();
let s1 = m.snapshot();
let s2 = s1.clone();
assert_eq!(s1, s2);
}
}