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    pub(crate) fn write_str_unescaped(&mut self, s: &str) -> Result<(), std::fmt::Error> {
458        self.label_value.push_str(s);
459        Ok(())
460    }
461}
462
463impl std::fmt::Write for LabelValueEncoder<'_> {
464    fn write_str(&mut self, s: &str) -> std::fmt::Result {
465        self.label_value.write_str(s)
466    }
467}
468
469#[cfg(test)]
470#[allow(deprecated)]
471mod tests {
472    use prost_types::Timestamp;
473
474    use super::*;
475    use crate::metrics::counter::Counter;
476    use crate::metrics::exemplar::{CounterWithExemplar, HistogramWithExemplars};
477    use crate::metrics::family::Family;
478    use crate::metrics::gauge::Gauge;
479    use crate::metrics::histogram::{exponential_buckets, Histogram, NativeHistogramConfig};
480    use crate::metrics::info::Info;
481    use crate::registry::Unit;
482    use std::borrow::Cow;
483    use std::collections::HashSet;
484    use std::sync::atomic::AtomicI64;
485    use std::sync::atomic::AtomicU64;
486    use std::time::SystemTime;
487
488    #[test]
489    fn encode_counter_int() {
490        let counter: Counter = Counter::default();
491        let mut registry = Registry::default();
492        registry.register("my_counter", "My counter", counter.clone());
493        counter.inc();
494
495        let metric_set = encode(&registry).unwrap();
496
497        let family = metric_set.metric_families.first().unwrap();
498        assert_eq!("my_counter", family.name);
499        assert_eq!("My counter.", family.help);
500
501        assert_eq!(
502            openmetrics_data_model::MetricType::Counter as i32,
503            extract_metric_type(&metric_set)
504        );
505
506        match extract_metric_point_value(&metric_set) {
507            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
508                let expected = openmetrics_data_model::counter_value::Total::IntValue(1);
509                assert_eq!(Some(expected), value.total);
510                assert_eq!(None, value.exemplar);
511                assert_eq!(None, value.created);
512            }
513            _ => panic!("wrong value type"),
514        }
515    }
516
517    #[test]
518    fn encode_counter_double() {
519        // Using `f64`
520        let counter: Counter<f64> = Counter::default();
521        let mut registry = Registry::default();
522        registry.register("my_counter", "My counter", counter.clone());
523        counter.inc();
524
525        let metric_set = encode(&registry).unwrap();
526
527        let family = metric_set.metric_families.first().unwrap();
528        assert_eq!("my_counter", family.name);
529        assert_eq!("My counter.", family.help);
530
531        assert_eq!(
532            openmetrics_data_model::MetricType::Counter as i32,
533            extract_metric_type(&metric_set)
534        );
535
536        match extract_metric_point_value(&metric_set) {
537            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
538                // The counter should be encoded  as `DoubleValue`
539                let expected = openmetrics_data_model::counter_value::Total::DoubleValue(1.0);
540                assert_eq!(Some(expected), value.total);
541                assert_eq!(None, value.exemplar);
542                assert_eq!(None, value.created);
543            }
544            _ => panic!("wrong value type"),
545        }
546    }
547
548    #[test]
549    fn encode_counter_with_unit() {
550        let mut registry = Registry::default();
551        let counter: Counter = Counter::default();
552        registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter);
553
554        let metric_set = encode(&registry).unwrap();
555
556        let family = metric_set.metric_families.first().unwrap();
557        assert_eq!("my_counter", family.name);
558        assert_eq!("My counter.", family.help);
559        assert_eq!("seconds", family.unit);
560    }
561
562    #[test]
563    fn encode_counter_with_exemplar() {
564        let now = SystemTime::now();
565        let now_ts: Timestamp = now.into();
566
567        let mut registry = Registry::default();
568
569        let counter_with_exemplar: CounterWithExemplar<Vec<(String, f64)>, f64> =
570            CounterWithExemplar::default();
571        registry.register(
572            "my_counter_with_exemplar",
573            "My counter with exemplar",
574            counter_with_exemplar.clone(),
575        );
576
577        counter_with_exemplar.inc_by(1.0, Some(vec![("user_id".to_string(), 42.0)]), None);
578
579        let metric_set = encode(&registry).unwrap();
580
581        let family = metric_set.metric_families.first().unwrap();
582        assert_eq!("my_counter_with_exemplar", family.name);
583        assert_eq!("My counter with exemplar.", family.help);
584
585        assert_eq!(
586            openmetrics_data_model::MetricType::Counter as i32,
587            extract_metric_type(&metric_set)
588        );
589
590        match extract_metric_point_value(&metric_set) {
591            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
592                // The counter should be encoded  as `DoubleValue`
593                let expected = openmetrics_data_model::counter_value::Total::DoubleValue(1.0);
594                assert_eq!(Some(expected), value.total);
595
596                let exemplar = value.exemplar.as_ref().unwrap();
597                assert_eq!(1.0, exemplar.value);
598
599                assert!(exemplar.timestamp.is_none());
600
601                let expected_label = {
602                    openmetrics_data_model::Label {
603                        name: "user_id".to_string(),
604                        value: "42.0".to_string(),
605                    }
606                };
607                assert_eq!(vec![expected_label], exemplar.label);
608            }
609            _ => panic!("wrong value type"),
610        }
611
612        counter_with_exemplar.inc_by(1.0, Some(vec![("user_id".to_string(), 99.0)]), Some(now));
613
614        match extract_metric_point_value(&encode(&registry).unwrap()) {
615            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
616                // The counter should be encoded  as `DoubleValue`
617                let expected = openmetrics_data_model::counter_value::Total::DoubleValue(2.0);
618                assert_eq!(Some(expected), value.total);
619
620                let exemplar = value.exemplar.as_ref().unwrap();
621                assert_eq!(1.0, exemplar.value);
622
623                assert_eq!(&now_ts, exemplar.timestamp.as_ref().unwrap());
624
625                let expected_label = {
626                    openmetrics_data_model::Label {
627                        name: "user_id".to_string(),
628                        value: "99.0".to_string(),
629                    }
630                };
631                assert_eq!(vec![expected_label], exemplar.label);
632            }
633            _ => panic!("wrong value type"),
634        }
635    }
636
637    #[test]
638    fn encode_gauge() {
639        let mut registry = Registry::default();
640        let gauge = Gauge::<i64, AtomicI64>::default();
641        registry.register("my_gauge", "My gauge", gauge.clone());
642        gauge.inc();
643
644        let metric_set = encode(&registry).unwrap();
645        let family = metric_set.metric_families.first().unwrap();
646        assert_eq!("my_gauge", family.name);
647        assert_eq!("My gauge.", family.help);
648
649        assert_eq!(
650            openmetrics_data_model::MetricType::Gauge as i32,
651            extract_metric_type(&metric_set)
652        );
653
654        match extract_metric_point_value(&metric_set) {
655            openmetrics_data_model::metric_point::Value::GaugeValue(value) => {
656                let expected = openmetrics_data_model::gauge_value::Value::IntValue(1);
657                assert_eq!(Some(expected), value.value);
658            }
659            _ => panic!("wrong value type"),
660        }
661    }
662
663    #[test]
664    fn encode_gauge_u64_normal() {
665        let mut registry = Registry::default();
666        let gauge = Gauge::<u64, AtomicU64>::default();
667        registry.register("my_gauge", "My gauge", gauge.clone());
668        gauge.set(12345);
669
670        let metric_set = encode(&registry).unwrap();
671        let family = metric_set.metric_families.first().unwrap();
672        assert_eq!("my_gauge", family.name);
673        assert_eq!("My gauge.", family.help);
674
675        assert_eq!(
676            openmetrics_data_model::MetricType::Gauge as i32,
677            extract_metric_type(&metric_set)
678        );
679
680        match extract_metric_point_value(&metric_set) {
681            openmetrics_data_model::metric_point::Value::GaugeValue(value) => {
682                let expected = openmetrics_data_model::gauge_value::Value::IntValue(12345);
683                assert_eq!(Some(expected), value.value);
684            }
685            _ => panic!("wrong value type"),
686        }
687    }
688
689    #[test]
690    fn encode_gauge_u64_max() {
691        let mut registry = Registry::default();
692        let gauge = Gauge::<u64, AtomicU64>::default();
693        registry.register("my_gauge", "My gauge", gauge.clone());
694        gauge.set(u64::MAX);
695
696        // This expected to fail as protobuf uses i64 and u64::MAX does not fit into it.
697        assert!(encode(&registry).is_err());
698    }
699
700    #[test]
701    fn encode_counter_family() {
702        let mut registry = Registry::default();
703        let family = Family::<Vec<(String, String)>, Counter>::default();
704        registry.register("my_counter_family", "My counter family", family.clone());
705
706        family
707            .get_or_create(&vec![
708                ("method".to_string(), "GET".to_string()),
709                ("status".to_string(), "200".to_string()),
710            ])
711            .inc();
712
713        family
714            .get_or_create(&vec![
715                ("method".to_string(), "POST".to_string()),
716                ("status".to_string(), "200".to_string()),
717            ])
718            .inc();
719
720        let metric_set = encode(&registry).unwrap();
721
722        let family = metric_set.metric_families.first().unwrap();
723        assert_eq!("my_counter_family", family.name);
724        assert_eq!("My counter family.", family.help);
725
726        assert_eq!(
727            openmetrics_data_model::MetricType::Counter as i32,
728            extract_metric_type(&metric_set)
729        );
730
731        // The order of the labels is not deterministic so we are testing the
732        // value to be either
733        let mut potential_method_value = HashSet::new();
734        potential_method_value.insert("GET");
735        potential_method_value.insert("POST");
736
737        // the first metric
738        let metric = family.metrics.first().unwrap();
739        assert_eq!(2, metric.labels.len());
740        assert_eq!("method", metric.labels[0].name);
741        assert!(potential_method_value.remove(&metric.labels[0].value.as_str()));
742        assert_eq!("status", metric.labels[1].name);
743        assert_eq!("200", metric.labels[1].value);
744
745        match extract_metric_point_value(&metric_set) {
746            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
747                let expected = openmetrics_data_model::counter_value::Total::IntValue(1);
748                assert_eq!(Some(expected), value.total);
749                assert_eq!(None, value.exemplar);
750                assert_eq!(None, value.created);
751            }
752            _ => panic!("wrong value type"),
753        }
754
755        // the second metric
756        let metric2 = &family.metrics[1];
757        assert_eq!(2, metric2.labels.len());
758        assert_eq!("method", metric2.labels[0].name);
759        assert!(potential_method_value.remove(&metric2.labels[0].value.as_str()));
760        assert_eq!("status", metric2.labels[1].name);
761        assert_eq!("200", metric2.labels[1].value);
762    }
763
764    #[test]
765    fn encode_counter_family_with_prefix_with_label() {
766        let mut registry = Registry::default();
767        let sub_registry = registry.sub_registry_with_prefix("my_prefix");
768        let sub_sub_registry = sub_registry
769            .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
770        let family = Family::<Vec<(String, String)>, Counter>::default();
771        sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
772
773        family
774            .get_or_create(&vec![
775                ("method".to_string(), "GET".to_string()),
776                ("status".to_string(), "200".to_string()),
777            ])
778            .inc();
779
780        let metric_set = encode(&registry).unwrap();
781
782        let family = metric_set.metric_families.first().unwrap();
783        assert_eq!("my_prefix_my_counter_family", family.name);
784        assert_eq!("My counter family.", family.help);
785
786        assert_eq!(
787            openmetrics_data_model::MetricType::Counter as i32,
788            extract_metric_type(&metric_set)
789        );
790
791        let metric = family.metrics.first().unwrap();
792        assert_eq!(3, metric.labels.len());
793        assert_eq!("my_key", metric.labels[0].name);
794        assert_eq!("my_value", metric.labels[0].value);
795        assert_eq!("method", metric.labels[1].name);
796        assert_eq!("GET", metric.labels[1].value);
797        assert_eq!("status", metric.labels[2].name);
798        assert_eq!("200", metric.labels[2].value);
799
800        match extract_metric_point_value(&metric_set) {
801            openmetrics_data_model::metric_point::Value::CounterValue(value) => {
802                let expected = openmetrics_data_model::counter_value::Total::IntValue(1);
803                assert_eq!(Some(expected), value.total);
804                assert_eq!(None, value.exemplar);
805                assert_eq!(None, value.created);
806            }
807            _ => panic!("wrong value type"),
808        }
809    }
810
811    #[test]
812    fn encode_histogram() {
813        let mut registry = Registry::default();
814        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
815        registry.register("my_histogram", "My histogram", histogram.clone());
816        histogram.observe(1.0);
817
818        let metric_set = encode(&registry).unwrap();
819
820        let family = metric_set.metric_families.first().unwrap();
821        assert_eq!("my_histogram", family.name);
822        assert_eq!("My histogram.", family.help);
823
824        assert_eq!(
825            openmetrics_data_model::MetricType::Histogram as i32,
826            extract_metric_type(&metric_set)
827        );
828
829        match extract_metric_point_value(&metric_set) {
830            openmetrics_data_model::metric_point::Value::HistogramValue(value) => {
831                assert_eq!(
832                    Some(openmetrics_data_model::histogram_value::Sum::DoubleValue(
833                        1.0
834                    )),
835                    value.sum
836                );
837                assert_eq!(1, value.count);
838                assert_eq!(11, value.buckets.len());
839            }
840            _ => panic!("wrong value type"),
841        }
842    }
843
844    #[test]
845    fn encode_native_only_histogram_errors() {
846        let mut registry = Registry::default();
847        let histogram = Histogram::new_native(NativeHistogramConfig::with_schema(0));
848        registry.register("my_histogram", "My histogram", histogram.clone());
849        histogram.observe(1.0);
850
851        assert!(encode(&registry).is_err());
852    }
853
854    #[test]
855    fn encode_histogram_with_exemplars() {
856        let now = SystemTime::now();
857        let now_ts: Timestamp = now.into();
858
859        let mut registry = Registry::default();
860        let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
861        registry.register("my_histogram", "My histogram", histogram.clone());
862
863        histogram.observe(1.0, Some(vec![("user_id".to_string(), 42u64)]), None);
864
865        let metric_set = encode(&registry).unwrap();
866
867        let family = metric_set.metric_families.first().unwrap();
868        assert_eq!("my_histogram", family.name);
869        assert_eq!("My histogram.", family.help);
870
871        assert_eq!(
872            openmetrics_data_model::MetricType::Histogram as i32,
873            extract_metric_type(&metric_set)
874        );
875
876        match extract_metric_point_value(&metric_set) {
877            openmetrics_data_model::metric_point::Value::HistogramValue(value) => {
878                let exemplar = value.buckets.first().unwrap().exemplar.as_ref().unwrap();
879                assert_eq!(1.0, exemplar.value);
880
881                assert!(exemplar.timestamp.is_none());
882
883                let expected_label = {
884                    openmetrics_data_model::Label {
885                        name: "user_id".to_string(),
886                        value: "42".to_string(),
887                    }
888                };
889                assert_eq!(vec![expected_label], exemplar.label);
890            }
891            _ => panic!("wrong value type"),
892        }
893
894        histogram.observe(2.0, Some(vec![("user_id".to_string(), 99u64)]), Some(now));
895
896        match extract_metric_point_value(&encode(&registry).unwrap()) {
897            openmetrics_data_model::metric_point::Value::HistogramValue(value) => {
898                let exemplar = value.buckets.get(1).unwrap().exemplar.as_ref().unwrap();
899                assert_eq!(2.0, exemplar.value);
900
901                assert_eq!(&now_ts, exemplar.timestamp.as_ref().unwrap());
902
903                let expected_label = {
904                    openmetrics_data_model::Label {
905                        name: "user_id".to_string(),
906                        value: "99".to_string(),
907                    }
908                };
909                assert_eq!(vec![expected_label], exemplar.label);
910            }
911            _ => panic!("wrong value type"),
912        }
913    }
914
915    #[test]
916    fn encode_family_counter_histogram() {
917        let mut registry = Registry::default();
918
919        let counter_family = Family::<Vec<(String, String)>, Counter>::default();
920        let histogram_family =
921            Family::<Vec<(String, String)>, Histogram>::new_with_constructor(|| {
922                Histogram::new(exponential_buckets(1.0, 2.0, 10))
923            });
924
925        registry.register("my_counter", "My counter", counter_family.clone());
926        registry.register("my_histogram", "My histogram", histogram_family.clone());
927
928        counter_family
929            .get_or_create(&vec![("path".to_string(), "/".to_string())])
930            .inc();
931
932        histogram_family
933            .get_or_create(&vec![("path".to_string(), "/".to_string())])
934            .observe(1.0);
935
936        let metric_set = encode(&registry).unwrap();
937        assert_eq!("my_counter", metric_set.metric_families[0].name);
938        assert_eq!("my_histogram", metric_set.metric_families[1].name);
939    }
940
941    #[test]
942    fn encode_family_and_counter_and_histogram() {
943        let mut registry = Registry::default();
944
945        // Family
946        let counter_family = Family::<Vec<(String, String)>, Counter>::default();
947        let histogram_family =
948            Family::<Vec<(String, String)>, Histogram>::new_with_constructor(|| {
949                Histogram::new(exponential_buckets(1.0, 2.0, 10))
950            });
951
952        registry.register("my_family_counter", "My counter", counter_family.clone());
953        registry.register(
954            "my_family_histogram",
955            "My histogram",
956            histogram_family.clone(),
957        );
958
959        counter_family
960            .get_or_create(&vec![("path".to_string(), "/".to_string())])
961            .inc();
962
963        histogram_family
964            .get_or_create(&vec![("path".to_string(), "/".to_string())])
965            .observe(1.0);
966
967        // Counter
968        let counter: Counter = Counter::default();
969        registry.register("my_counter", "My counter", counter.clone());
970        counter.inc();
971
972        // Histogram
973        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
974        registry.register("my_histogram", "My histogram", histogram.clone());
975        histogram.observe(1.0);
976
977        let metric_set = encode(&registry).unwrap();
978        assert_eq!("my_family_counter", metric_set.metric_families[0].name);
979        assert_eq!("my_family_histogram", metric_set.metric_families[1].name);
980    }
981
982    #[test]
983    fn encode_info() {
984        let mut registry = Registry::default();
985        let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
986        registry.register("my_info_metric", "My info metric", info);
987
988        let metric_set = encode(&registry).unwrap();
989
990        let family = metric_set.metric_families.first().unwrap();
991        assert_eq!("my_info_metric", family.name);
992        assert_eq!("My info metric.", family.help);
993
994        assert_eq!(
995            openmetrics_data_model::MetricType::Info as i32,
996            extract_metric_type(&metric_set)
997        );
998
999        match extract_metric_point_value(&metric_set) {
1000            openmetrics_data_model::metric_point::Value::InfoValue(value) => {
1001                assert_eq!(1, value.info.len());
1002
1003                let info = value.info.first().unwrap();
1004                assert_eq!("os", info.name);
1005                assert_eq!("GNU/linux", info.value);
1006            }
1007            _ => panic!("wrong value type"),
1008        }
1009    }
1010
1011    fn extract_metric_type(metric_set: &openmetrics_data_model::MetricSet) -> i32 {
1012        let family = metric_set.metric_families.first().unwrap();
1013        family.r#type
1014    }
1015
1016    fn extract_metric_point_value(
1017        metric_set: &openmetrics_data_model::MetricSet,
1018    ) -> openmetrics_data_model::metric_point::Value {
1019        let metric = metric_set
1020            .metric_families
1021            .first()
1022            .unwrap()
1023            .metrics
1024            .first()
1025            .unwrap();
1026
1027        metric
1028            .metric_points
1029            .first()
1030            .unwrap()
1031            .value
1032            .as_ref()
1033            .unwrap()
1034            .clone()
1035    }
1036}