use desc::Desc;
use errors::{Error, Result};
use metrics::{Collector, Metric, Opts};
use proto;
use std::sync::Arc;
use value::{Value, ValueType};
use vec::{MetricVec, MetricVecBuilder};
#[derive(Clone)]
pub struct Counter {
v: Arc<Value>,
}
impl Counter {
pub fn new<S: Into<String>>(name: S, help: S) -> Result<Counter> {
let opts = Opts::new(name, help);
Counter::with_opts(opts)
}
pub fn with_opts(opts: Opts) -> Result<Counter> {
Counter::with_opts_and_label_values(&opts, &[])
}
fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Counter> {
let v = Value::new(opts, ValueType::Counter, 0.0, label_values)?;
Ok(Counter { v: Arc::new(v) })
}
#[inline]
pub fn inc_by(&self, v: f64) -> Result<()> {
if v < 0.0 {
return Err(Error::DecreaseCounter(v));
}
Ok(self.v.inc_by(v))
}
#[inline]
pub fn inc(&self) {
self.inc_by(1.0).unwrap()
}
#[inline]
pub fn get(&self) -> f64 {
self.v.get()
}
}
impl Collector for Counter {
fn desc(&self) -> Vec<&Desc> {
vec![&self.v.desc]
}
fn collect(&self) -> Vec<proto::MetricFamily> {
vec![self.v.collect()]
}
}
impl Metric for Counter {
fn metric(&self) -> proto::Metric {
self.v.metric()
}
}
#[derive(Clone)]
pub struct CounterVecBuilder {}
impl MetricVecBuilder for CounterVecBuilder {
type M = Counter;
type P = Opts;
fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Counter> {
Counter::with_opts_and_label_values(opts, vals)
}
}
pub type CounterVec = MetricVec<CounterVecBuilder>;
impl CounterVec {
pub fn new(opts: Opts, label_names: &[&str]) -> Result<CounterVec> {
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::COUNTER, CounterVecBuilder {}, opts)?;
Ok(metric_vec as CounterVec)
}
}
#[cfg(test)]
mod tests {
use super::*;
use metrics::{Collector, Opts};
use std::collections::HashMap;
#[test]
fn test_counter() {
let opts = Opts::new("test", "test help")
.const_label("a", "1")
.const_label("b", "2");
let counter = Counter::with_opts(opts).unwrap();
counter.inc();
assert_eq!(counter.get() as u64, 1);
counter.inc_by(42.0).unwrap();
assert_eq!(counter.get() as u64, 43);
let mut mfs = counter.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_counter().get_value() as u64, 43);
}
#[test]
fn test_counter_vec_with_labels() {
let vec = CounterVec::new(
Opts::new("test_couter_vec", "test counter 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();
assert!(vec.remove(&labels).is_ok());
assert!(vec.remove(&labels).is_err());
let mut labels2 = HashMap::new();
labels2.insert("l1", "v2");
labels2.insert("l2", "v1");
vec.with(&labels).inc();
assert!(vec.remove(&labels2).is_err());
vec.with(&labels).inc();
let mut labels3 = HashMap::new();
labels3.insert("l1", "v1");
assert!(vec.remove(&labels3).is_err());
}
#[test]
fn test_counter_vec_with_label_values() {
let vec = CounterVec::new(
Opts::new("test_vec", "test counter 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();
assert!(vec.remove_label_values(&["v1"]).is_err());
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
}
}