use std::sync::Arc;
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
use super::{HistogramBucketSnapshot, HistogramSnapshot, HistogramValue};
#[derive(Debug)]
pub(super) struct CounterMetric {
value: AtomicU64,
}
impl CounterMetric {
pub(super) const fn new() -> Self {
Self {
value: AtomicU64::new(0),
}
}
pub(super) fn snapshot(&self) -> u64 {
self.value.load(Ordering::Relaxed)
}
}
#[derive(Debug)]
pub(super) struct GaugeMetric {
value: AtomicI64,
}
impl GaugeMetric {
pub(super) const fn new() -> Self {
Self {
value: AtomicI64::new(0),
}
}
pub(super) fn snapshot(&self) -> i64 {
self.value.load(Ordering::Relaxed)
}
}
#[derive(Debug)]
pub(super) struct HistogramMetric {
boundaries: Vec<f64>,
counts: Vec<AtomicU64>,
sum_bits: AtomicU64,
}
impl HistogramMetric {
pub(super) fn new(boundaries: Vec<f64>) -> Self {
let counts_len = boundaries.len().saturating_add(1);
let counts = (0..counts_len).map(|_| AtomicU64::new(0)).collect();
Self {
boundaries,
counts,
sum_bits: AtomicU64::new(0.0_f64.to_bits()),
}
}
pub(super) fn boundaries(&self) -> &[f64] {
&self.boundaries
}
pub(super) fn snapshot(&self) -> HistogramSnapshot {
let buckets = self
.counts
.iter()
.enumerate()
.map(|(index, count)| HistogramBucketSnapshot {
upper_bound: self.boundaries.get(index).copied(),
count: count.load(Ordering::Relaxed),
})
.collect();
let sum = f64::from_bits(self.sum_bits.load(Ordering::Relaxed));
HistogramSnapshot { buckets, sum }
}
fn bucket_index(&self, value: f64) -> usize {
for (index, boundary) in self.boundaries.iter().enumerate() {
if value <= *boundary {
return index;
}
}
self.boundaries.len()
}
}
#[derive(Clone, Debug)]
pub struct CounterHandle {
pub(super) metric: Arc<CounterMetric>,
}
impl CounterHandle {
#[must_use]
pub(super) fn noop() -> Self {
let metric = Arc::new(CounterMetric::new());
Self { metric }
}
pub fn increment(&self) {
self.increment_by(1);
}
pub fn increment_by(&self, amount: u64) {
let _ = self
.metric
.value
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
Some(current.saturating_add(amount))
});
}
#[must_use]
pub fn value(&self) -> u64 {
self.metric.value.load(Ordering::Relaxed)
}
}
#[derive(Clone, Debug)]
pub struct GaugeHandle {
pub(super) metric: Arc<GaugeMetric>,
}
impl GaugeHandle {
#[must_use]
pub(super) fn noop() -> Self {
let metric = Arc::new(GaugeMetric::new());
Self { metric }
}
pub fn set(&self, value: i64) {
self.metric.value.store(value, Ordering::Relaxed);
}
pub fn increment(&self) {
self.increment_by(1);
}
pub fn increment_by(&self, amount: i64) {
self.metric.value.fetch_add(amount, Ordering::Relaxed);
}
pub fn decrement(&self) {
self.decrement_by(1);
}
pub fn decrement_by(&self, amount: i64) {
self.metric.value.fetch_sub(amount, Ordering::Relaxed);
}
#[must_use]
pub fn value(&self) -> i64 {
self.metric.value.load(Ordering::Relaxed)
}
}
#[derive(Clone, Debug)]
pub struct HistogramHandle {
pub(super) metric: Arc<HistogramMetric>,
}
impl HistogramHandle {
#[must_use]
pub(super) fn noop(boundaries: Vec<f64>) -> Self {
let metric = Arc::new(HistogramMetric::new(boundaries));
Self { metric }
}
pub fn observe<Value>(&self, value: Value)
where
Value: HistogramValue,
{
let value = value.into_f64();
if !value.is_finite() {
return;
}
let bucket_index = self.metric.bucket_index(value);
if let Some(count) = self.metric.counts.get(bucket_index) {
let _ = self.metric.sum_bits.fetch_update(
Ordering::Relaxed,
Ordering::Relaxed,
|current| Some((f64::from_bits(current) + value).to_bits()),
);
count.fetch_add(1, Ordering::Relaxed);
}
}
#[must_use]
pub fn boundaries(&self) -> &[f64] {
self.metric.boundaries()
}
}