open_metrics_client/encoding/
text.rs

1//! Open Metrics text format implementation.
2//!
3//! ```
4//! # use open_metrics_client::encoding::text::encode;
5//! # use open_metrics_client::metrics::counter::Counter;
6//! # use open_metrics_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//! let mut buffer = vec![];
18//! encode(&mut buffer, &registry).unwrap();
19//!
20//! let expected = "# HELP my_counter This is my counter.\n".to_owned() +
21//!                "# TYPE my_counter counter\n" +
22//!                "my_counter_total 1\n" +
23//!                "# EOF\n";
24//! assert_eq!(expected, String::from_utf8(buffer).unwrap());
25//! ```
26
27use crate::metrics::counter::{self, Counter};
28use crate::metrics::exemplar::{CounterWithExemplar, Exemplar, HistogramWithExemplars};
29use crate::metrics::family::{Family, MetricConstructor};
30use crate::metrics::gauge::{self, Gauge};
31use crate::metrics::histogram::Histogram;
32use crate::metrics::info::Info;
33use crate::metrics::{MetricType, TypedMetric};
34use crate::registry::{Registry, Unit};
35
36use std::borrow::Cow;
37use std::collections::HashMap;
38use std::io::Write;
39use std::ops::Deref;
40
41pub use open_metrics_client_derive_text_encode::*;
42
43pub fn encode<W, M>(writer: &mut W, registry: &Registry<M>) -> Result<(), std::io::Error>
44where
45    W: Write,
46    M: EncodeMetric,
47{
48    for (desc, metric) in registry.iter() {
49        writer.write_all(b"# HELP ")?;
50        writer.write_all(desc.name().as_bytes())?;
51        if let Some(unit) = desc.unit() {
52            writer.write_all(b"_")?;
53            unit.encode(writer)?;
54        }
55        writer.write_all(b" ")?;
56        writer.write_all(desc.help().as_bytes())?;
57        writer.write_all(b"\n")?;
58
59        writer.write_all(b"# TYPE ")?;
60        writer.write_all(desc.name().as_bytes())?;
61        if let Some(unit) = desc.unit() {
62            writer.write_all(b"_")?;
63            unit.encode(writer)?;
64        }
65        writer.write_all(b" ")?;
66        metric.metric_type().encode(writer)?;
67        writer.write_all(b"\n")?;
68
69        if let Some(unit) = desc.unit() {
70            writer.write_all(b"# UNIT ")?;
71            writer.write_all(desc.name().as_bytes())?;
72            writer.write_all(b"_")?;
73            unit.encode(writer)?;
74            writer.write_all(b" ")?;
75            unit.encode(writer)?;
76            writer.write_all(b"\n")?;
77        }
78
79        let encoder = Encoder {
80            writer,
81            name: desc.name(),
82            unit: desc.unit(),
83            const_labels: desc.labels(),
84            labels: None,
85        };
86
87        metric.encode(encoder)?;
88    }
89
90    writer.write_all(b"# EOF\n")?;
91
92    Ok(())
93}
94
95pub trait Encode {
96    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error>;
97}
98
99impl Encode for f64 {
100    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
101        writer.write_all(dtoa::Buffer::new().format(*self).as_bytes())?;
102        Ok(())
103    }
104}
105
106impl Encode for u64 {
107    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
108        writer.write_all(itoa::Buffer::new().format(*self).as_bytes())?;
109        Ok(())
110    }
111}
112
113impl Encode for u32 {
114    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
115        writer.write_all(itoa::Buffer::new().format(*self).as_bytes())?;
116        Ok(())
117    }
118}
119
120impl<T: Encode> Encode for &[T] {
121    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
122        if self.is_empty() {
123            return Ok(());
124        }
125
126        let mut iter = self.iter().peekable();
127        while let Some(x) = iter.next() {
128            x.encode(writer)?;
129
130            if iter.peek().is_some() {
131                writer.write_all(b",")?;
132            }
133        }
134
135        Ok(())
136    }
137}
138
139impl<T: Encode> Encode for Vec<T> {
140    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
141        self.as_slice().encode(writer)
142    }
143}
144
145impl<K: Encode, V: Encode> Encode for (K, V) {
146    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
147        let (key, value) = self;
148
149        key.encode(writer)?;
150        writer.write_all(b"=\"")?;
151
152        value.encode(writer)?;
153        writer.write_all(b"\"")?;
154
155        Ok(())
156    }
157}
158
159impl Encode for &str {
160    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
161        // TODO: Can we do better?
162        writer.write_all(self.as_bytes())?;
163        Ok(())
164    }
165}
166
167impl Encode for String {
168    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
169        self.as_str().encode(writer)
170    }
171}
172
173impl<'a> Encode for Cow<'a, str> {
174    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
175        self.as_ref().encode(writer)
176    }
177}
178
179impl Encode for MetricType {
180    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
181        let t = match self {
182            MetricType::Counter => "counter",
183            MetricType::Gauge => "gauge",
184            MetricType::Histogram => "histogram",
185            MetricType::Info => "info",
186            MetricType::Unknown => "unknown",
187        };
188
189        writer.write_all(t.as_bytes())?;
190        Ok(())
191    }
192}
193
194impl Encode for Unit {
195    fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
196        let u = match self {
197            Unit::Amperes => "amperes",
198            Unit::Bytes => "bytes",
199            Unit::Celsius => "celsius",
200            Unit::Grams => "grams",
201            Unit::Joules => "joules",
202            Unit::Meters => "meters",
203            Unit::Ratios => "ratios",
204            Unit::Seconds => "seconds",
205            Unit::Volts => "volts",
206            Unit::Other(other) => other.as_str(),
207        };
208
209        writer.write_all(u.as_bytes())?;
210        Ok(())
211    }
212}
213
214impl Encode for () {
215    fn encode(&self, _writer: &mut dyn Write) -> Result<(), std::io::Error> {
216        Ok(())
217    }
218}
219
220// `Encoder` does not take a trait parameter for `writer` and `labels` because
221// `EncodeMetric` which uses `Encoder` needs to be usable as a trait object in
222// order to be able to register different metric types with a `Registry`. Trait
223// objects can not use type parameters.
224//
225// TODO: Alternative solutions to the above are very much appreciated.
226pub struct Encoder<'a, 'b> {
227    writer: &'a mut dyn Write,
228    name: &'a str,
229    unit: &'a Option<Unit>,
230    const_labels: &'a [(Cow<'static, str>, Cow<'static, str>)],
231    labels: Option<&'b dyn Encode>,
232}
233
234impl<'a, 'b> Encoder<'a, 'b> {
235    pub fn encode_suffix(&mut self, suffix: &'static str) -> Result<BucketEncoder, std::io::Error> {
236        self.write_name_and_unit()?;
237
238        self.writer.write_all(b"_")?;
239        self.writer.write_all(suffix.as_bytes()).map(|_| ())?;
240
241        self.encode_labels()
242    }
243
244    pub fn no_suffix(&mut self) -> Result<BucketEncoder, std::io::Error> {
245        self.write_name_and_unit()?;
246
247        self.encode_labels()
248    }
249
250    fn write_name_and_unit(&mut self) -> Result<(), std::io::Error> {
251        self.writer.write_all(self.name.as_bytes())?;
252        if let Some(unit) = self.unit {
253            self.writer.write_all(b"_")?;
254            unit.encode(self.writer)?;
255        }
256
257        Ok(())
258    }
259
260    // TODO: Consider caching the encoded labels for Histograms as they stay the
261    // same but are currently encoded multiple times.
262    fn encode_labels(&mut self) -> Result<BucketEncoder, std::io::Error> {
263        let mut opened_curly_brackets = false;
264
265        if !self.const_labels.is_empty() {
266            self.writer.write_all(b"{")?;
267            opened_curly_brackets = true;
268
269            self.const_labels.encode(self.writer)?;
270        }
271
272        if let Some(labels) = &self.labels {
273            if opened_curly_brackets {
274                self.writer.write_all(b",")?;
275            } else {
276                opened_curly_brackets = true;
277                self.writer.write_all(b"{")?;
278            }
279            labels.encode(self.writer)?;
280        }
281
282        Ok(BucketEncoder {
283            opened_curly_brackets,
284            writer: self.writer,
285        })
286    }
287
288    pub fn with_label_set<'c, 'd>(&'c mut self, label_set: &'d dyn Encode) -> Encoder<'c, 'd> {
289        debug_assert!(self.labels.is_none());
290
291        Encoder {
292            writer: self.writer,
293            name: self.name,
294            unit: self.unit,
295            const_labels: self.const_labels,
296            labels: Some(label_set),
297        }
298    }
299}
300
301#[must_use]
302pub struct BucketEncoder<'a> {
303    writer: &'a mut dyn Write,
304    opened_curly_brackets: bool,
305}
306
307impl<'a> BucketEncoder<'a> {
308    fn encode_bucket(&mut self, upper_bound: f64) -> Result<ValueEncoder, std::io::Error> {
309        if self.opened_curly_brackets {
310            self.writer.write_all(b",")?;
311        } else {
312            self.writer.write_all(b"{")?;
313        }
314
315        self.writer.write_all(b"le=\"")?;
316        if upper_bound == f64::MAX {
317            self.writer.write_all(b"+Inf")?;
318        } else {
319            upper_bound.encode(self.writer)?;
320        }
321        self.writer.write_all(b"\"}")?;
322
323        Ok(ValueEncoder {
324            writer: self.writer,
325        })
326    }
327
328    fn no_bucket(&mut self) -> Result<ValueEncoder, std::io::Error> {
329        if self.opened_curly_brackets {
330            self.writer.write_all(b"}")?;
331        }
332        Ok(ValueEncoder {
333            writer: self.writer,
334        })
335    }
336}
337
338#[must_use]
339pub struct ValueEncoder<'a> {
340    writer: &'a mut dyn Write,
341}
342
343impl<'a> ValueEncoder<'a> {
344    fn encode_value<V: Encode>(&mut self, v: V) -> Result<ExemplarEncoder, std::io::Error> {
345        self.writer.write_all(b" ")?;
346        v.encode(self.writer)?;
347        Ok(ExemplarEncoder {
348            writer: self.writer,
349        })
350    }
351}
352
353#[must_use]
354pub struct ExemplarEncoder<'a> {
355    writer: &'a mut dyn Write,
356}
357
358impl<'a> ExemplarEncoder<'a> {
359    fn encode_exemplar<S: Encode, V: Encode>(
360        &mut self,
361        exemplar: &Exemplar<S, V>,
362    ) -> Result<(), std::io::Error> {
363        self.writer.write_all(b" # {")?;
364        exemplar.label_set.encode(self.writer)?;
365        self.writer.write_all(b"} ")?;
366        exemplar.value.encode(self.writer)?;
367        self.writer.write_all(b"\n")?;
368        Ok(())
369    }
370
371    fn no_exemplar(&mut self) -> Result<(), std::io::Error> {
372        self.writer.write_all(b"\n")?;
373        Ok(())
374    }
375}
376
377pub trait EncodeMetric {
378    fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error>;
379
380    // One can not use [`TypedMetric`] directly, as associated constants are not
381    // object safe and thus can not be used with dynamic dispatching.
382    fn metric_type(&self) -> MetricType;
383}
384
385impl EncodeMetric for Box<dyn EncodeMetric> {
386    fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error> {
387        self.deref().encode(encoder)
388    }
389
390    fn metric_type(&self) -> MetricType {
391        self.deref().metric_type()
392    }
393}
394
395pub trait SendEncodeMetric: EncodeMetric + Send {}
396
397impl<T: EncodeMetric + Send> SendEncodeMetric for T {}
398
399impl EncodeMetric for Box<dyn SendEncodeMetric> {
400    fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error> {
401        self.deref().encode(encoder)
402    }
403
404    fn metric_type(&self) -> MetricType {
405        self.deref().metric_type()
406    }
407}
408
409/////////////////////////////////////////////////////////////////////////////////
410// Counter
411
412impl<N, A> EncodeMetric for Counter<N, A>
413where
414    N: Encode,
415    A: counter::Atomic<N>,
416{
417    fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error> {
418        // TODO: Would be better to use never type instead of `()`.
419        encode_counter_with_maybe_exemplar::<(), _>(self.get(), None, encoder)
420    }
421
422    fn metric_type(&self) -> MetricType {
423        Self::TYPE
424    }
425}
426
427// TODO: S, V, N, A are hard to grasp.
428impl<S, N, A> EncodeMetric for CounterWithExemplar<S, N, A>
429where
430    S: Encode,
431    N: Encode + Clone,
432    A: counter::Atomic<N>,
433{
434    fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error> {
435        let (value, exemplar) = self.get();
436        encode_counter_with_maybe_exemplar(value, exemplar.as_ref().as_ref(), encoder)
437    }
438
439    fn metric_type(&self) -> MetricType {
440        Counter::<N, A>::TYPE
441    }
442}
443
444fn encode_counter_with_maybe_exemplar<S, N>(
445    value: N,
446    exemplar: Option<&Exemplar<S, N>>,
447    mut encoder: Encoder,
448) -> Result<(), std::io::Error>
449where
450    S: Encode,
451    N: Encode,
452{
453    let mut bucket_encoder = encoder.encode_suffix("total")?;
454    let mut value_encoder = bucket_encoder.no_bucket()?;
455    let mut exemplar_encoder = value_encoder.encode_value(value)?;
456
457    match exemplar {
458        Some(exemplar) => exemplar_encoder.encode_exemplar(exemplar)?,
459        None => exemplar_encoder.no_exemplar()?,
460    }
461
462    Ok(())
463}
464
465/////////////////////////////////////////////////////////////////////////////////
466// Gauge
467
468impl<N, A> EncodeMetric for Gauge<N, A>
469where
470    N: Encode,
471    A: gauge::Atomic<N>,
472{
473    fn encode(&self, mut encoder: Encoder) -> Result<(), std::io::Error> {
474        encoder
475            .no_suffix()?
476            .no_bucket()?
477            .encode_value(self.get())?
478            .no_exemplar()?;
479
480        Ok(())
481    }
482    fn metric_type(&self) -> MetricType {
483        Self::TYPE
484    }
485}
486
487/////////////////////////////////////////////////////////////////////////////////
488// Family
489
490impl<S, M, C> EncodeMetric for Family<S, M, C>
491where
492    S: Clone + std::hash::Hash + Eq + Encode,
493    M: EncodeMetric + TypedMetric,
494    C: MetricConstructor<M>,
495{
496    fn encode(&self, mut encoder: Encoder) -> Result<(), std::io::Error> {
497        let guard = self.read();
498        for (label_set, m) in guard.iter() {
499            let encoder = encoder.with_label_set(label_set);
500            m.encode(encoder)?;
501        }
502        Ok(())
503    }
504
505    fn metric_type(&self) -> MetricType {
506        M::TYPE
507    }
508}
509
510/////////////////////////////////////////////////////////////////////////////////
511// Histogram
512
513impl EncodeMetric for Histogram {
514    fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error> {
515        let (sum, count, buckets) = self.get();
516        // TODO: Would be better to use never type instead of `()`.
517        encode_histogram_with_maybe_exemplars::<()>(sum, count, &buckets, None, encoder)
518    }
519
520    fn metric_type(&self) -> MetricType {
521        Self::TYPE
522    }
523}
524
525impl<S: Encode> EncodeMetric for HistogramWithExemplars<S> {
526    fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error> {
527        let inner = self.inner();
528        let (sum, count, buckets) = inner.histogram.get();
529        encode_histogram_with_maybe_exemplars(sum, count, &buckets, Some(&inner.exemplars), encoder)
530    }
531
532    fn metric_type(&self) -> MetricType {
533        Histogram::TYPE
534    }
535}
536
537fn encode_histogram_with_maybe_exemplars<S: Encode>(
538    sum: f64,
539    count: u64,
540    buckets: &[(f64, u64)],
541    exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
542    mut encoder: Encoder,
543) -> Result<(), std::io::Error> {
544    encoder
545        .encode_suffix("sum")?
546        .no_bucket()?
547        .encode_value(sum)?
548        .no_exemplar()?;
549    encoder
550        .encode_suffix("count")?
551        .no_bucket()?
552        .encode_value(count)?
553        .no_exemplar()?;
554
555    let mut cummulative = 0;
556    for (i, (upper_bound, count)) in buckets.iter().enumerate() {
557        cummulative += count;
558        let mut bucket_encoder = encoder.encode_suffix("bucket")?;
559        let mut value_encoder = bucket_encoder.encode_bucket(*upper_bound)?;
560        let mut exemplar_encoder = value_encoder.encode_value(cummulative)?;
561
562        match exemplars.map(|es| es.get(&i)).flatten() {
563            Some(exemplar) => exemplar_encoder.encode_exemplar(exemplar)?,
564            None => exemplar_encoder.no_exemplar()?,
565        }
566    }
567
568    Ok(())
569}
570
571/////////////////////////////////////////////////////////////////////////////////
572// Info
573
574impl<S> EncodeMetric for Info<S>
575where
576    S: Clone + std::hash::Hash + Eq + Encode,
577{
578    fn encode(&self, mut encoder: Encoder) -> Result<(), std::io::Error> {
579        encoder
580            .with_label_set(&self.0)
581            .encode_suffix("info")?
582            .no_bucket()?
583            .encode_value(1u32)?
584            .no_exemplar()?;
585
586        Ok(())
587    }
588
589    fn metric_type(&self) -> MetricType {
590        Self::TYPE
591    }
592}
593
594#[cfg(test)]
595mod tests {
596    use super::*;
597    use crate::metrics::counter::Counter;
598    use crate::metrics::gauge::Gauge;
599    use crate::metrics::histogram::exponential_buckets;
600    use pyo3::{prelude::*, types::PyModule};
601    use std::borrow::Cow;
602
603    #[test]
604    fn encode_counter() {
605        let counter: Counter = Counter::default();
606        let mut registry = Registry::default();
607        registry.register("my_counter", "My counter", counter.clone());
608
609        let mut encoded = Vec::new();
610
611        encode(&mut encoded, &registry).unwrap();
612
613        parse_with_python_client(String::from_utf8(encoded).unwrap());
614    }
615
616    #[test]
617    fn encode_counter_with_unit() {
618        let mut registry = Registry::default();
619        let counter: Counter = Counter::default();
620        registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter.clone());
621
622        let mut encoded = Vec::new();
623        encode(&mut encoded, &registry).unwrap();
624
625        let expected = "# HELP my_counter_seconds My counter.\n".to_owned()
626            + "# TYPE my_counter_seconds counter\n"
627            + "# UNIT my_counter_seconds seconds\n"
628            + "my_counter_seconds_total 0\n"
629            + "# EOF\n";
630        assert_eq!(expected, String::from_utf8(encoded.clone()).unwrap());
631
632        parse_with_python_client(String::from_utf8(encoded).unwrap());
633    }
634
635    #[test]
636    fn encode_counter_with_exemplar() {
637        let mut registry = Registry::default();
638
639        let counter_with_exemplar: CounterWithExemplar<(String, u64)> =
640            CounterWithExemplar::default();
641        registry.register_with_unit(
642            "my_counter_with_exemplar",
643            "My counter with exemplar",
644            Unit::Seconds,
645            counter_with_exemplar.clone(),
646        );
647
648        counter_with_exemplar.inc_by(1, Some(("user_id".to_string(), 42)));
649
650        let mut encoded = Vec::new();
651        encode(&mut encoded, &registry).unwrap();
652
653        let expected = "# HELP my_counter_with_exemplar_seconds My counter with exemplar.\n"
654            .to_owned()
655            + "# TYPE my_counter_with_exemplar_seconds counter\n"
656            + "# UNIT my_counter_with_exemplar_seconds seconds\n"
657            + "my_counter_with_exemplar_seconds_total 1 # {user_id=\"42\"} 1\n"
658            + "# EOF\n";
659        assert_eq!(expected, String::from_utf8(encoded.clone()).unwrap());
660
661        parse_with_python_client(String::from_utf8(encoded).unwrap());
662    }
663
664    #[test]
665    fn encode_gauge() {
666        let mut registry = Registry::default();
667        let gauge: Gauge = Gauge::default();
668        registry.register("my_gauge", "My gauge", gauge.clone());
669
670        let mut encoded = Vec::new();
671
672        encode(&mut encoded, &registry).unwrap();
673
674        parse_with_python_client(String::from_utf8(encoded).unwrap());
675    }
676
677    #[test]
678    fn encode_counter_family() {
679        let mut registry = Registry::default();
680        let family = Family::<Vec<(String, String)>, Counter>::default();
681        registry.register("my_counter_family", "My counter family", family.clone());
682
683        family
684            .get_or_create(&vec![
685                ("method".to_string(), "GET".to_string()),
686                ("status".to_string(), "200".to_string()),
687            ])
688            .inc();
689
690        let mut encoded = Vec::new();
691
692        encode(&mut encoded, &registry).unwrap();
693
694        parse_with_python_client(String::from_utf8(encoded).unwrap());
695    }
696
697    #[test]
698    fn encode_counter_family_with_prefix_with_label() {
699        let mut registry = Registry::default();
700        let sub_registry = registry.sub_registry_with_prefix("my_prefix");
701        let sub_sub_registry = sub_registry
702            .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
703        let family = Family::<Vec<(String, String)>, Counter>::default();
704        sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
705
706        family
707            .get_or_create(&vec![
708                ("method".to_string(), "GET".to_string()),
709                ("status".to_string(), "200".to_string()),
710            ])
711            .inc();
712
713        let mut encoded = Vec::new();
714
715        encode(&mut encoded, &registry).unwrap();
716
717        let expected = "# HELP my_prefix_my_counter_family My counter family.\n"
718            .to_owned()
719            + "# TYPE my_prefix_my_counter_family counter\n"
720            + "my_prefix_my_counter_family_total{my_key=\"my_value\",method=\"GET\",status=\"200\"} 1\n"
721            + "# EOF\n";
722        assert_eq!(expected, String::from_utf8(encoded.clone()).unwrap());
723
724        parse_with_python_client(String::from_utf8(encoded).unwrap());
725    }
726
727    #[test]
728    fn encode_info() {
729        let mut registry = Registry::default();
730        let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
731        registry.register("my_info_metric", "My info metric", info);
732
733        let mut encoded = Vec::new();
734        encode(&mut encoded, &registry).unwrap();
735
736        let expected = "# HELP my_info_metric My info metric.\n".to_owned()
737            + "# TYPE my_info_metric info\n"
738            + "my_info_metric_info{os=\"GNU/linux\"} 1\n"
739            + "# EOF\n";
740        assert_eq!(expected, String::from_utf8(encoded.clone()).unwrap());
741
742        parse_with_python_client(String::from_utf8(encoded).unwrap());
743    }
744
745    #[test]
746    fn encode_histogram() {
747        let mut registry = Registry::default();
748        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
749        registry.register("my_histogram", "My histogram", histogram.clone());
750        histogram.observe(1.0);
751
752        let mut encoded = Vec::new();
753
754        encode(&mut encoded, &registry).unwrap();
755
756        parse_with_python_client(String::from_utf8(encoded).unwrap());
757    }
758
759    #[test]
760    fn encode_histogram_family() {
761        let mut registry = Registry::default();
762        let family =
763            Family::new_with_constructor(|| Histogram::new(exponential_buckets(1.0, 2.0, 10)));
764        registry.register("my_histogram", "My histogram", family.clone());
765        family
766            .get_or_create(&vec![
767                ("method".to_string(), "GET".to_string()),
768                ("status".to_string(), "200".to_string()),
769            ])
770            .observe(1.0);
771
772        let mut encoded = Vec::new();
773
774        encode(&mut encoded, &registry).unwrap();
775
776        parse_with_python_client(String::from_utf8(encoded).unwrap());
777    }
778
779    #[test]
780    fn encode_histogram_with_exemplars() {
781        let mut registry = Registry::default();
782        let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
783        registry.register("my_histogram", "My histogram", histogram.clone());
784        histogram.observe(1.0, Some(("user_id".to_string(), 42u64)));
785
786        let mut encoded = Vec::new();
787        encode(&mut encoded, &registry).unwrap();
788
789        let expected = "# HELP my_histogram My histogram.\n".to_owned()
790            + "# TYPE my_histogram histogram\n"
791            + "my_histogram_sum 1.0\n"
792            + "my_histogram_count 1\n"
793            + "my_histogram_bucket{le=\"1.0\"} 1 # {user_id=\"42\"} 1.0\n"
794            + "my_histogram_bucket{le=\"2.0\"} 1\n"
795            + "my_histogram_bucket{le=\"4.0\"} 1\n"
796            + "my_histogram_bucket{le=\"8.0\"} 1\n"
797            + "my_histogram_bucket{le=\"16.0\"} 1\n"
798            + "my_histogram_bucket{le=\"32.0\"} 1\n"
799            + "my_histogram_bucket{le=\"64.0\"} 1\n"
800            + "my_histogram_bucket{le=\"128.0\"} 1\n"
801            + "my_histogram_bucket{le=\"256.0\"} 1\n"
802            + "my_histogram_bucket{le=\"512.0\"} 1\n"
803            + "my_histogram_bucket{le=\"+Inf\"} 1\n"
804            + "# EOF\n";
805        assert_eq!(expected, String::from_utf8(encoded.clone()).unwrap());
806
807        parse_with_python_client(String::from_utf8(encoded).unwrap());
808    }
809
810    fn parse_with_python_client(input: String) {
811        pyo3::prepare_freethreaded_python();
812
813        println!("{:?}", input);
814        Python::with_gil(|py| {
815            let parser = PyModule::from_code(
816                py,
817                r#"
818from prometheus_client.openmetrics.parser import text_string_to_metric_families
819
820def parse(input):
821    families = text_string_to_metric_families(input)
822    list(families)
823"#,
824                "parser.py",
825                "parser",
826            )
827            .map_err(|e| e.to_string())
828            .unwrap();
829
830            parser
831                .getattr("parse")
832                .expect("`parse` to exist.")
833                .call1((input,))
834                .map_err(|e| e.to_string())
835                .unwrap();
836        })
837    }
838}