/rust/registry/src/index.crates.io-1949cf8c6b5b557f/prometheus-client-0.24.0/src/metrics/exemplar.rs
Line | Count | Source |
1 | | //! Module implementing an Open Metrics exemplars for counters and histograms. |
2 | | //! |
3 | | //! See [`CounterWithExemplar`] and [`HistogramWithExemplars`] for details. |
4 | | |
5 | | use crate::encoding::{ |
6 | | EncodeCounterValue, EncodeExemplarValue, EncodeLabelSet, EncodeMetric, MetricEncoder, |
7 | | }; |
8 | | |
9 | | use super::counter::{self, Counter}; |
10 | | use super::histogram::Histogram; |
11 | | use super::{MetricType, TypedMetric}; |
12 | | use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; |
13 | | use std::collections::HashMap; |
14 | | #[cfg(not(target_has_atomic = "64"))] |
15 | | use std::sync::atomic::AtomicU32; |
16 | | #[cfg(target_has_atomic = "64")] |
17 | | use std::sync::atomic::AtomicU64; |
18 | | use std::sync::Arc; |
19 | | use std::time::SystemTime; |
20 | | |
21 | | /// An OpenMetrics exemplar. |
22 | | #[derive(Debug)] |
23 | | pub struct Exemplar<S, V> { |
24 | | pub(crate) label_set: S, |
25 | | pub(crate) value: V, |
26 | | pub(crate) timestamp: Option<SystemTime>, |
27 | | } |
28 | | |
29 | | ///////////////////////////////////////////////////////////////////////////////// |
30 | | // Counter |
31 | | |
32 | | /// Open Metrics [`Counter`] with an [`Exemplar`] to both measure discrete |
33 | | /// events and track references to data outside of the metric set. |
34 | | /// |
35 | | /// ``` |
36 | | /// # use prometheus_client::metrics::exemplar::CounterWithExemplar; |
37 | | /// let counter_with_exemplar = CounterWithExemplar::<Vec<(String, String)>>::default(); |
38 | | /// counter_with_exemplar.inc_by(1, Some(vec![("user_id".to_string(), "42".to_string())]), None); |
39 | | /// let _value: (u64, _) = counter_with_exemplar.get(); |
40 | | /// ``` |
41 | | /// You can also use exemplars with families. Just wrap the exemplar in a Family. |
42 | | /// ``` |
43 | | /// # use prometheus_client::metrics::exemplar::CounterWithExemplar; |
44 | | /// # use prometheus_client::metrics::histogram::exponential_buckets; |
45 | | /// # use prometheus_client::metrics::family::Family; |
46 | | /// # use prometheus_client_derive_encode::EncodeLabelSet; |
47 | | /// #[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug, Default)] |
48 | | /// pub struct ResultLabel { |
49 | | /// pub result: String, |
50 | | /// } |
51 | | /// |
52 | | /// #[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug, Default)] |
53 | | /// pub struct TraceLabel { |
54 | | /// pub trace_id: String, |
55 | | /// } |
56 | | /// |
57 | | /// let latency: Family<ResultLabel, CounterWithExemplar<TraceLabel>> = Family::default(); |
58 | | /// |
59 | | /// latency |
60 | | /// .get_or_create(&ResultLabel { |
61 | | /// result: "success".to_owned(), |
62 | | /// }) |
63 | | /// .inc_by( |
64 | | /// 1, |
65 | | /// Some(TraceLabel { |
66 | | /// trace_id: "3a2f90c9f80b894f".to_owned(), |
67 | | /// }), |
68 | | /// None, |
69 | | /// ); |
70 | | /// ``` |
71 | | #[cfg(target_has_atomic = "64")] |
72 | | #[derive(Debug)] |
73 | | pub struct CounterWithExemplar<S, N = u64, A = AtomicU64> { |
74 | | pub(crate) inner: Arc<RwLock<CounterWithExemplarInner<S, N, A>>>, |
75 | | } |
76 | | |
77 | | impl<S> TypedMetric for CounterWithExemplar<S> { |
78 | | const TYPE: MetricType = MetricType::Counter; |
79 | | } |
80 | | |
81 | | /// Open Metrics [`Counter`] with an [`Exemplar`] to both measure discrete |
82 | | /// events and track references to data outside of the metric set. |
83 | | #[cfg(not(target_has_atomic = "64"))] |
84 | | #[derive(Debug)] |
85 | | pub struct CounterWithExemplar<S, N = u32, A = AtomicU32> { |
86 | | pub(crate) inner: Arc<RwLock<CounterWithExemplarInner<S, N, A>>>, |
87 | | } |
88 | | |
89 | | impl<S, N, A> Clone for CounterWithExemplar<S, N, A> { |
90 | 0 | fn clone(&self) -> Self { |
91 | 0 | CounterWithExemplar { |
92 | 0 | inner: self.inner.clone(), |
93 | 0 | } |
94 | 0 | } |
95 | | } |
96 | | |
97 | | /// An OpenMetrics [`Counter`] in combination with an OpenMetrics [`Exemplar`]. |
98 | | #[derive(Debug)] |
99 | | pub struct CounterWithExemplarInner<S, N, A> { |
100 | | pub(crate) exemplar: Option<Exemplar<S, N>>, |
101 | | pub(crate) counter: Counter<N, A>, |
102 | | } |
103 | | |
104 | | impl<S, N, A: Default> Default for CounterWithExemplar<S, N, A> { |
105 | 0 | fn default() -> Self { |
106 | 0 | Self { |
107 | 0 | inner: Arc::new(RwLock::new(CounterWithExemplarInner { |
108 | 0 | exemplar: None, |
109 | 0 | counter: Default::default(), |
110 | 0 | })), |
111 | 0 | } |
112 | 0 | } |
113 | | } |
114 | | |
115 | | impl<S, N: Clone, A: counter::Atomic<N>> CounterWithExemplar<S, N, A> { |
116 | | // TODO: Implement `fn inc`. Problematic right now as one can not produce |
117 | | // value `1` of type `N`. |
118 | | |
119 | | /// Increase the [`CounterWithExemplar`] by `v`, updating the [`Exemplar`] |
120 | | /// if a label set is provided, returning the previous value. |
121 | 0 | pub fn inc_by(&self, v: N, label_set: Option<S>, timestamp: Option<SystemTime>) -> N { |
122 | 0 | let mut inner = self.inner.write(); |
123 | | |
124 | 0 | inner.exemplar = label_set.map(|label_set| Exemplar { |
125 | 0 | label_set, |
126 | 0 | value: v.clone(), |
127 | 0 | timestamp, |
128 | 0 | }); |
129 | | |
130 | 0 | inner.counter.inc_by(v) |
131 | 0 | } |
132 | | |
133 | | /// Get the current value of the [`CounterWithExemplar`] as well as its |
134 | | /// [`Exemplar`] if any. |
135 | 0 | pub fn get(&self) -> (N, MappedRwLockReadGuard<'_, Option<Exemplar<S, N>>>) { |
136 | 0 | let inner = self.inner.read(); |
137 | 0 | let value = inner.counter.get(); |
138 | 0 | let exemplar = RwLockReadGuard::map(inner, |inner| &inner.exemplar); |
139 | 0 | (value, exemplar) |
140 | 0 | } |
141 | | |
142 | | /// Exposes the inner atomic type of the [`CounterWithExemplar`]. |
143 | | /// |
144 | | /// This should only be used for advanced use-cases which are not directly |
145 | | /// supported by the library. |
146 | | /// |
147 | | /// The caller of this function has to uphold the property of an Open |
148 | | /// Metrics counter namely that the value is monotonically increasing, i.e. |
149 | | /// either stays the same or increases. |
150 | 0 | pub fn inner(&self) -> MappedRwLockReadGuard<'_, A> { |
151 | 0 | RwLockReadGuard::map(self.inner.read(), |inner| inner.counter.inner()) |
152 | 0 | } |
153 | | } |
154 | | |
155 | | // TODO: S, V, N, A are hard to grasp. |
156 | | impl<S, N, A> EncodeMetric for crate::metrics::exemplar::CounterWithExemplar<S, N, A> |
157 | | where |
158 | | S: EncodeLabelSet, |
159 | | N: EncodeCounterValue + EncodeExemplarValue + Clone, |
160 | | A: counter::Atomic<N>, |
161 | | { |
162 | 0 | fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> { |
163 | 0 | let (value, exemplar) = self.get(); |
164 | 0 | encoder.encode_counter(&value, exemplar.as_ref()) |
165 | 0 | } |
166 | | |
167 | 0 | fn metric_type(&self) -> MetricType { |
168 | 0 | Counter::<N, A>::TYPE |
169 | 0 | } |
170 | | } |
171 | | |
172 | | ///////////////////////////////////////////////////////////////////////////////// |
173 | | // Histogram |
174 | | |
175 | | /// Open Metrics [`Histogram`] to both measure distributions of discrete events. |
176 | | /// and track references to data outside of the metric set. |
177 | | /// |
178 | | /// ``` |
179 | | /// # use prometheus_client::metrics::exemplar::HistogramWithExemplars; |
180 | | /// # use prometheus_client::metrics::histogram::exponential_buckets; |
181 | | /// let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10)); |
182 | | /// histogram.observe(4.2, Some(vec![("user_id".to_string(), "42".to_string())]), None); |
183 | | /// ``` |
184 | | /// You can also use exemplars with families. Just wrap the exemplar in a Family. |
185 | | /// ``` |
186 | | /// # use prometheus_client::metrics::exemplar::HistogramWithExemplars; |
187 | | /// # use prometheus_client::metrics::histogram::exponential_buckets; |
188 | | /// # use prometheus_client::metrics::family::Family; |
189 | | /// # use prometheus_client::encoding::EncodeLabelSet; |
190 | | /// #[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug, Default)] |
191 | | /// pub struct ResultLabel { |
192 | | /// pub result: String, |
193 | | /// } |
194 | | /// |
195 | | /// #[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug, Default)] |
196 | | /// pub struct TraceLabel { |
197 | | /// pub trace_id: String, |
198 | | /// } |
199 | | /// |
200 | | /// let latency: Family<ResultLabel, HistogramWithExemplars<TraceLabel>> = |
201 | | /// Family::new_with_constructor(|| { |
202 | | /// HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10)) |
203 | | /// }); |
204 | | /// |
205 | | /// latency |
206 | | /// .get_or_create(&ResultLabel { |
207 | | /// result: "success".to_owned(), |
208 | | /// }) |
209 | | /// .observe( |
210 | | /// 0.001345422, |
211 | | /// Some(TraceLabel { |
212 | | /// trace_id: "3a2f90c9f80b894f".to_owned(), |
213 | | /// }), |
214 | | /// None, |
215 | | /// ); |
216 | | /// ``` |
217 | | #[derive(Debug)] |
218 | | pub struct HistogramWithExemplars<S> { |
219 | | // TODO: Not ideal, as Histogram has a Mutex as well. |
220 | | pub(crate) inner: Arc<RwLock<HistogramWithExemplarsInner<S>>>, |
221 | | } |
222 | | |
223 | | impl<S> TypedMetric for HistogramWithExemplars<S> { |
224 | | const TYPE: MetricType = MetricType::Histogram; |
225 | | } |
226 | | |
227 | | impl<S> Clone for HistogramWithExemplars<S> { |
228 | 0 | fn clone(&self) -> Self { |
229 | 0 | Self { |
230 | 0 | inner: self.inner.clone(), |
231 | 0 | } |
232 | 0 | } |
233 | | } |
234 | | |
235 | | /// An OpenMetrics [`Histogram`] in combination with an OpenMetrics [`Exemplar`]. |
236 | | #[derive(Debug)] |
237 | | pub struct HistogramWithExemplarsInner<S> { |
238 | | pub(crate) exemplars: HashMap<usize, Exemplar<S, f64>>, |
239 | | pub(crate) histogram: Histogram, |
240 | | } |
241 | | |
242 | | impl<S> HistogramWithExemplars<S> { |
243 | | /// Create a new [`HistogramWithExemplars`]. |
244 | 0 | pub fn new(buckets: impl Iterator<Item = f64>) -> Self { |
245 | 0 | Self { |
246 | 0 | inner: Arc::new(RwLock::new(HistogramWithExemplarsInner { |
247 | 0 | exemplars: Default::default(), |
248 | 0 | histogram: Histogram::new(buckets), |
249 | 0 | })), |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | /// Observe the given value, optionally providing a label set and thus |
254 | | /// setting the [`Exemplar`] value. |
255 | 0 | pub fn observe(&self, v: f64, label_set: Option<S>, timestamp: Option<SystemTime>) { |
256 | 0 | let mut inner = self.inner.write(); |
257 | 0 | let bucket = inner.histogram.observe_and_bucket(v); |
258 | 0 | if let (Some(bucket), Some(label_set)) = (bucket, label_set) { |
259 | 0 | inner.exemplars.insert( |
260 | 0 | bucket, |
261 | 0 | Exemplar { |
262 | 0 | label_set, |
263 | 0 | value: v, |
264 | 0 | timestamp, |
265 | 0 | }, |
266 | 0 | ); |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | 0 | pub(crate) fn inner(&self) -> RwLockReadGuard<'_, HistogramWithExemplarsInner<S>> { |
271 | 0 | self.inner.read() |
272 | 0 | } |
273 | | } |
274 | | |
275 | | impl<S: EncodeLabelSet> EncodeMetric for HistogramWithExemplars<S> { |
276 | 0 | fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> { |
277 | 0 | let inner = self.inner(); |
278 | 0 | let (sum, count, buckets) = inner.histogram.get(); |
279 | 0 | encoder.encode_histogram(sum, count, &buckets, Some(&inner.exemplars)) |
280 | 0 | } |
281 | | |
282 | 0 | fn metric_type(&self) -> MetricType { |
283 | 0 | Histogram::TYPE |
284 | 0 | } |
285 | | } |