use std::marker::PhantomData;
use std::sync::Arc;
use crate::atomic64::{Atomic, AtomicF64, AtomicI64, Number};
use crate::desc::Desc;
use crate::errors::Result;
use crate::metrics::{Collector, Metric, Opts};
use crate::proto;
use crate::value::{Value, ValueType};
use crate::vec::{MetricVec, MetricVecBuilder};
#[derive(Debug)]
pub struct GenericGauge<P: Atomic> {
v: Arc<Value<P>>,
}
pub type Gauge = GenericGauge<AtomicF64>;
pub type IntGauge = GenericGauge<AtomicI64>;
impl<P: Atomic> Clone for GenericGauge<P> {
fn clone(&self) -> Self {
Self {
v: Arc::clone(&self.v),
}
}
}
impl<P: Atomic> GenericGauge<P> {
pub fn new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Result<Self> {
let opts = Opts::new(name, help);
Self::with_opts(opts)
}
pub fn with_opts(opts: Opts) -> Result<Self> {
Self::with_opts_and_label_values(&opts, &[])
}
fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> {
let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?;
Ok(Self { v: Arc::new(v) })
}
#[inline]
pub fn set(&self, v: P::T) {
self.v.set(v);
}
#[inline]
pub fn inc(&self) {
self.v.inc();
}
#[inline]
pub fn dec(&self) {
self.v.dec();
}
#[inline]
pub fn add(&self, v: P::T) {
self.v.inc_by(v);
}
#[inline]
pub fn sub(&self, v: P::T) {
self.v.dec_by(v);
}
#[inline]
pub fn get(&self) -> P::T {
self.v.get()
}
}
impl<P: Atomic> Collector for GenericGauge<P> {
fn desc(&self) -> Vec<&Desc> {
vec![&self.v.desc]
}
fn collect(&self) -> Vec<proto::MetricFamily> {
vec![self.v.collect()]
}
}
impl<P: Atomic> Metric for GenericGauge<P> {
fn metric(&self) -> proto::Metric {
self.v.metric()
}
}
#[derive(Debug)]
pub struct GaugeVecBuilder<P: Atomic> {
_phantom: PhantomData<P>,
}
impl<P: Atomic> GaugeVecBuilder<P> {
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<P: Atomic> Clone for GaugeVecBuilder<P> {
fn clone(&self) -> Self {
Self::new()
}
}
impl<P: Atomic> MetricVecBuilder for GaugeVecBuilder<P> {
type M = GenericGauge<P>;
type P = Opts;
fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> {
Self::M::with_opts_and_label_values(opts, vals)
}
}
pub type GenericGaugeVec<P> = MetricVec<GaugeVecBuilder<P>>;
pub type GaugeVec = GenericGaugeVec<AtomicF64>;
pub type IntGaugeVec = GenericGaugeVec<AtomicI64>;
impl<P: Atomic> GenericGaugeVec<P> {
pub fn new(opts: Opts, label_names: &[&str]) -> Result<Self> {
let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect();
let opts = opts.variable_labels(variable_names);
let metric_vec = MetricVec::create(proto::MetricType::GAUGE, GaugeVecBuilder::new(), opts)?;
Ok(metric_vec as Self)
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
use crate::metrics::{Collector, Opts};
#[test]
fn test_gauge() {
let opts = Opts::new("test", "test help")
.const_label("a", "1")
.const_label("b", "2");
let gauge = Gauge::with_opts(opts).unwrap();
gauge.inc();
assert_eq!(gauge.get() as u64, 1);
gauge.add(42.0);
assert_eq!(gauge.get() as u64, 43);
gauge.sub(42.0);
assert_eq!(gauge.get() as u64, 1);
gauge.dec();
assert_eq!(gauge.get() as u64, 0);
gauge.set(42.0);
assert_eq!(gauge.get() as u64, 42);
let mut mfs = gauge.collect();
assert_eq!(mfs.len(), 1);
let mf = mfs.pop().unwrap();
let m = mf.get_metric().get(0).unwrap();
assert_eq!(m.get_label().len(), 2);
assert_eq!(m.get_gauge().get_value() as u64, 42);
}
#[test]
fn test_gauge_vec_with_labels() {
let vec = GaugeVec::new(
Opts::new("test_gauge_vec", "test gauge vec help"),
&["l1", "l2"],
)
.unwrap();
let mut labels = HashMap::new();
labels.insert("l1", "v1");
labels.insert("l2", "v2");
assert!(vec.remove(&labels).is_err());
vec.with(&labels).inc();
vec.with(&labels).dec();
vec.with(&labels).add(42.0);
vec.with(&labels).sub(42.0);
vec.with(&labels).set(42.0);
assert!(vec.remove(&labels).is_ok());
assert!(vec.remove(&labels).is_err());
}
#[test]
fn test_gauge_vec_with_label_values() {
let vec = GaugeVec::new(
Opts::new("test_gauge_vec", "test gauge vec help"),
&["l1", "l2"],
)
.unwrap();
assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
vec.with_label_values(&["v1", "v2"]).inc();
assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
vec.with_label_values(&["v1", "v2"]).inc();
vec.with_label_values(&["v1", "v2"]).dec();
vec.with_label_values(&["v1", "v2"]).add(42.0);
vec.with_label_values(&["v1", "v2"]).sub(42.0);
vec.with_label_values(&["v1", "v2"]).set(42.0);
assert!(vec.remove_label_values(&["v1"]).is_err());
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
}
}