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