diff --git a/src/lib.rs b/src/lib.rs index 7991d8ff..55cbea35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,6 +158,7 @@ mod errors; mod gauge; mod histogram; mod metrics; +mod pulling_gauge; #[cfg(feature = "push")] mod push; mod registry; @@ -219,6 +220,7 @@ pub use self::histogram::DEFAULT_BUCKETS; pub use self::histogram::{exponential_buckets, linear_buckets}; pub use self::histogram::{Histogram, HistogramOpts, HistogramTimer, HistogramVec}; pub use self::metrics::Opts; +pub use self::pulling_gauge::PullingGauge; #[cfg(feature = "push")] pub use self::push::{ hostname_grouping_key, push_add_collector, push_add_metrics, push_collector, push_metrics, diff --git a/src/pulling_gauge.rs b/src/pulling_gauge.rs new file mode 100644 index 00000000..6fbe9633 --- /dev/null +++ b/src/pulling_gauge.rs @@ -0,0 +1,101 @@ +use std::{collections::HashMap, fmt, sync::Arc}; + +use crate::{ + core::Collector, + proto::{Gauge, Metric, MetricFamily, MetricType}, +}; +use protobuf::RepeatedField; + +/// A [Gauge] that returns the value from a provided function on every collect run. +/// +/// This metric is the equivalant of Go's +/// +/// +/// # Examples +/// ``` +/// # use prometheus::{Registry, PullingGauge}; +/// # // We are stubbing out std::thread::available_parallelism since it's not available in the +/// # // oldest Rust version that we support. +/// # fn available_parallelism() -> f64 { 0.0 } +/// +/// let registry = Registry::new(); +/// let gauge = PullingGauge::new( +/// "available_parallelism", +/// "The available parallelism, usually the numbers of logical cores.", +/// Box::new(|| available_parallelism()) +/// ).unwrap(); +/// registry.register(Box::new(gauge)); +/// ``` +#[derive(Clone)] +pub struct PullingGauge { + desc: crate::core::Desc, + value: Arc f64 + Send + Sync>>, +} + +impl fmt::Debug for PullingGauge { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PullingGauge") + .field("desc", &self.desc) + .field("value", &"") + .finish() + } +} + +impl PullingGauge { + /// Create a new [`PullingGauge`]. + pub fn new, S2: Into>( + name: S1, + help: S2, + value: Box f64 + Send + Sync>, + ) -> crate::Result { + Ok(PullingGauge { + value: Arc::new(value), + desc: crate::core::Desc::new(name.into(), help.into(), Vec::new(), HashMap::new())?, + }) + } + + fn metric(&self) -> Metric { + let mut gauge = Gauge::default(); + let getter = &self.value; + gauge.set_value(getter()); + + let mut metric = Metric::default(); + metric.set_gauge(gauge); + + metric + } +} + +impl Collector for PullingGauge { + fn desc(&self) -> Vec<&crate::core::Desc> { + vec![&self.desc] + } + + fn collect(&self) -> Vec { + let mut m = MetricFamily::default(); + m.set_name(self.desc.fq_name.clone()); + m.set_help(self.desc.help.clone()); + m.set_field_type(MetricType::GAUGE); + m.set_metric(RepeatedField::from_vec(vec![self.metric()])); + vec![m] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::metrics::Collector; + + #[test] + fn test_pulling_gauge() { + const VALUE: f64 = 10.0; + + let gauge = + PullingGauge::new("test_gauge", "Purely for testing", Box::new(|| VALUE)).unwrap(); + + let metrics = gauge.collect(); + assert_eq!(metrics.len(), 1); + + assert_eq!(VALUE, metrics[0].get_metric()[0].get_gauge().get_value()); + } +}