1 // Copyright 2014 The Prometheus Authors 2 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. 3 4 use std::marker::PhantomData; 5 use std::sync::Arc; 6 7 use crate::atomic64::{Atomic, AtomicF64, AtomicI64, Number}; 8 use crate::desc::Desc; 9 use crate::errors::Result; 10 use crate::metrics::{Collector, Metric, Opts}; 11 use crate::proto; 12 use crate::value::{Value, ValueType}; 13 use crate::vec::{MetricVec, MetricVecBuilder}; 14 15 /// The underlying implementation for [`Gauge`] and [`IntGauge`]. 16 #[derive(Debug)] 17 pub struct GenericGauge<P: Atomic> { 18 v: Arc<Value<P>>, 19 } 20 21 /// A [`Metric`] represents a single numerical value that can arbitrarily go up 22 /// and down. 23 pub type Gauge = GenericGauge<AtomicF64>; 24 25 /// The integer version of [`Gauge`]. Provides better performance if metric values are 26 /// all integers. 27 pub type IntGauge = GenericGauge<AtomicI64>; 28 29 impl<P: Atomic> Clone for GenericGauge<P> { clone(&self) -> Self30 fn clone(&self) -> Self { 31 Self { 32 v: Arc::clone(&self.v), 33 } 34 } 35 } 36 37 impl<P: Atomic> GenericGauge<P> { 38 /// Create a [`GenericGauge`] with the `name` and `help` arguments. new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Result<Self>39 pub fn new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Result<Self> { 40 let opts = Opts::new(name, help); 41 Self::with_opts(opts) 42 } 43 44 /// Create a [`GenericGauge`] with the `opts` options. with_opts(opts: Opts) -> Result<Self>45 pub fn with_opts(opts: Opts) -> Result<Self> { 46 Self::with_opts_and_label_values(&opts, &[]) 47 } 48 with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self>49 fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> { 50 let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?; 51 Ok(Self { v: Arc::new(v) }) 52 } 53 54 /// Set the gauge to an arbitrary value. 55 #[inline] set(&self, v: P::T)56 pub fn set(&self, v: P::T) { 57 self.v.set(v); 58 } 59 60 /// Increase the gauge by 1. 61 #[inline] inc(&self)62 pub fn inc(&self) { 63 self.v.inc(); 64 } 65 66 /// Decrease the gauge by 1. 67 #[inline] dec(&self)68 pub fn dec(&self) { 69 self.v.dec(); 70 } 71 72 /// Add the given value to the gauge. (The value can be 73 /// negative, resulting in a decrement of the gauge.) 74 #[inline] add(&self, v: P::T)75 pub fn add(&self, v: P::T) { 76 self.v.inc_by(v); 77 } 78 79 /// Subtract the given value from the gauge. (The value can be 80 /// negative, resulting in an increment of the gauge.) 81 #[inline] sub(&self, v: P::T)82 pub fn sub(&self, v: P::T) { 83 self.v.dec_by(v); 84 } 85 86 /// Return the gauge value. 87 #[inline] get(&self) -> P::T88 pub fn get(&self) -> P::T { 89 self.v.get() 90 } 91 } 92 93 impl<P: Atomic> Collector for GenericGauge<P> { desc(&self) -> Vec<&Desc>94 fn desc(&self) -> Vec<&Desc> { 95 vec![&self.v.desc] 96 } 97 collect(&self) -> Vec<proto::MetricFamily>98 fn collect(&self) -> Vec<proto::MetricFamily> { 99 vec![self.v.collect()] 100 } 101 } 102 103 impl<P: Atomic> Metric for GenericGauge<P> { metric(&self) -> proto::Metric104 fn metric(&self) -> proto::Metric { 105 self.v.metric() 106 } 107 } 108 109 #[derive(Debug)] 110 pub struct GaugeVecBuilder<P: Atomic> { 111 _phantom: PhantomData<P>, 112 } 113 114 impl<P: Atomic> GaugeVecBuilder<P> { new() -> Self115 pub fn new() -> Self { 116 Self { 117 _phantom: PhantomData, 118 } 119 } 120 } 121 122 impl<P: Atomic> Clone for GaugeVecBuilder<P> { clone(&self) -> Self123 fn clone(&self) -> Self { 124 Self::new() 125 } 126 } 127 128 impl<P: Atomic> MetricVecBuilder for GaugeVecBuilder<P> { 129 type M = GenericGauge<P>; 130 type P = Opts; 131 build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M>132 fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> { 133 Self::M::with_opts_and_label_values(opts, vals) 134 } 135 } 136 137 /// The underlying implementation for [`GaugeVec`] and [`IntGaugeVec`]. 138 pub type GenericGaugeVec<P> = MetricVec<GaugeVecBuilder<P>>; 139 140 /// A [`Collector`] that bundles a set of [`Gauge`]s that all share 141 /// the same [`Desc`], but have different values for their variable labels. This is 142 /// used if you want to count the same thing partitioned by various dimensions 143 /// (e.g. number of operations queued, partitioned by user and operation type). 144 pub type GaugeVec = GenericGaugeVec<AtomicF64>; 145 146 /// The integer version of [`GaugeVec`]. Provides better performance if metric values 147 /// are all integers. 148 pub type IntGaugeVec = GenericGaugeVec<AtomicI64>; 149 150 impl<P: Atomic> GenericGaugeVec<P> { 151 /// Create a new [`GenericGaugeVec`] based on the provided 152 /// [`Opts`] and partitioned by the given label names. At least one label name must 153 /// be provided. new(opts: Opts, label_names: &[&str]) -> Result<Self>154 pub fn new(opts: Opts, label_names: &[&str]) -> Result<Self> { 155 let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect(); 156 let opts = opts.variable_labels(variable_names); 157 let metric_vec = MetricVec::create(proto::MetricType::GAUGE, GaugeVecBuilder::new(), opts)?; 158 159 Ok(metric_vec as Self) 160 } 161 } 162 163 #[cfg(test)] 164 mod tests { 165 use std::collections::HashMap; 166 167 use super::*; 168 use crate::metrics::{Collector, Opts}; 169 170 #[test] test_gauge()171 fn test_gauge() { 172 let opts = Opts::new("test", "test help") 173 .const_label("a", "1") 174 .const_label("b", "2"); 175 let gauge = Gauge::with_opts(opts).unwrap(); 176 gauge.inc(); 177 assert_eq!(gauge.get() as u64, 1); 178 gauge.add(42.0); 179 assert_eq!(gauge.get() as u64, 43); 180 gauge.sub(42.0); 181 assert_eq!(gauge.get() as u64, 1); 182 gauge.dec(); 183 assert_eq!(gauge.get() as u64, 0); 184 gauge.set(42.0); 185 assert_eq!(gauge.get() as u64, 42); 186 187 let mut mfs = gauge.collect(); 188 assert_eq!(mfs.len(), 1); 189 190 let mf = mfs.pop().unwrap(); 191 let m = mf.get_metric().get(0).unwrap(); 192 assert_eq!(m.get_label().len(), 2); 193 assert_eq!(m.get_gauge().get_value() as u64, 42); 194 } 195 196 #[test] test_gauge_vec_with_labels()197 fn test_gauge_vec_with_labels() { 198 let vec = GaugeVec::new( 199 Opts::new("test_gauge_vec", "test gauge vec help"), 200 &["l1", "l2"], 201 ) 202 .unwrap(); 203 204 let mut labels = HashMap::new(); 205 labels.insert("l1", "v1"); 206 labels.insert("l2", "v2"); 207 assert!(vec.remove(&labels).is_err()); 208 209 vec.with(&labels).inc(); 210 vec.with(&labels).dec(); 211 vec.with(&labels).add(42.0); 212 vec.with(&labels).sub(42.0); 213 vec.with(&labels).set(42.0); 214 215 assert!(vec.remove(&labels).is_ok()); 216 assert!(vec.remove(&labels).is_err()); 217 } 218 219 #[test] test_gauge_vec_with_label_values()220 fn test_gauge_vec_with_label_values() { 221 let vec = GaugeVec::new( 222 Opts::new("test_gauge_vec", "test gauge vec help"), 223 &["l1", "l2"], 224 ) 225 .unwrap(); 226 227 assert!(vec.remove_label_values(&["v1", "v2"]).is_err()); 228 vec.with_label_values(&["v1", "v2"]).inc(); 229 assert!(vec.remove_label_values(&["v1", "v2"]).is_ok()); 230 231 vec.with_label_values(&["v1", "v2"]).inc(); 232 vec.with_label_values(&["v1", "v2"]).dec(); 233 vec.with_label_values(&["v1", "v2"]).add(42.0); 234 vec.with_label_values(&["v1", "v2"]).sub(42.0); 235 vec.with_label_values(&["v1", "v2"]).set(42.0); 236 237 assert!(vec.remove_label_values(&["v1"]).is_err()); 238 assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); 239 } 240 } 241