substrate_prometheus_endpoint/
sourced.rs1use prometheus::{
21 core::{Collector, Desc, Describer, Number, Opts},
22 proto,
23};
24use std::{cmp::Ordering, marker::PhantomData};
25
26pub type SourcedCounter<S> = SourcedMetric<Counter, S>;
32
33pub type SourcedGauge<S> = SourcedMetric<Gauge, S>;
35
36#[derive(Copy, Clone)]
38pub enum Counter {}
39
40#[derive(Copy, Clone)]
42pub enum Gauge {}
43
44#[derive(Debug, Clone)]
47pub struct SourcedMetric<T, S> {
48 source: S,
49 desc: Desc,
50 _type: PhantomData<T>,
51}
52
53pub trait MetricSource: Sync + Send + Clone {
55 type N: Number;
57 fn collect(&self, set: impl FnMut(&[&str], Self::N));
59}
60
61impl<T: SourcedType, S: MetricSource> SourcedMetric<T, S> {
62 pub fn new(opts: &Opts, source: S) -> prometheus::Result<Self> {
64 let desc = opts.describe()?;
65 Ok(Self { source, desc, _type: PhantomData })
66 }
67}
68
69impl<T: SourcedType, S: MetricSource> Collector for SourcedMetric<T, S> {
70 fn desc(&self) -> Vec<&Desc> {
71 vec![&self.desc]
72 }
73
74 fn collect(&self) -> Vec<proto::MetricFamily> {
75 let mut counters = Vec::new();
76
77 self.source.collect(|label_values, value| {
78 let mut m = proto::Metric::default();
79
80 match T::proto() {
81 proto::MetricType::COUNTER => {
82 let mut c = proto::Counter::default();
83 c.set_value(value.into_f64());
84 m.set_counter(c);
85 },
86 proto::MetricType::GAUGE => {
87 let mut g = proto::Gauge::default();
88 g.set_value(value.into_f64());
89 m.set_gauge(g);
90 },
91 t => {
92 log::error!("Unsupported sourced metric type: {:?}", t);
93 },
94 }
95
96 debug_assert_eq!(self.desc.variable_labels.len(), label_values.len());
97 match self.desc.variable_labels.len().cmp(&label_values.len()) {
98 Ordering::Greater => {
99 log::warn!("Missing label values for sourced metric {}", self.desc.fq_name)
100 },
101 Ordering::Less => {
102 log::warn!("Too many label values for sourced metric {}", self.desc.fq_name)
103 },
104 Ordering::Equal => {},
105 }
106
107 m.set_label(
108 self.desc
109 .variable_labels
110 .iter()
111 .zip(label_values)
112 .map(|(l_name, l_value)| {
113 let mut l = proto::LabelPair::default();
114 l.set_name(l_name.to_string());
115 l.set_value(l_value.to_string());
116 l
117 })
118 .chain(self.desc.const_label_pairs.iter().cloned())
119 .collect::<Vec<_>>(),
120 );
121
122 counters.push(m);
123 });
124
125 let mut m = proto::MetricFamily::default();
126 m.set_name(self.desc.fq_name.clone());
127 m.set_help(self.desc.help.clone());
128 m.set_field_type(T::proto());
129 m.set_metric(counters);
130
131 vec![m]
132 }
133}
134
135pub trait SourcedType: private::Sealed + Sync + Send {
137 #[doc(hidden)]
138 fn proto() -> proto::MetricType;
139}
140
141impl SourcedType for Counter {
142 fn proto() -> proto::MetricType {
143 proto::MetricType::COUNTER
144 }
145}
146
147impl SourcedType for Gauge {
148 fn proto() -> proto::MetricType {
149 proto::MetricType::GAUGE
150 }
151}
152
153mod private {
154 pub trait Sealed {}
155 impl Sealed for super::Counter {}
156 impl Sealed for super::Gauge {}
157}