use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Counter {
value: Arc<AtomicU64>,
}
impl Counter {
pub fn new() -> Self {
Self {
value: Arc::new(AtomicU64::new(0)),
}
}
pub fn inc(&self) {
self.value.fetch_add(1, Ordering::Relaxed);
}
pub fn add(&self, n: u64) {
self.value.fetch_add(n, Ordering::Relaxed);
}
pub fn get(&self) -> u64 {
self.value.load(Ordering::Relaxed)
}
pub fn reset(&self) {
self.value.store(0, Ordering::Relaxed);
}
}
impl Default for Counter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Gauge {
value: Arc<AtomicU64>,
}
impl Gauge {
pub fn new() -> Self {
Self {
value: Arc::new(AtomicU64::new(0)),
}
}
pub fn set(&self, value: u64) {
self.value.store(value, Ordering::Relaxed);
}
pub fn inc(&self) {
self.value.fetch_add(1, Ordering::Relaxed);
}
pub fn dec(&self) {
self.value.fetch_sub(1, Ordering::Relaxed);
}
pub fn add(&self, n: u64) {
self.value.fetch_add(n, Ordering::Relaxed);
}
pub fn sub(&self, n: u64) {
self.value.fetch_sub(n, Ordering::Relaxed);
}
pub fn get(&self) -> u64 {
self.value.load(Ordering::Relaxed)
}
}
impl Default for Gauge {
fn default() -> Self {
Self::new()
}
}
pub struct Histogram {
inner: hdrhistogram::Histogram<u64>,
}
impl Histogram {
pub fn new(max_value: u64, significant_figures: u8) -> Result<Self, String> {
hdrhistogram::Histogram::new_with_max(max_value, significant_figures)
.map(|inner| Self { inner })
.map_err(|e| format!("Failed to create histogram: {}", e))
}
pub fn for_latency() -> Result<Self, String> {
Self::new(60_000_000, 3) }
pub fn for_duration() -> Result<Self, String> {
Self::new(10_000_000, 3) }
pub fn record(&mut self, value: u64) {
let _ = self.inner.record(value);
}
pub fn count(&self) -> u64 {
self.inner.len()
}
pub fn min(&self) -> u64 {
self.inner.min()
}
pub fn max(&self) -> u64 {
self.inner.max()
}
pub fn mean(&self) -> f64 {
self.inner.mean()
}
pub fn value_at_percentile(&self, percentile: f64) -> u64 {
self.inner.value_at_percentile(percentile)
}
pub fn p50(&self) -> u64 {
self.value_at_percentile(50.0)
}
pub fn p95(&self) -> u64 {
self.value_at_percentile(95.0)
}
pub fn p99(&self) -> u64 {
self.value_at_percentile(99.0)
}
pub fn reset(&mut self) {
self.inner.reset();
}
}
impl std::fmt::Debug for Histogram {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Histogram")
.field("count", &self.count())
.field("min", &self.min())
.field("max", &self.max())
.field("mean", &self.mean())
.field("p50", &self.p50())
.field("p95", &self.p95())
.field("p99", &self.p99())
.finish()
}
}
fn now_micros() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_micros() as u64
}
#[derive(Debug, Clone)]
pub struct RateCalculator {
last_value: Arc<AtomicU64>,
last_timestamp: Arc<AtomicU64>,
}
impl RateCalculator {
pub fn new() -> Self {
Self {
last_value: Arc::new(AtomicU64::new(0)),
last_timestamp: Arc::new(AtomicU64::new(now_micros())),
}
}
pub fn calculate(&self, current_value: u64) -> Option<f64> {
let now = now_micros();
let last_value = self.last_value.load(Ordering::Relaxed);
let last_timestamp = self.last_timestamp.load(Ordering::Relaxed);
let elapsed_us = now.saturating_sub(last_timestamp);
if elapsed_us < 100_000 {
return None;
}
let value_delta = current_value.saturating_sub(last_value);
let elapsed_sec = elapsed_us as f64 / 1_000_000.0;
let rate = value_delta as f64 / elapsed_sec;
self.last_value.store(current_value, Ordering::Relaxed);
self.last_timestamp.store(now, Ordering::Relaxed);
Some(rate)
}
pub fn reset(&self) {
self.last_value.store(0, Ordering::Relaxed);
self.last_timestamp.store(now_micros(), Ordering::Relaxed);
}
}
impl Default for RateCalculator {
fn default() -> Self {
Self::new()
}
}