prometheus_client/encoding/
protobuf.rs

1//! Open Metrics protobuf implementation.
2//!
3//! ```
4//! # use prometheus_client::encoding::protobuf::encode;
5//! # use prometheus_client::metrics::counter::Counter;
6//! # use prometheus_client::registry::Registry;
7//! #
8//! # // Create registry and counter and register the latter with the former.
9//! # let mut registry = Registry::default();
10//! # let counter: Counter = Counter::default();
11//! # registry.register(
12//! #   "my_counter",
13//! #   "This is my counter",
14//! #   counter.clone(),
15//! # );
16//! # counter.inc();
17//! // Returns `MetricSet`, the top-level container type. Please refer to [openmetrics_data_model.proto](https://github.com/prometheus/OpenMetrics/blob/v1.0.0/proto/openmetrics_data_model.proto) for details.
18//! let metric_set = encode(&registry).unwrap();
19//!
20//! let family = metric_set.metric_families.first().unwrap();
21//! assert_eq!("my_counter", family.name);
22//! assert_eq!("This is my counter.", family.help);
23//! ```
24
25// Allowing some lints here as the `openmetrics.rs` is an automatically generated file.
26#[allow(missing_docs, clippy::derive_partial_eq_without_eq)]
27/// Data models that are automatically generated from OpenMetrics protobuf
28/// format.
29pub mod openmetrics_data_model {
30    include!(concat!(env!("OUT_DIR"), "/openmetrics.rs"));
31}
32
33use std::{borrow::Cow, collections::HashMap};
34
35use crate::metrics::MetricType;
36use crate::registry::{Registry, Unit};
37use crate::{metrics::exemplar::Exemplar, registry::Prefix};
38
39use super::{EncodeCounterValue, EncodeExemplarValue, EncodeGaugeValue, EncodeLabelSet};
40
41/// Encode the metrics registered with the provided [`Registry`] into MetricSet
42/// using the OpenMetrics protobuf format.
43pub fn encode(registry: &Registry) -> Result<openmetrics_data_model::MetricSet, std::fmt::Error> {
44    let mut metric_set = openmetrics_data_model::MetricSet::default();
45    let mut descriptor_encoder = DescriptorEncoder::new(&mut metric_set.metric_families).into();
46    registry.encode(&mut descriptor_encoder)?;
47    Ok(metric_set)
48}
49
50impl From<MetricType> for openmetrics_data_model::MetricType {
51    fn from(m: MetricType) -> Self {
52        match m {
53            MetricType::Counter => openmetrics_data_model::MetricType::Counter,
54            MetricType::Gauge => openmetrics_data_model::MetricType::Gauge,
55            MetricType::Histogram => openmetrics_data_model::MetricType::Histogram,
56            MetricType::Info => openmetrics_data_model::MetricType::Info,
57            MetricType::Unknown => openmetrics_data_model::MetricType::Unknown,
58        }
59    }
60}
61
62/// Metric Descriptor encoder for protobuf encoding.
63///
64/// This is an inner type for [`super::DescriptorEncoder`].
65#[derive(Debug)]
66pub(crate) struct DescriptorEncoder<'a> {
67    metric_families: &'a mut Vec<openmetrics_data_model::MetricFamily>,
68    prefix: Option<&'a Prefix>,
69    labels: &'a [(Cow<'static, str>, Cow<'static, str>)],
70}
71
72impl DescriptorEncoder<'_> {
73    pub(crate) fn new(
74        metric_families: &mut Vec<openmetrics_data_model::MetricFamily>,
75    ) -> DescriptorEncoder<'_> {
76        DescriptorEncoder {
77            metric_families,
78            prefix: Default::default(),
79            labels: Default::default(),
80        }
81    }
82
83    pub(crate) fn with_prefix_and_labels<'s>(
84        &'s mut self,
85        prefix: Option<&'s Prefix>,
86        labels: &'s [(Cow<'static, str>, Cow<'static, str>)],
87    ) -> DescriptorEncoder<'s> {
88        DescriptorEncoder {
89            prefix,
90            labels,
91            metric_families: self.metric_families,
92        }
93    }
94
95    pub fn encode_descriptor<'s>(
96        &'s mut self,
97        name: &str,
98        help: &str,
99        unit: Option<&Unit>,
100        metric_type: MetricType,
101    ) -> Result<MetricEncoder<'s>, std::fmt::Error> {
102        let family = openmetrics_data_model::MetricFamily {
103            name: {
104                match self.prefix {
105                    Some(prefix) => prefix.as_str().to_string() + "_" + name,
106                    None => name.to_string(),
107                }
108            },
109            r#type: {
110                let metric_type: openmetrics_data_model::MetricType = metric_type.into();
111                metric_type as i32
112            },
113            unit: if let Some(unit) = unit {
114                unit.as_str().to_string()
115            } else {
116                String::new()
117            },
118            help: help.to_string(),
119            ..Default::default()
120        };
121        let mut labels = vec![];
122        self.labels.encode(
123            &mut LabelSetEncoder {
124                labels: &mut labels,
125            }
126            .into(),
127        )?;
128        self.metric_families.push(family);
129
130        Ok(MetricEncoder {
131            family: &mut self
132                .metric_families
133                .last_mut()
134                .expect("previous push")
135                .metrics,
136            metric_type,
137            labels,
138        })
139    }
140}
141
142/// Encoder for protobuf encoding.
143///
144/// This is an inner type for [`super::MetricEncoder`].
145#[derive(Debug)]
146pub(crate) struct MetricEncoder<'f> {
147    /// OpenMetrics metric type of the metric.
148    metric_type: MetricType,
149    /// Vector of OpenMetrics metrics to which encoded metrics are added.
150    family: &'f mut Vec<openmetrics_data_model::Metric>,
151    /// Labels to be added to each metric.
152    labels: Vec<openmetrics_data_model::Label>,
153}
154
155impl MetricEncoder<'_> {
156    pub fn encode_counter<
157        S: EncodeLabelSet,
158        CounterValue: EncodeCounterValue,
159        ExemplarValue: EncodeExemplarValue,
160    >(
161        &mut self,
162        v: &CounterValue,
163        exemplar: Option<&Exemplar<S, ExemplarValue>>,
164    ) -> Result<(), std::fmt::Error> {
165        let mut value = openmetrics_data_model::counter_value::Total::IntValue(0);
166        let mut e = CounterValueEncoder { value: &mut value }.into();
167        v.encode(&mut e)?;
168
169        self.family.push(openmetrics_data_model::Metric {
170            labels: self.labels.clone(),
171            metric_points: vec![openmetrics_data_model::MetricPoint {
172                value: Some(openmetrics_data_model::metric_point::Value::CounterValue(
173                    openmetrics_data_model::CounterValue {
174                        total: Some(value),
175                        exemplar: exemplar.map(|e| e.try_into()).transpose()?,
176                        ..Default::default()
177                    },
178                )),
179                ..Default::default()
180            }],
181        });
182
183        Ok(())
184    }
185
186    pub fn encode_gauge<GaugeValue: EncodeGaugeValue>(
187        &mut self,
188        v: &GaugeValue,
189    ) -> Result<(), std::fmt::Error> {
190        let mut value = openmetrics_data_model::gauge_value::Value::IntValue(0);
191        let mut e = GaugeValueEncoder { value: &mut value }.into();
192        v.encode(&mut e)?;
193
194        self.family.push(openmetrics_data_model::Metric {
195            labels: self.labels.clone(),
196            metric_points: vec![openmetrics_data_model::MetricPoint {
197                value: Some(openmetrics_data_model::metric_point::Value::GaugeValue(
198                    openmetrics_data_model::GaugeValue { value: Some(value) },
199                )),
200                ..Default::default()
201            }],
202        });
203
204        Ok(())
205    }
206
207    pub fn encode_info(
208        &mut self,
209        label_set: &impl super::EncodeLabelSet,
210    ) -> Result<(), std::fmt::Error> {
211        let mut info_labels = vec![];
212        label_set.encode(
213            &mut LabelSetEncoder {
214                labels: &mut info_labels,
215            }
216            .into(),
217        )?;
218
219        self.family.push(openmetrics_data_model::Metric {
220            labels: self.labels.clone(),
221            metric_points: vec![openmetrics_data_model::MetricPoint {
222                value: Some(openmetrics_data_model::metric_point::Value::InfoValue(
223                    openmetrics_data_model::InfoValue { info: info_labels },
224                )),
225                ..Default::default()
226            }],
227        });
228
229        Ok(())
230    }
231
232    pub fn encode_family<S: EncodeLabelSet>(
233        &mut self,
234        label_set: &S,
235    ) -> Result<MetricEncoder<'_>, std::fmt::Error> {
236        let mut labels = self.labels.clone();
237        label_set.encode(
238            &mut LabelSetEncoder {
239                labels: &mut labels,
240            }
241            .into(),
242        )?;
243
244        Ok(MetricEncoder {
245            metric_type: self.metric_type,
246            family: self.family,
247            labels,
248        })
249    }
250
251    pub fn encode_histogram<S: EncodeLabelSet>(
252        &mut self,
253        sum: f64,
254        count: u64,
255        buckets: &[(f64, u64)],
256        exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
257    ) -> Result<(), std::fmt::Error> {
258        let buckets = buckets
259            .iter()
260            .enumerate()
261            .map(|(i, (upper_bound, count))| {
262                Ok(openmetrics_data_model::histogram_value::Bucket {
263                    upper_bound: *upper_bound,
264                    count: *count,
265                    exemplar: exemplars
266                        .and_then(|exemplars| exemplars.get(&i).map(|exemplar| exemplar.try_into()))
267                        .transpose()?,
268                })
269            })
270            .collect::<Result<Vec<_>, std::fmt::Error>>()?;
271
272        self.family.push(openmetrics_data_model::Metric {
273            labels: self.labels.clone(),
274            metric_points: vec![openmetrics_data_model::MetricPoint {
275                value: Some(openmetrics_data_model::metric_point::Value::HistogramValue(
276                    openmetrics_data_model::HistogramValue {
277                        count,
278                        created: None,
279                        buckets,
280                        sum: Some(openmetrics_data_model::histogram_value::Sum::DoubleValue(
281                            sum,
282                        )),
283                    },
284                )),
285                ..Default::default()
286            }],
287        });
288
289        Ok(())
290    }
291}
292
293impl<S: EncodeLabelSet, V: EncodeExemplarValue> TryFrom<&Exemplar<S, V>>
294    for openmetrics_data_model::Exemplar
295{
296    type Error = std::fmt::Error;
297
298    fn try_from(exemplar: &Exemplar<S, V>) -> Result<Self, Self::Error> {
299        let mut value = f64::default();
300        exemplar
301            .value
302            .encode(ExemplarValueEncoder { value: &mut value }.into())?;
303
304        let mut labels = vec![];
305        exemplar.label_set.encode(
306            &mut LabelSetEncoder {
307                labels: &mut labels,
308            }
309            .into(),
310        )?;
311
312        Ok(openmetrics_data_model::Exemplar {
313            value,
314            timestamp: exemplar.timestamp.map(Into::into),
315            label: labels,
316        })
317    }
318}
319
320#[derive(Debug)]
321pub(crate) struct GaugeValueEncoder<'a> {
322    value: &'a mut openmetrics_data_model::gauge_value::Value,
323}
324
325impl GaugeValueEncoder<'_> {
326    pub fn encode_u32(&mut self, v: u32) -> Result<(), std::fmt::Error> {
327        self.encode_i64(v as i64)
328    }
329
330    pub fn encode_i64(&mut self, v: i64) -> Result<(), std::fmt::Error> {
331        *self.value = openmetrics_data_model::gauge_value::Value::IntValue(v);
332        Ok(())
333    }
334
335    pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
336        *self.value = openmetrics_data_model::gauge_value::Value::DoubleValue(v);
337        Ok(())
338    }
339}
340
341#[derive(Debug)]
342pub(crate) struct ExemplarValueEncoder<'a> {
343    value: &'a mut f64,
344}
345
346impl ExemplarValueEncoder<'_> {
347    pub fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> {
348        *self.value = v;
349        Ok(())
350    }
351}
352
353impl<K: ToString, V: ToString> From<&(K, V)> for openmetrics_data_model::Label {
354    fn from(kv: &(K, V)) -> Self {
355        openmetrics_data_model::Label {
356            name: kv.0.to_string(),
357            value: kv.1.to_string(),
358        }
359    }
360}
361
362#[derive(Debug)]
363pub(crate) struct CounterValueEncoder<'a> {
364    value: &'a mut openmetrics_data_model::counter_value::Total,
365}
366
367impl CounterValueEncoder<'_> {
368    pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
369        *self.value = openmetrics_data_model::counter_value::Total::DoubleValue(v);
370        Ok(())
371    }
372
373    pub fn encode_u64(&mut self, v: u64) -> Result<(), std::fmt::Error> {
374        *self.value = openmetrics_data_model::counter_value::Total::IntValue(v);
375        Ok(())
376    }
377}
378
379#[derive(Debug)]
380pub(crate) struct LabelSetEncoder<'a> {
381    labels: &'a mut Vec<openmetrics_data_model::Label>,
382}
383
384impl LabelSetEncoder<'_> {
385    pub fn encode_label(&mut self) -> LabelEncoder<'_> {
386        LabelEncoder {
387            labels: self.labels,
388        }
389    }
390}
391
392#[derive(Debug)]
393pub(crate) struct LabelEncoder<'a> {
394    labels: &'a mut Vec<openmetrics_data_model::Label>,
395}
396
397impl LabelEncoder<'_> {
398    pub fn encode_label_key(&mut self) -> Result<LabelKeyEncoder<'_>, std::fmt::Error> {
399        self.labels.push(openmetrics_data_model::Label::default());
400
401        Ok(LabelKeyEncoder {
402            label: self.labels.last_mut().expect("To find pushed label."),
403        })
404    }
405}
406
407#[derive(Debug)]
408pub(crate) struct LabelKeyEncoder<'a> {
409    label: &'a mut openmetrics_data_model::Label,
410}
411
412impl std::fmt::Write for LabelKeyEncoder<'_> {
413    fn write_str(&mut self, s: &str) -> std::fmt::Result {
414        self.label.name.write_str(s)
415    }
416}
417
418impl<'a> LabelKeyEncoder<'a> {
419    pub fn encode_label_value(self) -> Result<LabelValueEncoder<'a>, std::fmt::Error> {
420        Ok(LabelValueEncoder {
421            label_value: &mut self.label.value,
422        })
423    }
424}
425
426#[derive(Debug)]
427pub(crate) struct LabelValueEncoder<'a> {
428    label_value: &'a mut String,
429}
430
431impl LabelValueEncoder<'_> {
432    pub fn finish(self) -> Result<(), std::fmt::Error> {
433        Ok(())
434    }
435}
436
437impl std::fmt::Write for LabelValueEncoder<'_> {
438    fn write_str(&mut self, s: &str) -> std::fmt::Result {
439        self.label_value.write_str(s)
440    }
441}
442
443#[cfg(test)]
444mod tests {
445    use prost_types::Timestamp;
446
447    use super::*;
448    use crate::metrics::counter::Counter;
449    use crate::metrics::exemplar::{CounterWithExemplar, HistogramWithExemplars};
450    use crate::metrics::family::Family;
451    use crate::metrics::gauge::Gauge;
452    use crate::metrics::histogram::{exponential_buckets, Histogram};
453    use crate::metrics::info::Info;
454    use crate::registry::Unit;
455    use std::borrow::Cow;
456    use std::collections::HashSet;
457    use std::sync::atomic::AtomicI64;
458    use std::sync::atomic::AtomicU64;
459    use std::time::SystemTime;
460
461    #[test]
462    fn encode_counter_int() {
463        let counter: Counter = Counter::default();
464        let mut registry = Registry::default();
465        registry.register("my_counter", "My counter", counter.clone());
466        counter.inc();
467
468        let metric_set = encode(&registry).unwrap();
469
470        let family = metric_set.metric_families.first().unwrap();
471        assert_eq!("my_counter", family.name);
472        assert_eq!("My counter.", family.help);
473
474        assert_eq!(
475            openmetrics_data_model::MetricType::Counter as i32,
476            extract_metric_type(&metric_set)
477        );
478
479        match extract_metric_point_value(&metric_set) {
480            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
481                let expected = openmetrics_data_model::counter_value::Total::IntValue(1);
482                assert_eq!(Some(expected), value.total);
483                assert_eq!(None, value.exemplar);
484                assert_eq!(None, value.created);
485            }
486            _ => panic!("wrong value type"),
487        }
488    }
489
490    #[test]
491    fn encode_counter_double() {
492        // Using `f64`
493        let counter: Counter<f64> = Counter::default();
494        let mut registry = Registry::default();
495        registry.register("my_counter", "My counter", counter.clone());
496        counter.inc();
497
498        let metric_set = encode(&registry).unwrap();
499
500        let family = metric_set.metric_families.first().unwrap();
501        assert_eq!("my_counter", family.name);
502        assert_eq!("My counter.", family.help);
503
504        assert_eq!(
505            openmetrics_data_model::MetricType::Counter as i32,
506            extract_metric_type(&metric_set)
507        );
508
509        match extract_metric_point_value(&metric_set) {
510            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
511                // The counter should be encoded  as `DoubleValue`
512                let expected = openmetrics_data_model::counter_value::Total::DoubleValue(1.0);
513                assert_eq!(Some(expected), value.total);
514                assert_eq!(None, value.exemplar);
515                assert_eq!(None, value.created);
516            }
517            _ => panic!("wrong value type"),
518        }
519    }
520
521    #[test]
522    fn encode_counter_with_unit() {
523        let mut registry = Registry::default();
524        let counter: Counter = Counter::default();
525        registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter);
526
527        let metric_set = encode(&registry).unwrap();
528
529        let family = metric_set.metric_families.first().unwrap();
530        assert_eq!("my_counter", family.name);
531        assert_eq!("My counter.", family.help);
532        assert_eq!("seconds", family.unit);
533    }
534
535    #[test]
536    fn encode_counter_with_exemplar() {
537        let now = SystemTime::now();
538        let now_ts: Timestamp = now.into();
539
540        let mut registry = Registry::default();
541
542        let counter_with_exemplar: CounterWithExemplar<Vec<(String, f64)>, f64> =
543            CounterWithExemplar::default();
544        registry.register(
545            "my_counter_with_exemplar",
546            "My counter with exemplar",
547            counter_with_exemplar.clone(),
548        );
549
550        counter_with_exemplar.inc_by(1.0, Some(vec![("user_id".to_string(), 42.0)]), None);
551
552        let metric_set = encode(&registry).unwrap();
553
554        let family = metric_set.metric_families.first().unwrap();
555        assert_eq!("my_counter_with_exemplar", family.name);
556        assert_eq!("My counter with exemplar.", family.help);
557
558        assert_eq!(
559            openmetrics_data_model::MetricType::Counter as i32,
560            extract_metric_type(&metric_set)
561        );
562
563        match extract_metric_point_value(&metric_set) {
564            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
565                // The counter should be encoded  as `DoubleValue`
566                let expected = openmetrics_data_model::counter_value::Total::DoubleValue(1.0);
567                assert_eq!(Some(expected), value.total);
568
569                let exemplar = value.exemplar.as_ref().unwrap();
570                assert_eq!(1.0, exemplar.value);
571
572                assert!(exemplar.timestamp.is_none());
573
574                let expected_label = {
575                    openmetrics_data_model::Label {
576                        name: "user_id".to_string(),
577                        value: "42.0".to_string(),
578                    }
579                };
580                assert_eq!(vec![expected_label], exemplar.label);
581            }
582            _ => panic!("wrong value type"),
583        }
584
585        counter_with_exemplar.inc_by(1.0, Some(vec![("user_id".to_string(), 99.0)]), Some(now));
586
587        match extract_metric_point_value(&encode(&registry).unwrap()) {
588            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
589                // The counter should be encoded  as `DoubleValue`
590                let expected = openmetrics_data_model::counter_value::Total::DoubleValue(2.0);
591                assert_eq!(Some(expected), value.total);
592
593                let exemplar = value.exemplar.as_ref().unwrap();
594                assert_eq!(1.0, exemplar.value);
595
596                assert_eq!(&now_ts, exemplar.timestamp.as_ref().unwrap());
597
598                let expected_label = {
599                    openmetrics_data_model::Label {
600                        name: "user_id".to_string(),
601                        value: "99.0".to_string(),
602                    }
603                };
604                assert_eq!(vec![expected_label], exemplar.label);
605            }
606            _ => panic!("wrong value type"),
607        }
608    }
609
610    #[test]
611    fn encode_gauge() {
612        let mut registry = Registry::default();
613        let gauge = Gauge::<i64, AtomicI64>::default();
614        registry.register("my_gauge", "My gauge", gauge.clone());
615        gauge.inc();
616
617        let metric_set = encode(&registry).unwrap();
618        let family = metric_set.metric_families.first().unwrap();
619        assert_eq!("my_gauge", family.name);
620        assert_eq!("My gauge.", family.help);
621
622        assert_eq!(
623            openmetrics_data_model::MetricType::Gauge as i32,
624            extract_metric_type(&metric_set)
625        );
626
627        match extract_metric_point_value(&metric_set) {
628            openmetrics_data_model::metric_point::Value::GaugeValue(value) => {
629                let expected = openmetrics_data_model::gauge_value::Value::IntValue(1);
630                assert_eq!(Some(expected), value.value);
631            }
632            _ => panic!("wrong value type"),
633        }
634    }
635
636    #[test]
637    fn encode_gauge_u64_normal() {
638        let mut registry = Registry::default();
639        let gauge = Gauge::<u64, AtomicU64>::default();
640        registry.register("my_gauge", "My gauge", gauge.clone());
641        gauge.set(12345);
642
643        let metric_set = encode(&registry).unwrap();
644        let family = metric_set.metric_families.first().unwrap();
645        assert_eq!("my_gauge", family.name);
646        assert_eq!("My gauge.", family.help);
647
648        assert_eq!(
649            openmetrics_data_model::MetricType::Gauge as i32,
650            extract_metric_type(&metric_set)
651        );
652
653        match extract_metric_point_value(&metric_set) {
654            openmetrics_data_model::metric_point::Value::GaugeValue(value) => {
655                let expected = openmetrics_data_model::gauge_value::Value::IntValue(12345);
656                assert_eq!(Some(expected), value.value);
657            }
658            _ => panic!("wrong value type"),
659        }
660    }
661
662    #[test]
663    fn encode_gauge_u64_max() {
664        let mut registry = Registry::default();
665        let gauge = Gauge::<u64, AtomicU64>::default();
666        registry.register("my_gauge", "My gauge", gauge.clone());
667        gauge.set(u64::MAX);
668
669        // This expected to fail as protobuf uses i64 and u64::MAX does not fit into it.
670        assert!(encode(&registry).is_err());
671    }
672
673    #[test]
674    fn encode_counter_family() {
675        let mut registry = Registry::default();
676        let family = Family::<Vec<(String, String)>, Counter>::default();
677        registry.register("my_counter_family", "My counter family", family.clone());
678
679        family
680            .get_or_create(&vec![
681                ("method".to_string(), "GET".to_string()),
682                ("status".to_string(), "200".to_string()),
683            ])
684            .inc();
685
686        family
687            .get_or_create(&vec![
688                ("method".to_string(), "POST".to_string()),
689                ("status".to_string(), "200".to_string()),
690            ])
691            .inc();
692
693        let metric_set = encode(&registry).unwrap();
694
695        let family = metric_set.metric_families.first().unwrap();
696        assert_eq!("my_counter_family", family.name);
697        assert_eq!("My counter family.", family.help);
698
699        assert_eq!(
700            openmetrics_data_model::MetricType::Counter as i32,
701            extract_metric_type(&metric_set)
702        );
703
704        // The order of the labels is not deterministic so we are testing the
705        // value to be either
706        let mut potential_method_value = HashSet::new();
707        potential_method_value.insert("GET");
708        potential_method_value.insert("POST");
709
710        // the first metric
711        let metric = family.metrics.first().unwrap();
712        assert_eq!(2, metric.labels.len());
713        assert_eq!("method", metric.labels[0].name);
714        assert!(potential_method_value.remove(&metric.labels[0].value.as_str()));
715        assert_eq!("status", metric.labels[1].name);
716        assert_eq!("200", metric.labels[1].value);
717
718        match extract_metric_point_value(&metric_set) {
719            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
720                let expected = openmetrics_data_model::counter_value::Total::IntValue(1);
721                assert_eq!(Some(expected), value.total);
722                assert_eq!(None, value.exemplar);
723                assert_eq!(None, value.created);
724            }
725            _ => panic!("wrong value type"),
726        }
727
728        // the second metric
729        let metric2 = &family.metrics[1];
730        assert_eq!(2, metric2.labels.len());
731        assert_eq!("method", metric2.labels[0].name);
732        assert!(potential_method_value.remove(&metric2.labels[0].value.as_str()));
733        assert_eq!("status", metric2.labels[1].name);
734        assert_eq!("200", metric2.labels[1].value);
735    }
736
737    #[test]
738    fn encode_counter_family_with_prefix_with_label() {
739        let mut registry = Registry::default();
740        let sub_registry = registry.sub_registry_with_prefix("my_prefix");
741        let sub_sub_registry = sub_registry
742            .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
743        let family = Family::<Vec<(String, String)>, Counter>::default();
744        sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
745
746        family
747            .get_or_create(&vec![
748                ("method".to_string(), "GET".to_string()),
749                ("status".to_string(), "200".to_string()),
750            ])
751            .inc();
752
753        let metric_set = encode(&registry).unwrap();
754
755        let family = metric_set.metric_families.first().unwrap();
756        assert_eq!("my_prefix_my_counter_family", family.name);
757        assert_eq!("My counter family.", family.help);
758
759        assert_eq!(
760            openmetrics_data_model::MetricType::Counter as i32,
761            extract_metric_type(&metric_set)
762        );
763
764        let metric = family.metrics.first().unwrap();
765        assert_eq!(3, metric.labels.len());
766        assert_eq!("my_key", metric.labels[0].name);
767        assert_eq!("my_value", metric.labels[0].value);
768        assert_eq!("method", metric.labels[1].name);
769        assert_eq!("GET", metric.labels[1].value);
770        assert_eq!("status", metric.labels[2].name);
771        assert_eq!("200", metric.labels[2].value);
772
773        match extract_metric_point_value(&metric_set) {
774            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
775                let expected = openmetrics_data_model::counter_value::Total::IntValue(1);
776                assert_eq!(Some(expected), value.total);
777                assert_eq!(None, value.exemplar);
778                assert_eq!(None, value.created);
779            }
780            _ => panic!("wrong value type"),
781        }
782    }
783
784    #[test]
785    fn encode_histogram() {
786        let mut registry = Registry::default();
787        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
788        registry.register("my_histogram", "My histogram", histogram.clone());
789        histogram.observe(1.0);
790
791        let metric_set = encode(&registry).unwrap();
792
793        let family = metric_set.metric_families.first().unwrap();
794        assert_eq!("my_histogram", family.name);
795        assert_eq!("My histogram.", family.help);
796
797        assert_eq!(
798            openmetrics_data_model::MetricType::Histogram as i32,
799            extract_metric_type(&metric_set)
800        );
801
802        match extract_metric_point_value(&metric_set) {
803            openmetrics_data_model::metric_point::Value::HistogramValue(value) => {
804                assert_eq!(
805                    Some(openmetrics_data_model::histogram_value::Sum::DoubleValue(
806                        1.0
807                    )),
808                    value.sum
809                );
810                assert_eq!(1, value.count);
811                assert_eq!(11, value.buckets.len());
812            }
813            _ => panic!("wrong value type"),
814        }
815    }
816
817    #[test]
818    fn encode_histogram_with_exemplars() {
819        let now = SystemTime::now();
820        let now_ts: Timestamp = now.into();
821
822        let mut registry = Registry::default();
823        let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
824        registry.register("my_histogram", "My histogram", histogram.clone());
825
826        histogram.observe(1.0, Some(vec![("user_id".to_string(), 42u64)]), None);
827
828        let metric_set = encode(&registry).unwrap();
829
830        let family = metric_set.metric_families.first().unwrap();
831        assert_eq!("my_histogram", family.name);
832        assert_eq!("My histogram.", family.help);
833
834        assert_eq!(
835            openmetrics_data_model::MetricType::Histogram as i32,
836            extract_metric_type(&metric_set)
837        );
838
839        match extract_metric_point_value(&metric_set) {
840            openmetrics_data_model::metric_point::Value::HistogramValue(value) => {
841                let exemplar = value.buckets.first().unwrap().exemplar.as_ref().unwrap();
842                assert_eq!(1.0, exemplar.value);
843
844                assert!(exemplar.timestamp.is_none());
845
846                let expected_label = {
847                    openmetrics_data_model::Label {
848                        name: "user_id".to_string(),
849                        value: "42".to_string(),
850                    }
851                };
852                assert_eq!(vec![expected_label], exemplar.label);
853            }
854            _ => panic!("wrong value type"),
855        }
856
857        histogram.observe(2.0, Some(vec![("user_id".to_string(), 99u64)]), Some(now));
858
859        match extract_metric_point_value(&encode(&registry).unwrap()) {
860            openmetrics_data_model::metric_point::Value::HistogramValue(value) => {
861                let exemplar = value.buckets.get(1).unwrap().exemplar.as_ref().unwrap();
862                assert_eq!(2.0, exemplar.value);
863
864                assert_eq!(&now_ts, exemplar.timestamp.as_ref().unwrap());
865
866                let expected_label = {
867                    openmetrics_data_model::Label {
868                        name: "user_id".to_string(),
869                        value: "99".to_string(),
870                    }
871                };
872                assert_eq!(vec![expected_label], exemplar.label);
873            }
874            _ => panic!("wrong value type"),
875        }
876    }
877
878    #[test]
879    fn encode_family_counter_histogram() {
880        let mut registry = Registry::default();
881
882        let counter_family = Family::<Vec<(String, String)>, Counter>::default();
883        let histogram_family =
884            Family::<Vec<(String, String)>, Histogram>::new_with_constructor(|| {
885                Histogram::new(exponential_buckets(1.0, 2.0, 10))
886            });
887
888        registry.register("my_counter", "My counter", counter_family.clone());
889        registry.register("my_histogram", "My histogram", histogram_family.clone());
890
891        counter_family
892            .get_or_create(&vec![("path".to_string(), "/".to_string())])
893            .inc();
894
895        histogram_family
896            .get_or_create(&vec![("path".to_string(), "/".to_string())])
897            .observe(1.0);
898
899        let metric_set = encode(&registry).unwrap();
900        assert_eq!("my_counter", metric_set.metric_families[0].name);
901        assert_eq!("my_histogram", metric_set.metric_families[1].name);
902    }
903
904    #[test]
905    fn encode_family_and_counter_and_histogram() {
906        let mut registry = Registry::default();
907
908        // Family
909        let counter_family = Family::<Vec<(String, String)>, Counter>::default();
910        let histogram_family =
911            Family::<Vec<(String, String)>, Histogram>::new_with_constructor(|| {
912                Histogram::new(exponential_buckets(1.0, 2.0, 10))
913            });
914
915        registry.register("my_family_counter", "My counter", counter_family.clone());
916        registry.register(
917            "my_family_histogram",
918            "My histogram",
919            histogram_family.clone(),
920        );
921
922        counter_family
923            .get_or_create(&vec![("path".to_string(), "/".to_string())])
924            .inc();
925
926        histogram_family
927            .get_or_create(&vec![("path".to_string(), "/".to_string())])
928            .observe(1.0);
929
930        // Counter
931        let counter: Counter = Counter::default();
932        registry.register("my_counter", "My counter", counter.clone());
933        counter.inc();
934
935        // Histogram
936        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
937        registry.register("my_histogram", "My histogram", histogram.clone());
938        histogram.observe(1.0);
939
940        let metric_set = encode(&registry).unwrap();
941        assert_eq!("my_family_counter", metric_set.metric_families[0].name);
942        assert_eq!("my_family_histogram", metric_set.metric_families[1].name);
943    }
944
945    #[test]
946    fn encode_info() {
947        let mut registry = Registry::default();
948        let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
949        registry.register("my_info_metric", "My info metric", info);
950
951        let metric_set = encode(&registry).unwrap();
952
953        let family = metric_set.metric_families.first().unwrap();
954        assert_eq!("my_info_metric", family.name);
955        assert_eq!("My info metric.", family.help);
956
957        assert_eq!(
958            openmetrics_data_model::MetricType::Info as i32,
959            extract_metric_type(&metric_set)
960        );
961
962        match extract_metric_point_value(&metric_set) {
963            openmetrics_data_model::metric_point::Value::InfoValue(value) => {
964                assert_eq!(1, value.info.len());
965
966                let info = value.info.first().unwrap();
967                assert_eq!("os", info.name);
968                assert_eq!("GNU/linux", info.value);
969            }
970            _ => panic!("wrong value type"),
971        }
972    }
973
974    fn extract_metric_type(metric_set: &openmetrics_data_model::MetricSet) -> i32 {
975        let family = metric_set.metric_families.first().unwrap();
976        family.r#type
977    }
978
979    fn extract_metric_point_value(
980        metric_set: &openmetrics_data_model::MetricSet,
981    ) -> openmetrics_data_model::metric_point::Value {
982        let metric = metric_set
983            .metric_families
984            .first()
985            .unwrap()
986            .metrics
987            .first()
988            .unwrap();
989
990        metric
991            .metric_points
992            .first()
993            .unwrap()
994            .value
995            .as_ref()
996            .unwrap()
997            .clone()
998    }
999}