Skip to main content

prometheus_client/encoding/
prometheus_protobuf.rs

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