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
587impl std::fmt::Write for LabelValueEncoder<'_> {
588    fn write_str(&mut self, s: &str) -> std::fmt::Result {
589        self.label_value.write_str(s)
590    }
591}
592
593#[cfg(test)]
594mod tests {
595    use prost_types::Timestamp;
596
597    use super::*;
598    use crate::metrics::counter::Counter;
599    use crate::metrics::exemplar::{CounterWithExemplar, HistogramWithExemplars};
600    use crate::metrics::family::Family;
601    use crate::metrics::gauge::Gauge;
602    use crate::metrics::histogram::{
603        exponential_buckets, Histogram, NativeHistogramConfig, NATIVE_HISTOGRAM_ZERO_THRESHOLD_ZERO,
604    };
605    use crate::metrics::info::Info;
606    use crate::registry::Unit;
607    use std::borrow::Cow;
608    use std::collections::HashSet;
609    use std::sync::atomic::AtomicI64;
610    use std::sync::atomic::AtomicU64;
611    use std::time::SystemTime;
612
613    #[test]
614    fn encode_counter_int() {
615        let counter: Counter = Counter::default();
616        let mut registry = Registry::default();
617        registry.register("my_counter", "My counter", counter.clone());
618        counter.inc();
619
620        let metric_families = encode(&registry).unwrap();
621        let family = metric_families.first().unwrap();
622        assert_eq!("my_counter_total", family.name);
623        assert_eq!("My counter.", family.help);
624        assert_eq!(
625            prometheus_data_model::MetricType::Counter as i32,
626            family.r#type
627        );
628
629        let metric = family.metric.first().unwrap();
630        assert_eq!(1.0, metric.counter.as_ref().unwrap().value);
631    }
632
633    #[test]
634    fn encode_counter_with_unit() {
635        let counter: Counter = Counter::default();
636        let mut registry = Registry::default();
637        registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter);
638
639        let metric_families = encode(&registry).unwrap();
640        let family = metric_families.first().unwrap();
641        assert_eq!("my_counter_seconds_total", family.name);
642        assert_eq!("seconds", family.unit);
643    }
644
645    #[test]
646    fn encode_counter_with_exemplar() {
647        let now = SystemTime::now();
648        let now_ts: Timestamp = now.into();
649
650        let mut registry = Registry::default();
651        let counter: CounterWithExemplar<Vec<(String, f64)>, f64> = CounterWithExemplar::default();
652        registry.register("my_counter", "My counter", counter.clone());
653
654        counter.inc_by(1.0, Some(vec![("user_id".to_string(), 42.0)]), None);
655
656        let metric_families = encode(&registry).unwrap();
657        let exemplar = metric_families[0].metric[0]
658            .counter
659            .as_ref()
660            .unwrap()
661            .exemplar
662            .as_ref()
663            .unwrap();
664        assert_eq!(1.0, exemplar.value);
665        assert_eq!(None, exemplar.timestamp);
666        assert_eq!("user_id", exemplar.label[0].name);
667        assert_eq!("42.0", exemplar.label[0].value);
668
669        counter.inc_by(1.0, Some(vec![("user_id".to_string(), 99.0)]), Some(now));
670
671        let metric_families = encode(&registry).unwrap();
672        let counter = metric_families[0].metric[0].counter.as_ref().unwrap();
673        assert_eq!(2.0, counter.value);
674        let exemplar = counter.exemplar.as_ref().unwrap();
675        assert_eq!(1.0, exemplar.value);
676        assert_eq!(Some(now_ts), exemplar.timestamp.clone());
677        assert_eq!("99.0", exemplar.label[0].value);
678    }
679
680    #[test]
681    fn encode_gauge() {
682        let gauge = Gauge::<i64, AtomicI64>::default();
683        let mut registry = Registry::default();
684        registry.register("my_gauge", "My gauge", gauge.clone());
685        gauge.inc();
686
687        let metric_families = encode(&registry).unwrap();
688        let family = metric_families.first().unwrap();
689        assert_eq!("my_gauge", family.name.as_str());
690        assert_eq!(
691            prometheus_data_model::MetricType::Gauge as i32,
692            family.r#type
693        );
694        assert_eq!(1.0, family.metric[0].gauge.as_ref().unwrap().value);
695    }
696
697    #[test]
698    fn encode_gauge_u64_max() {
699        let gauge = Gauge::<u64, AtomicU64>::default();
700        let mut registry = Registry::default();
701        registry.register("my_gauge", "My gauge", gauge.clone());
702        gauge.set(u64::MAX);
703
704        let metric_families = encode(&registry).unwrap();
705        assert_eq!(
706            u64::MAX as f64,
707            metric_families[0].metric[0].gauge.as_ref().unwrap().value
708        );
709    }
710
711    #[test]
712    fn encode_counter_family() {
713        let mut registry = Registry::default();
714
715        let family = Family::<Vec<(String, String)>, Counter>::default();
716        registry.register("my_counter_family", "My counter family", family.clone());
717
718        family
719            .get_or_create(&vec![
720                ("method".to_string(), "GET".to_string()),
721                ("status".to_string(), "200".to_string()),
722            ])
723            .inc();
724
725        family
726            .get_or_create(&vec![
727                ("method".to_string(), "POST".to_string()),
728                ("status".to_string(), "200".to_string()),
729            ])
730            .inc();
731
732        let metric_families = encode(&registry).unwrap();
733        let family = metric_families.first().unwrap();
734        assert_eq!("my_counter_family_total", family.name.as_str());
735        assert_eq!(2, family.metric.len());
736
737        let mut potential_method_value = HashSet::new();
738        potential_method_value.insert("GET");
739        potential_method_value.insert("POST");
740
741        let metric = family.metric.first().unwrap();
742        assert_eq!(2, metric.label.len());
743        assert_eq!("method", metric.label[0].name);
744        assert!(potential_method_value.remove(metric.label[0].value.as_str()));
745        assert_eq!("status", metric.label[1].name);
746        assert_eq!("200", metric.label[1].value);
747
748        let metric2 = &family.metric[1];
749        assert_eq!(2, metric2.label.len());
750        assert_eq!("method", metric2.label[0].name);
751        assert!(potential_method_value.remove(metric2.label[0].value.as_str()));
752        assert_eq!("status", metric2.label[1].name);
753        assert_eq!("200", metric2.label[1].value);
754    }
755
756    #[test]
757    fn encode_counter_family_with_prefix_and_label() {
758        let mut registry = Registry::default();
759        let sub_registry = registry.sub_registry_with_prefix("my_prefix");
760        let sub_sub_registry = sub_registry
761            .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
762        let family = Family::<Vec<(String, String)>, Counter>::default();
763        sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
764
765        family
766            .get_or_create(&vec![
767                ("method".to_string(), "GET".to_string()),
768                ("status".to_string(), "200".to_string()),
769            ])
770            .inc();
771
772        let metric_families = encode(&registry).unwrap();
773        let family = metric_families.first().unwrap();
774        assert_eq!("my_prefix_my_counter_family_total", family.name.as_str());
775
776        let metric = family.metric.first().unwrap();
777        assert_eq!("my_key", metric.label[0].name);
778        assert_eq!("my_value", metric.label[0].value);
779        assert_eq!("method", metric.label[1].name);
780        assert_eq!("GET", metric.label[1].value);
781        assert_eq!("status", metric.label[2].name);
782        assert_eq!("200", metric.label[2].value);
783    }
784
785    #[test]
786    fn encode_histogram() {
787        let mut registry = Registry::default();
788        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
789        registry.register("my_histogram", "My histogram", histogram.clone());
790        histogram.observe(1.0);
791
792        let metric_families = encode(&registry).unwrap();
793        let family = metric_families.first().unwrap();
794        assert_eq!("my_histogram", family.name.as_str());
795        assert_eq!(
796            prometheus_data_model::MetricType::Histogram as i32,
797            family.r#type
798        );
799
800        let histogram = family.metric[0].histogram.as_ref().unwrap();
801        assert_eq!(1, histogram.sample_count);
802        assert_eq!(1.0, histogram.sample_sum);
803        assert_eq!(11, histogram.bucket.len());
804        assert_eq!(1, histogram.bucket[0].cumulative_count);
805        assert_eq!(1.0, histogram.bucket[0].upper_bound);
806        assert_eq!(f64::MAX, histogram.bucket.last().unwrap().upper_bound);
807    }
808
809    #[test]
810    fn encode_histogram_with_exemplars() {
811        let now = SystemTime::now();
812        let now_ts: Timestamp = now.into();
813
814        let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
815        let mut registry = Registry::default();
816        registry.register("my_histogram", "My histogram", histogram.clone());
817
818        histogram.observe(1.0, Some(vec![("user_id".to_string(), 42u64)]), None);
819
820        let metric_families = encode(&registry).unwrap();
821        let exemplar = metric_families[0].metric[0]
822            .histogram
823            .as_ref()
824            .unwrap()
825            .bucket[0]
826            .exemplar
827            .as_ref()
828            .unwrap();
829        assert_eq!(1.0, exemplar.value);
830        assert_eq!(None, exemplar.timestamp);
831        assert_eq!("42", exemplar.label[0].value);
832
833        histogram.observe(2.0, Some(vec![("user_id".to_string(), 99u64)]), Some(now));
834
835        let metric_families = encode(&registry).unwrap();
836        let exemplar = metric_families[0].metric[0]
837            .histogram
838            .as_ref()
839            .unwrap()
840            .bucket[1]
841            .exemplar
842            .as_ref()
843            .unwrap();
844        assert_eq!(2.0, exemplar.value);
845        assert_eq!(Some(now_ts), exemplar.timestamp.clone());
846        assert_eq!("99", exemplar.label[0].value);
847    }
848
849    #[test]
850    fn encode_native_histogram() {
851        let histogram = Histogram::new_native(NativeHistogramConfig::with_schema(0));
852        let mut registry = Registry::default();
853        registry.register("my_histogram", "My histogram", histogram.clone());
854
855        histogram.observe(1.0);
856        histogram.observe(4.0);
857        histogram.observe(-2.0);
858
859        let metric_families = encode(&registry).unwrap();
860        let histogram = metric_families[0].metric[0].histogram.as_ref().unwrap();
861
862        assert_eq!(3, histogram.sample_count);
863        assert_eq!(3.0, histogram.sample_sum);
864        assert_eq!(0, histogram.schema);
865        assert_eq!(1, histogram.negative_span.len());
866        assert_eq!(1, histogram.positive_span.len());
867        assert_eq!(vec![1], histogram.negative_delta);
868        assert_eq!(vec![1, -1, 1], histogram.positive_delta);
869        assert!(histogram.bucket.is_empty());
870        assert!(histogram.start_timestamp.is_some());
871    }
872
873    #[test]
874    fn encode_native_histogram_nan_only_has_no_op_span() {
875        let histogram = Histogram::new_native(
876            NativeHistogramConfig::with_schema(0)
877                .zero_threshold(NATIVE_HISTOGRAM_ZERO_THRESHOLD_ZERO),
878        );
879        let mut registry = Registry::default();
880        registry.register("my_histogram", "My histogram", histogram.clone());
881
882        histogram.observe(f64::NAN);
883
884        let metric_families = encode(&registry).unwrap();
885        let histogram = metric_families[0].metric[0].histogram.as_ref().unwrap();
886
887        assert_eq!(1, histogram.sample_count);
888        assert!(histogram.sample_sum.is_nan());
889        assert_eq!(0.0, histogram.zero_threshold);
890        assert_eq!(0, histogram.zero_count);
891        assert_eq!(1, histogram.positive_span.len());
892        assert_eq!(0, histogram.positive_span[0].offset);
893        assert_eq!(0, histogram.positive_span[0].length);
894        assert!(histogram.positive_delta.is_empty());
895        assert!(histogram.negative_span.is_empty());
896    }
897
898    #[test]
899    fn encode_classic_and_native_histogram() {
900        let histogram =
901            Histogram::new_classic_and_native([1.0, 2.0], NativeHistogramConfig::with_schema(0));
902        let mut registry = Registry::default();
903        registry.register("my_histogram", "My histogram", histogram.clone());
904
905        histogram.observe(1.0);
906        histogram.observe(4.0);
907
908        let metric_families = encode(&registry).unwrap();
909        let histogram = metric_families[0].metric[0].histogram.as_ref().unwrap();
910
911        assert_eq!(2, histogram.sample_count);
912        assert_eq!(5.0, histogram.sample_sum);
913        assert_eq!(3, histogram.bucket.len());
914        assert_eq!(1, histogram.bucket[0].cumulative_count);
915        assert_eq!(2, histogram.bucket[2].cumulative_count);
916        assert_eq!(0, histogram.schema);
917        assert!(!histogram.positive_span.is_empty());
918        assert!(!histogram.positive_delta.is_empty());
919    }
920
921    #[test]
922    fn encode_family_and_counter_and_histogram() {
923        let mut registry = Registry::default();
924
925        let counter_family = Family::<Vec<(String, String)>, Counter>::default();
926        let histogram_family =
927            Family::<Vec<(String, String)>, Histogram>::new_with_constructor(|| {
928                Histogram::new(exponential_buckets(1.0, 2.0, 10))
929            });
930
931        registry.register("my_family_counter", "My counter", counter_family.clone());
932        registry.register(
933            "my_family_histogram",
934            "My histogram",
935            histogram_family.clone(),
936        );
937
938        counter_family
939            .get_or_create(&vec![("path".to_string(), "/".to_string())])
940            .inc();
941
942        histogram_family
943            .get_or_create(&vec![("path".to_string(), "/".to_string())])
944            .observe(1.0);
945
946        let counter: Counter = Counter::default();
947        registry.register("my_counter", "My counter", counter.clone());
948        counter.inc();
949
950        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
951        registry.register("my_histogram", "My histogram", histogram.clone());
952        histogram.observe(1.0);
953
954        let metric_families = encode(&registry).unwrap();
955        assert_eq!("my_family_counter_total", metric_families[0].name);
956        assert_eq!("my_family_histogram", metric_families[1].name);
957        assert_eq!("my_counter_total", metric_families[2].name);
958        assert_eq!("my_histogram", metric_families[3].name);
959    }
960
961    #[test]
962    fn encode_info() {
963        let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
964        let mut registry = Registry::default();
965        registry.register("my_info_metric", "My info metric", info);
966
967        let metric_families = encode(&registry).unwrap();
968        let family = metric_families.first().unwrap();
969        assert_eq!("my_info_metric_info", family.name.as_str());
970        assert_eq!(
971            prometheus_data_model::MetricType::Gauge as i32,
972            family.r#type
973        );
974
975        let metric = family.metric.first().unwrap();
976        assert_eq!(1.0, metric.gauge.as_ref().unwrap().value);
977        assert_eq!("os", metric.label[0].name);
978        assert_eq!("GNU/linux", metric.label[0].value);
979    }
980
981    #[test]
982    fn encode_to_vec_length_delimited() {
983        let counter: Counter = Counter::default();
984        let mut registry = Registry::default();
985        registry.register("my_counter", "My counter", counter.clone());
986        counter.inc();
987
988        let payload = encode_to_vec(&registry).unwrap();
989        let family =
990            prometheus_data_model::MetricFamily::decode_length_delimited(payload.as_slice())
991                .unwrap();
992
993        assert_eq!("my_counter_total", family.name);
994        assert_eq!(1.0, family.metric[0].counter.as_ref().unwrap().value);
995    }
996}