Skip to main content

prometheus_client/encoding/
openmetrics_protobuf.rs

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