use crate::guards::DeferredAddWithLabels;
use prometheus::{
register_histogram_vec, register_int_counter_vec, register_int_gauge_vec, HistogramTimer,
HistogramVec, IntCounterVec, IntGaugeVec,
};
use std::marker::PhantomData;
pub type LabelValues<'a> = Vec<&'a str>;
pub trait Labels {
fn label_names() -> Vec<&'static str>;
fn possible_label_values() -> Vec<LabelValues<'static>>;
fn label_values(&self) -> LabelValues;
}
pub struct IntCounterWithLabels<L: Labels> {
metric: IntCounterVec,
_labels: PhantomData<L>,
}
impl<L: Labels> IntCounterWithLabels<L> {
pub fn register_new(name: &str, help: &str) -> IntCounterWithLabels<L> {
let metric = register_int_counter_vec!(name, help, &L::label_names()).unwrap();
for vals in L::possible_label_values() {
metric.with_label_values(&vals).inc_by(0);
}
Self {
metric,
_labels: PhantomData,
}
}
pub fn get(&self, labels: &L) -> u64 {
self.metric.with_label_values(&labels.label_values()).get()
}
pub fn inc(&self, labels: &L) {
self.metric.with_label_values(&labels.label_values()).inc();
}
pub fn add(&self, v: u64, labels: &L) {
self.metric
.with_label_values(&labels.label_values())
.inc_by(v);
}
#[must_use]
pub fn deferred_inc<'a>(&'a self, labels: L) -> DeferredAddWithLabels<'a, L> {
DeferredAddWithLabels::new(self, 1, labels)
}
#[must_use]
pub fn deferred_add<'a>(&'a self, v: u64, labels: L) -> DeferredAddWithLabels<'a, L> {
DeferredAddWithLabels::new(self, v, labels)
}
}
pub struct IntGaugeWithLabels<L: Labels> {
metric: IntGaugeVec,
_labels: PhantomData<L>,
}
impl<L: Labels> IntGaugeWithLabels<L> {
pub fn register_new(name: &str, help: &str) -> IntGaugeWithLabels<L> {
let metric = register_int_gauge_vec!(name, help, &L::label_names()).unwrap();
Self {
metric,
_labels: PhantomData,
}
}
pub fn get(&self, labels: &L) -> i64 {
self.metric.with_label_values(&labels.label_values()).get()
}
pub fn set(&self, labels: &L, value: i64) {
self.metric
.with_label_values(&labels.label_values())
.set(value);
}
pub fn add(&self, labels: &L, value: i64) {
self.metric
.with_label_values(&labels.label_values())
.add(value);
}
pub fn sub(&self, labels: &L, value: i64) {
self.metric
.with_label_values(&labels.label_values())
.sub(value);
}
pub fn inc(&self, labels: &L) {
self.metric.with_label_values(&labels.label_values()).inc();
}
pub fn dec(&self, labels: &L) {
self.metric.with_label_values(&labels.label_values()).dec();
}
}
pub struct HistogramWithLabels<L: Labels> {
metric: HistogramVec,
_labels: PhantomData<L>,
}
impl<L: Labels> HistogramWithLabels<L> {
pub fn register_new(name: &str, help: &str) -> Self {
let metric = register_histogram_vec!(name, help, &L::label_names()).unwrap();
Self {
metric,
_labels: PhantomData,
}
}
pub fn register_new_with_buckets(name: &str, help: &str, buckets: Vec<f64>) -> Self {
let metric = register_histogram_vec!(name, help, &L::label_names(), buckets).unwrap();
Self {
metric,
_labels: PhantomData,
}
}
pub fn observe(&self, labels: &L, value: f64) {
self.metric
.with_label_values(&labels.label_values())
.observe(value);
}
pub fn start_timer(&self, labels: &L) -> HistogramTimer {
self.metric
.with_label_values(&labels.label_values())
.start_timer()
}
pub fn observe_closure_duration<F, T>(&self, labels: &L, f: F) -> T
where
F: FnOnce() -> T,
{
self.metric
.with_label_values(&labels.label_values())
.observe_closure_duration(f)
}
pub fn get_sample_sum(&self, labels: &L) -> f64 {
self.metric
.with_label_values(&labels.label_values())
.get_sample_sum()
}
pub fn get_sample_count(&self, labels: &L) -> u64 {
self.metric
.with_label_values(&labels.label_values())
.get_sample_count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use lazy_static::lazy_static;
use test_labels::*;
#[allow(dead_code)]
mod test_labels {
use crate::label_enum;
label_enum! {
pub enum Animal {
Bird,
Cat,
Dog,
}
}
label_enum! {
pub enum Size {
Small,
Large,
}
}
}
struct TestLabels {
animal: Animal,
size: Size,
}
impl Labels for TestLabels {
fn label_names() -> Vec<&'static str> {
vec!["animal", "size"]
}
fn possible_label_values() -> Vec<LabelValues<'static>> {
Default::default() }
fn label_values(&self) -> LabelValues {
let Self { animal, size } = self;
vec![animal.as_str(), size.as_str()]
}
}
#[test]
fn int_counter_with_labels_get_works() {
lazy_static! {
static ref COUNTER: IntCounterWithLabels<TestLabels> =
IntCounterWithLabels::register_new(
"test_label_counter",
"a labeled counter for tests"
);
}
COUNTER.inc(&TestLabels {
animal: Animal::Bird,
size: Size::Large,
});
COUNTER.inc(&TestLabels {
animal: Animal::Bird,
size: Size::Small,
});
COUNTER.inc(&TestLabels {
animal: Animal::Cat,
size: Size::Large,
});
assert_eq!(
COUNTER.get(&TestLabels {
animal: Animal::Bird,
size: Size::Large,
}),
1
);
assert_eq!(
COUNTER.get(&TestLabels {
animal: Animal::Cat,
size: Size::Small,
}),
0
);
}
#[test]
fn int_gauge_with_labels_get_works() {
lazy_static! {
static ref GAUGE: IntGaugeWithLabels<TestLabels> =
IntGaugeWithLabels::register_new("test_label_gauge", "a labeled gauge for tests");
}
GAUGE.inc(&TestLabels {
animal: Animal::Bird,
size: Size::Large,
});
GAUGE.inc(&TestLabels {
animal: Animal::Bird,
size: Size::Small,
});
GAUGE.inc(&TestLabels {
animal: Animal::Cat,
size: Size::Large,
});
assert_eq!(
GAUGE.get(&TestLabels {
animal: Animal::Bird,
size: Size::Large,
}),
1
);
assert_eq!(
GAUGE.get(&TestLabels {
animal: Animal::Cat,
size: Size::Small,
}),
0
);
}
}