use std::{
fmt,
sync::atomic::{AtomicI64, AtomicIsize, AtomicU64, AtomicUsize, Ordering},
time::Duration,
};
use prometheus_client::{
encoding::{EncodeLabel, EncodeLabelValue, LabelSetEncoder, LabelValueEncoder},
metrics::gauge,
};
pub trait EncodeLabelSet: Send + Sync {
fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> fmt::Result;
}
impl EncodeLabelSet for () {
fn encode(&self, _encoder: &mut LabelSetEncoder<'_>) -> fmt::Result {
Ok(())
}
}
impl<T: EncodeLabelSet + ?Sized> EncodeLabelSet for &T {
fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> fmt::Result {
(**self).encode(encoder)
}
}
impl<T: EncodeLabel + Send + Sync> EncodeLabelSet for [T] {
fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> fmt::Result {
for label in self {
label.encode(encoder.encode_label())?;
}
Ok(())
}
}
impl<T: EncodeLabel + Send + Sync, const N: usize> EncodeLabelSet for [T; N] {
fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> fmt::Result {
<[T]>::encode(self, encoder)
}
}
impl<T: EncodeLabel + Send + Sync> EncodeLabelSet for Vec<T> {
fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> fmt::Result {
<[T]>::encode(self, encoder)
}
}
#[derive(Debug, Clone, Copy)]
pub enum EncodedGaugeValue {
I64(i64),
F64(f64),
}
pub trait GaugeValue: 'static + Copy + fmt::Debug {
type Atomic: gauge::Atomic<Self> + Default + fmt::Debug;
fn encode(self) -> EncodedGaugeValue;
}
impl GaugeValue for i64 {
type Atomic = AtomicI64;
fn encode(self) -> EncodedGaugeValue {
EncodedGaugeValue::I64(self)
}
}
impl GaugeValue for u64 {
type Atomic = AtomicU64Wrapper;
#[allow(clippy::cast_precision_loss)] fn encode(self) -> EncodedGaugeValue {
i64::try_from(self).map_or_else(
|_| EncodedGaugeValue::F64(self as f64),
EncodedGaugeValue::I64,
)
}
}
#[derive(Debug, Default)]
pub struct AtomicU64Wrapper(AtomicU64);
macro_rules! impl_atomic_wrapper {
($wrapper:ty => $int:ty) => {
impl gauge::Atomic<$int> for $wrapper {
fn inc(&self) -> $int {
self.inc_by(1)
}
fn inc_by(&self, v: $int) -> $int {
self.0.fetch_add(v, Ordering::Relaxed)
}
fn dec(&self) -> $int {
self.dec_by(1)
}
fn dec_by(&self, v: $int) -> $int {
self.0.fetch_sub(v, Ordering::Relaxed)
}
fn set(&self, v: $int) -> $int {
self.0.swap(v, Ordering::Relaxed)
}
fn get(&self) -> $int {
self.0.load(Ordering::Relaxed)
}
}
};
}
impl_atomic_wrapper!(AtomicU64Wrapper => u64);
impl GaugeValue for usize {
type Atomic = AtomicUsizeWrapper;
fn encode(self) -> EncodedGaugeValue {
GaugeValue::encode(self as u64)
}
}
#[derive(Debug, Default)]
pub struct AtomicUsizeWrapper(AtomicUsize);
impl_atomic_wrapper!(AtomicUsizeWrapper => usize);
impl GaugeValue for isize {
type Atomic = AtomicIsizeWrapper;
fn encode(self) -> EncodedGaugeValue {
EncodedGaugeValue::I64(self as i64)
}
}
#[derive(Debug, Default)]
pub struct AtomicIsizeWrapper(AtomicIsize);
impl_atomic_wrapper!(AtomicIsizeWrapper => isize);
impl GaugeValue for f64 {
type Atomic = AtomicU64;
fn encode(self) -> EncodedGaugeValue {
EncodedGaugeValue::F64(self)
}
}
impl GaugeValue for Duration {
type Atomic = AtomicU64Wrapper;
fn encode(self) -> EncodedGaugeValue {
EncodedGaugeValue::F64(self.as_secs_f64())
}
}
impl gauge::Atomic<Duration> for AtomicU64Wrapper {
fn inc(&self) -> Duration {
self.inc_by(Duration::from_secs(1))
}
fn inc_by(&self, v: Duration) -> Duration {
Duration::from_secs_f64(self.0.inc_by(v.as_secs_f64()))
}
fn dec(&self) -> Duration {
self.dec_by(Duration::from_secs(1))
}
fn dec_by(&self, v: Duration) -> Duration {
Duration::from_secs_f64(self.0.dec_by(v.as_secs_f64()))
}
fn set(&self, v: Duration) -> Duration {
Duration::from_secs_f64(self.0.set(v.as_secs_f64()))
}
fn get(&self) -> Duration {
Duration::from_secs_f64(self.0.get())
}
}
pub trait HistogramValue: 'static + Copy + fmt::Debug {
fn encode(self) -> f64;
}
impl HistogramValue for f64 {
fn encode(self) -> f64 {
self
}
}
impl HistogramValue for Duration {
fn encode(self) -> f64 {
self.as_secs_f64()
}
}
macro_rules! impl_histogram_value_for_int {
($int:ty) => {
impl HistogramValue for $int {
#[allow(clippy::cast_precision_loss)] fn encode(self) -> f64 {
self as f64
}
}
};
}
impl_histogram_value_for_int!(i64);
impl_histogram_value_for_int!(u64);
impl_histogram_value_for_int!(usize);
impl_histogram_value_for_int!(isize);
pub trait MapLabels<S>: Copy {
type Output<'a>: EncodeLabelSet
where
Self: 'a,
S: 'a;
fn map_labels<'a>(&'a self, labels: &'a S) -> Self::Output<'a>;
}
impl<S: EncodeLabelSet> MapLabels<S> for () {
type Output<'a>
= LabelRef<'a, S>
where
S: 'a;
fn map_labels<'a>(&'a self, labels: &'a S) -> Self::Output<'a> {
LabelRef(labels)
}
}
#[derive(Debug)]
pub struct LabelRef<'a, S>(pub &'a S);
impl<S: EncodeLabelSet> EncodeLabelSet for LabelRef<'_, S> {
fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> fmt::Result {
self.0.encode(encoder)
}
}
impl<S: EncodeLabelValue> EncodeLabelValue for LabelRef<'_, S> {
fn encode(&self, encoder: &mut LabelValueEncoder) -> fmt::Result {
self.0.encode(encoder)
}
}
#[derive(Debug)]
pub struct StaticLabelSet<'a, S: 'a> {
label_keys: &'a [&'static str],
label_values: S,
}
impl<S: EncodeLabelValue + Send + Sync> MapLabels<S> for [&'static str; 1] {
type Output<'a>
= StaticLabelSet<'a, (&'a S,)>
where
S: 'a;
fn map_labels<'a>(&'a self, labels: &'a S) -> Self::Output<'a> {
StaticLabelSet {
label_keys: self,
label_values: (labels,),
}
}
}
macro_rules! impl_map_labels {
($len:tt => $($idx:tt : $typ:ident),+) => {
impl<$($typ,)+> MapLabels<($($typ,)+)> for [&'static str; $len]
where
$($typ: EncodeLabelValue + Send + Sync,)+
{
type Output<'a> = StaticLabelSet<'a, ($(&'a $typ,)+)> where $($typ: 'a,)+;
fn map_labels<'a>(&'a self, labels: &'a ($($typ,)+)) -> Self::Output<'a> {
StaticLabelSet {
label_keys: self,
label_values: ($(&labels.$idx,)+),
}
}
}
};
}
impl_map_labels!(2 => 0: S0, 1: S1);
impl_map_labels!(3 => 0: S0, 1: S1, 2: S2);
impl_map_labels!(4 => 0: S0, 1: S1, 2: S2, 3: S3);
macro_rules! impl_encode_for_static_label_set {
($len:tt => $($idx:tt : $typ:ident),+) => {
impl<'a, $($typ,)+> EncodeLabelSet for StaticLabelSet<'a, ($(&'a $typ,)+)>
where
$($typ: EncodeLabelValue + Send + Sync,)+
{
fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> fmt::Result {
$(
let label = (self.label_keys[$idx], LabelRef(self.label_values.$idx));
EncodeLabel::encode(&label, encoder.encode_label())?;
)+
Ok(())
}
}
};
}
impl_encode_for_static_label_set!(1 => 0: S);
impl_encode_for_static_label_set!(2 => 0: S0, 1: S1);
impl_encode_for_static_label_set!(3 => 0: S0, 1: S1, 2: S2);
impl_encode_for_static_label_set!(4 => 0: S0, 1: S1, 2: S2, 3: S3);