use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MetricLabel {
pub key: String,
pub value: String,
}
impl MetricLabel {
pub fn new(key: &str, value: &str) -> Self {
Self {
key: key.to_string(),
value: value.to_string(),
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MetricKey {
pub name: String,
pub labels: Vec<MetricLabel>,
}
impl MetricKey {
pub fn new(name: &str, labels: Vec<MetricLabel>) -> Self {
Self {
name: name.to_string(),
labels,
}
}
}
pub trait Metric: Send + Sync {
fn as_any(&self) -> &dyn std::any::Any;
}
pub struct Counter {
value: std::sync::atomic::AtomicU64,
}
impl Default for Counter {
fn default() -> Self {
Self::new()
}
}
impl Counter {
pub fn new() -> Self {
Self {
value: std::sync::atomic::AtomicU64::new(0),
}
}
pub fn increment(&self, amount: u64) {
self.value
.fetch_add(amount, std::sync::atomic::Ordering::Relaxed);
}
pub fn get(&self) -> u64 {
self.value.load(std::sync::atomic::Ordering::Relaxed)
}
}
impl Metric for Counter {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub struct Gauge {
value: std::sync::atomic::AtomicI64,
}
impl Default for Gauge {
fn default() -> Self {
Self::new()
}
}
impl Gauge {
pub fn new() -> Self {
Self {
value: std::sync::atomic::AtomicI64::new(0),
}
}
pub fn set(&self, val: i64) {
self.value.store(val, std::sync::atomic::Ordering::Relaxed);
}
pub fn get(&self) -> i64 {
self.value.load(std::sync::atomic::Ordering::Relaxed)
}
}
impl Metric for Gauge {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
use std::sync::atomic::{AtomicU64, Ordering};
pub struct Histogram {
buckets: Vec<f64>,
counts: Vec<AtomicU64>,
sum: Mutex<f64>,
count: AtomicU64,
}
impl Histogram {
pub fn new(buckets: Vec<f64>) -> Self {
let len = buckets.len() + 1;
let mut counts = Vec::with_capacity(len);
for _ in 0..len {
counts.push(AtomicU64::new(0));
}
Self {
buckets,
counts,
sum: Mutex::new(0.0),
count: AtomicU64::new(0),
}
}
pub fn record(&self, value: f64) {
self.count.fetch_add(1, Ordering::Relaxed);
if let Ok(mut g) = self.sum.lock() {
*g += value;
}
let mut idx = self.buckets.len();
for (i, boundary) in self.buckets.iter().enumerate() {
if value <= *boundary {
idx = i;
break;
}
}
self.counts[idx].fetch_add(1, Ordering::Relaxed);
}
}
impl Metric for Histogram {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub struct MetricRegistry {
metrics: Mutex<HashMap<MetricKey, Arc<dyn Metric>>>,
}
impl Default for MetricRegistry {
fn default() -> Self {
Self::new()
}
}
impl MetricRegistry {
pub fn new() -> Self {
Self {
metrics: Mutex::new(HashMap::new()),
}
}
pub fn counter(&self, name: &str, labels: Vec<MetricLabel>) -> Arc<Counter> {
let _key = MetricKey::new(name, labels);
let _map = self.metrics.lock().unwrap();
todo!()
}
}
pub struct SimpleRegistry {
counters: Mutex<HashMap<MetricKey, Arc<Counter>>>,
gauges: Mutex<HashMap<MetricKey, Arc<Gauge>>>,
histograms: Mutex<HashMap<MetricKey, Arc<Histogram>>>,
}
impl Default for SimpleRegistry {
fn default() -> Self {
Self::new()
}
}
impl SimpleRegistry {
pub fn new() -> Self {
Self {
counters: Mutex::new(HashMap::new()),
gauges: Mutex::new(HashMap::new()),
histograms: Mutex::new(HashMap::new()),
}
}
pub fn get_counter(&self, name: &str, labels: Vec<MetricLabel>) -> Arc<Counter> {
let key = MetricKey::new(name, labels);
let mut map = self.counters.lock().unwrap();
map.entry(key)
.or_insert_with(|| Arc::new(Counter::new()))
.clone()
}
pub fn get_gauge(&self, name: &str, labels: Vec<MetricLabel>) -> Arc<Gauge> {
let key = MetricKey::new(name, labels);
let mut map = self.gauges.lock().unwrap();
map.entry(key)
.or_insert_with(|| Arc::new(Gauge::new()))
.clone()
}
pub fn get_histogram(
&self,
name: &str,
labels: Vec<MetricLabel>,
buckets: Vec<f64>,
) -> Arc<Histogram> {
let key = MetricKey::new(name, labels);
let mut map = self.histograms.lock().unwrap();
map.entry(key)
.or_insert_with(|| Arc::new(Histogram::new(buckets)))
.clone()
}
}
lazy_static::lazy_static! {
pub static ref REGISTRY: SimpleRegistry = SimpleRegistry::new();
}