#![deny(missing_docs)]
pub mod tracing;
use std::borrow::Cow;
use std::sync::Arc;
use parking_lot::Mutex;
mod counter;
mod gauge;
mod histogram;
mod timer;
pub use counter::*;
pub use gauge::*;
pub use histogram::*;
pub use timer::*;
#[derive(Clone, Debug)]
pub struct Label {
key: Cow<'static, str>,
value: Cow<'static, str>,
}
impl std::fmt::Display for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}={}", self.key, self.value)
}
}
impl<K, V> From<(K, V)> for Label
where
K: Into<Cow<'static, str>>,
V: Into<Cow<'static, str>>,
{
fn from(value: (K, V)) -> Self {
Label::new(value.0, value.1)
}
}
impl Label {
pub fn new(key: impl Into<Cow<'static, str>>, value: impl Into<Cow<'static, str>>) -> Self {
Self {
key: key.into(),
value: value.into(),
}
}
pub fn key(&self) -> &str {
&self.key
}
pub fn value(&self) -> &str {
&self.value
}
}
pub trait MetricsRegistry: Send + Sync {
fn register_counter(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Counter;
fn register_histogram(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Histogram;
fn register_timer(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Timer;
fn register_gauge(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Gauge;
fn snapshot(&self) -> Vec<Metric>;
}
pub struct MetricBuilder<'s> {
labels: Vec<Label>,
registry: &'s dyn MetricsRegistry,
}
impl<'r> MetricBuilder<'r> {
pub fn new(registry: &'r dyn MetricsRegistry) -> Self {
Self {
labels: vec![],
registry,
}
}
pub fn add_label<K, V>(mut self, key: K, value: V) -> Self
where
K: Into<Cow<'static, str>>,
V: Into<Cow<'static, str>>,
{
self.labels.push(Label::new(key, value));
self
}
pub fn add_labels<I, L>(mut self, labels: I) -> Self
where
I: IntoIterator<Item = L>,
L: Into<Label>,
{
self.labels.extend(labels.into_iter().map(|l| l.into()));
self
}
pub fn counter(self, name: impl Into<Cow<'static, str>>) -> Counter {
self.registry.register_counter(name.into(), self.labels)
}
pub fn histogram(self, name: impl Into<Cow<'static, str>>) -> Histogram {
self.registry.register_histogram(name.into(), self.labels)
}
pub fn timer(self, name: impl Into<Cow<'static, str>>) -> Timer {
self.registry.register_timer(name.into(), self.labels)
}
pub fn gauge(self, name: impl Into<Cow<'static, str>>) -> Gauge {
self.registry.register_gauge(name.into(), self.labels)
}
}
#[derive(Default, Clone)]
pub struct DefaultMetricsRegistry {
inner: Arc<Mutex<Vec<Metric>>>,
}
#[derive(Clone, Debug)]
pub enum MetricValue {
Counter(Counter),
Histogram(Histogram),
Timer(Timer),
Gauge(Gauge),
}
impl From<Counter> for MetricValue {
fn from(value: Counter) -> Self {
Self::Counter(value)
}
}
impl From<Histogram> for MetricValue {
fn from(value: Histogram) -> Self {
Self::Histogram(value)
}
}
impl From<Timer> for MetricValue {
fn from(value: Timer) -> Self {
Self::Timer(value)
}
}
impl From<Gauge> for MetricValue {
fn from(value: Gauge) -> Self {
Self::Gauge(value)
}
}
#[derive(Clone, Debug)]
pub struct Metric {
name: Cow<'static, str>,
labels: Vec<Label>,
value: MetricValue,
}
impl Metric {
pub fn name(&self) -> &Cow<'static, str> {
&self.name
}
pub fn labels(&self) -> &[Label] {
&self.labels
}
pub fn value(&self) -> &MetricValue {
&self.value
}
}
impl MetricsRegistry for DefaultMetricsRegistry {
fn register_counter(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Counter {
let counter = Counter::new();
let metric = Metric {
name,
labels,
value: counter.clone().into(),
};
self.inner.lock().push(metric);
counter
}
fn register_histogram(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Histogram {
let histogram = Histogram::new();
let metric = Metric {
name,
labels,
value: histogram.clone().into(),
};
self.inner.lock().push(metric);
histogram
}
fn register_timer(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Timer {
let timer = Timer::new();
let metric = Metric {
name,
labels,
value: timer.clone().into(),
};
self.inner.lock().push(metric);
timer
}
fn register_gauge(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Gauge {
let gauge = Gauge::new();
let metric = Metric {
name,
labels,
value: gauge.clone().into(),
};
self.inner.lock().push(metric);
gauge
}
fn snapshot(&self) -> Vec<Metric> {
self.inner.lock().clone()
}
}