Skip to main content

prometheus_client/encoding/
text.rs

1//! Open Metrics text format implementation.
2//!
3//! ```
4//! # use prometheus_client::encoding::text::{encode, encode_registry, encode_eof};
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//! let mut buffer = String::new();
18//!
19//! // Encode the complete OpenMetrics exposition into the message buffer
20//! encode(&mut buffer, &registry).unwrap();
21//! let expected_msg = "# HELP my_counter This is my counter.\n".to_owned() +
22//!                    "# TYPE my_counter counter\n" +
23//!                    "my_counter_total 1\n" +
24//!                    "# EOF\n";
25//! assert_eq!(expected_msg, buffer);
26//! buffer.clear();
27//!
28//! // Encode just the registry into the message buffer
29//! encode_registry(&mut buffer, &registry).unwrap();
30//! let expected_reg = "# HELP my_counter This is my counter.\n".to_owned() +
31//!                    "# TYPE my_counter counter\n" +
32//!                    "my_counter_total 1\n";
33//! assert_eq!(expected_reg, buffer);
34//!
35//! // Encode EOF marker into message buffer to complete the OpenMetrics exposition
36//! encode_eof(&mut buffer).unwrap();
37//! assert_eq!(expected_msg, buffer);
38//! ```
39
40use crate::encoding::{
41    EncodeExemplarTime, EncodeExemplarValue, EncodeLabelSet, NativeHistogram, NoLabelSet,
42};
43use crate::metrics::exemplar::Exemplar;
44use crate::metrics::MetricType;
45use crate::registry::{Prefix, Registry, Unit};
46
47use std::borrow::Cow;
48use std::collections::HashMap;
49use std::fmt::Write;
50
51/// Encode both the metrics registered with the provided [`Registry`] and the
52/// EOF marker into the provided [`Write`]r using the OpenMetrics text format.
53///
54/// Note: This function encodes the **complete** OpenMetrics exposition.
55///
56/// Use [`encode_registry`] or [`encode_eof`] if partial encoding is needed.
57///
58/// See [OpenMetrics exposition format](https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#text-format)
59/// for additional details.
60///
61/// # Examples
62///
63/// ```no_run
64/// # use prometheus_client::encoding::text::encode;
65/// # use prometheus_client::metrics::counter::Counter;
66/// # use prometheus_client::metrics::gauge::Gauge;
67/// # use prometheus_client::registry::Registry;
68/// #
69/// // Initialize registry with metric families
70/// let mut registry = Registry::default();
71/// let counter: Counter = Counter::default();
72/// registry.register(
73///     "my_counter",
74///     "This is my counter",
75///     counter.clone(),
76/// );
77/// let gauge: Gauge = Gauge::default();
78/// registry.register(
79///     "my_gauge",
80///     "This is my gauge",
81///     gauge.clone(),
82/// );
83///
84/// // Encode the complete OpenMetrics exposition into the buffer
85/// let mut buffer = String::new();
86/// encode(&mut buffer, &registry)?;
87/// # Ok::<(), std::fmt::Error>(())
88/// ```
89pub fn encode<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
90where
91    W: Write,
92{
93    encode_registry(writer, registry)?;
94    encode_eof(writer)
95}
96
97/// Encode the metrics registered with the provided [`Registry`] into the
98/// provided [`Write`]r using the OpenMetrics text format.
99///
100/// Note: The OpenMetrics exposition requires that a complete message must end
101/// with an EOF marker.
102///
103/// This function may be called repeatedly for the HTTP scrape response until
104/// [`encode_eof`] signals the end of the response.
105///
106/// This may also be used to compose a partial message with metrics assembled
107/// from multiple registries.
108///
109/// # Examples
110///
111/// ```no_run
112/// # use prometheus_client::encoding::text::encode_registry;
113/// # use prometheus_client::metrics::counter::Counter;
114/// # use prometheus_client::metrics::gauge::Gauge;
115/// # use prometheus_client::registry::Registry;
116/// #
117/// // Initialize registry with a counter
118/// let mut reg_counter = Registry::default();
119/// let counter: Counter = Counter::default();
120/// reg_counter.register(
121///     "my_counter",
122///     "This is my counter",
123///     counter.clone(),
124/// );
125///
126/// // Encode the counter registry into the buffer
127/// let mut buffer = String::new();
128/// encode_registry(&mut buffer, &reg_counter)?;
129///
130/// // Initialize another registry but with a gauge
131/// let mut reg_gauge = Registry::default();
132/// let gauge: Gauge = Gauge::default();
133/// reg_gauge.register(
134///   "my_gauge",
135///   "This is my gauge",
136///   gauge.clone(),
137/// );
138///
139/// // Encode the gauge registry into the buffer
140/// encode_registry(&mut buffer, &reg_gauge)?;
141/// # Ok::<(), std::fmt::Error>(())
142/// ```
143pub fn encode_registry<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
144where
145    W: Write,
146{
147    registry.encode(&mut DescriptorEncoder::new(writer).into())
148}
149
150/// Encode the EOF marker into the provided [`Write`]r using the OpenMetrics
151/// text format.
152///
153/// Note: This function is used to mark/signal the end of the exposition.
154///
155/// # Examples
156///
157/// ```no_run
158/// # use prometheus_client::encoding::text::{encode_registry, encode_eof};
159/// # use prometheus_client::metrics::counter::Counter;
160/// # use prometheus_client::metrics::gauge::Gauge;
161/// # use prometheus_client::registry::Registry;
162/// #
163/// // Initialize registry with a counter
164/// let mut registry = Registry::default();
165/// let counter: Counter = Counter::default();
166/// registry.register(
167///     "my_counter",
168///     "This is my counter",
169///     counter.clone(),
170/// );
171///
172/// // Encode registry into the buffer
173/// let mut buffer = String::new();
174/// encode_registry(&mut buffer, &registry)?;
175///
176/// // Encode EOF marker to complete the message
177/// encode_eof(&mut buffer)?;
178/// # Ok::<(), std::fmt::Error>(())
179/// ```
180pub fn encode_eof<W>(writer: &mut W) -> Result<(), std::fmt::Error>
181where
182    W: Write,
183{
184    writer.write_str("# EOF\n")
185}
186
187pub(crate) struct DescriptorEncoder<'a> {
188    writer: &'a mut dyn Write,
189    prefix: Option<&'a Prefix>,
190    labels: &'a [(Cow<'static, str>, Cow<'static, str>)],
191}
192
193impl std::fmt::Debug for DescriptorEncoder<'_> {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        f.debug_struct("DescriptorEncoder").finish()
196    }
197}
198
199impl DescriptorEncoder<'_> {
200    pub(crate) fn new(writer: &mut dyn Write) -> DescriptorEncoder<'_> {
201        DescriptorEncoder {
202            writer,
203            prefix: Default::default(),
204            labels: Default::default(),
205        }
206    }
207
208    pub(crate) fn with_prefix_and_labels<'s>(
209        &'s mut self,
210        prefix: Option<&'s Prefix>,
211        labels: &'s [(Cow<'static, str>, Cow<'static, str>)],
212    ) -> DescriptorEncoder<'s> {
213        DescriptorEncoder {
214            prefix,
215            labels,
216            writer: self.writer,
217        }
218    }
219
220    pub fn encode_descriptor<'s>(
221        &'s mut self,
222        name: &'s str,
223        help: &str,
224        unit: Option<&'s Unit>,
225        metric_type: MetricType,
226    ) -> Result<MetricEncoder<'s>, std::fmt::Error> {
227        self.writer.write_str("# HELP ")?;
228        if let Some(prefix) = self.prefix {
229            self.writer.write_str(prefix.as_str())?;
230            self.writer.write_str("_")?;
231        }
232        self.writer.write_str(name)?;
233        if let Some(unit) = unit {
234            self.writer.write_str("_")?;
235            self.writer.write_str(unit.as_str())?;
236        }
237        self.writer.write_str(" ")?;
238        self.writer.write_str(help)?;
239        self.writer.write_str("\n")?;
240
241        self.writer.write_str("# TYPE ")?;
242        if let Some(prefix) = self.prefix {
243            self.writer.write_str(prefix.as_str())?;
244            self.writer.write_str("_")?;
245        }
246        self.writer.write_str(name)?;
247        if let Some(unit) = unit {
248            self.writer.write_str("_")?;
249            self.writer.write_str(unit.as_str())?;
250        }
251        self.writer.write_str(" ")?;
252        self.writer.write_str(metric_type.as_str())?;
253        self.writer.write_str("\n")?;
254
255        if let Some(unit) = unit {
256            self.writer.write_str("# UNIT ")?;
257            if let Some(prefix) = self.prefix {
258                self.writer.write_str(prefix.as_str())?;
259                self.writer.write_str("_")?;
260            }
261            self.writer.write_str(name)?;
262            self.writer.write_str("_")?;
263            self.writer.write_str(unit.as_str())?;
264            self.writer.write_str(" ")?;
265            self.writer.write_str(unit.as_str())?;
266            self.writer.write_str("\n")?;
267        }
268
269        Ok(MetricEncoder {
270            writer: self.writer,
271            prefix: self.prefix,
272            name,
273            unit,
274            const_labels: self.labels,
275            family_labels: None,
276        })
277    }
278}
279
280/// Helper type for [`EncodeMetric`](super::EncodeMetric), see
281/// [`EncodeMetric::encode`](super::EncodeMetric::encode).
282///
283// `MetricEncoder` does not take a trait parameter for `writer` and `labels`
284// because `EncodeMetric` which uses `MetricEncoder` needs to be usable as a
285// trait object in order to be able to register different metric types with a
286// `Registry`. Trait objects can not use type parameters.
287//
288// TODO: Alternative solutions to the above are very much appreciated.
289pub(crate) struct MetricEncoder<'a> {
290    writer: &'a mut dyn Write,
291    prefix: Option<&'a Prefix>,
292    name: &'a str,
293    unit: Option<&'a Unit>,
294    const_labels: &'a [(Cow<'static, str>, Cow<'static, str>)],
295    family_labels: Option<&'a dyn super::EncodeLabelSet>,
296}
297
298impl std::fmt::Debug for MetricEncoder<'_> {
299    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300        let mut labels = String::new();
301        let mut encoder = LabelSetEncoder::new(&mut labels).into();
302        if let Some(l) = self.family_labels {
303            l.encode(&mut encoder)?;
304        }
305
306        f.debug_struct("Encoder")
307            .field("name", &self.name)
308            .field("prefix", &self.prefix)
309            .field("unit", &self.unit)
310            .field("const_labels", &self.const_labels)
311            .field("labels", &labels.as_str())
312            .finish()
313    }
314}
315
316impl MetricEncoder<'_> {
317    pub fn encode_counter<
318        S: EncodeLabelSet,
319        CounterValue: super::EncodeCounterValue,
320        ExemplarValue: EncodeExemplarValue,
321    >(
322        &mut self,
323        v: &CounterValue,
324        exemplar: Option<&Exemplar<S, ExemplarValue>>,
325    ) -> Result<(), std::fmt::Error> {
326        self.write_prefix_name_unit()?;
327
328        self.write_suffix("total")?;
329
330        self.encode_labels::<NoLabelSet>(None)?;
331
332        v.encode(
333            &mut CounterValueEncoder {
334                writer: self.writer,
335            }
336            .into(),
337        )?;
338
339        if let Some(exemplar) = exemplar {
340            self.encode_exemplar(exemplar)?;
341        }
342
343        self.newline()?;
344
345        Ok(())
346    }
347
348    pub fn encode_gauge<GaugeValue: super::EncodeGaugeValue>(
349        &mut self,
350        v: &GaugeValue,
351    ) -> Result<(), std::fmt::Error> {
352        self.write_prefix_name_unit()?;
353
354        self.encode_labels::<NoLabelSet>(None)?;
355
356        v.encode(
357            &mut GaugeValueEncoder {
358                writer: self.writer,
359            }
360            .into(),
361        )?;
362
363        self.newline()?;
364
365        Ok(())
366    }
367
368    pub fn encode_info<S: EncodeLabelSet>(&mut self, label_set: &S) -> Result<(), std::fmt::Error> {
369        self.write_prefix_name_unit()?;
370
371        self.write_suffix("info")?;
372
373        self.encode_labels(Some(label_set))?;
374
375        self.writer.write_str(" ")?;
376        self.writer.write_str(itoa::Buffer::new().format(1))?;
377
378        self.newline()?;
379
380        Ok(())
381    }
382
383    /// Encode a set of labels. Used by wrapper metric types like
384    /// [`Family`](crate::metrics::family::Family).
385    pub fn encode_family<'s, S: EncodeLabelSet>(
386        &'s mut self,
387        label_set: &'s S,
388    ) -> Result<MetricEncoder<'s>, std::fmt::Error> {
389        debug_assert!(self.family_labels.is_none());
390
391        Ok(MetricEncoder {
392            writer: self.writer,
393            prefix: self.prefix,
394            name: self.name,
395            unit: self.unit,
396            const_labels: self.const_labels,
397            family_labels: Some(label_set),
398        })
399    }
400
401    pub fn encode_histogram<S: EncodeLabelSet>(
402        &mut self,
403        sum: f64,
404        count: u64,
405        buckets: &[(f64, u64)],
406        exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
407    ) -> Result<(), std::fmt::Error> {
408        self.write_prefix_name_unit()?;
409        self.write_suffix("sum")?;
410        self.encode_labels::<NoLabelSet>(None)?;
411        self.writer.write_str(" ")?;
412        self.writer.write_str(dtoa::Buffer::new().format(sum))?;
413        self.newline()?;
414
415        self.write_prefix_name_unit()?;
416        self.write_suffix("count")?;
417        self.encode_labels::<NoLabelSet>(None)?;
418        self.writer.write_str(" ")?;
419        self.writer.write_str(itoa::Buffer::new().format(count))?;
420        self.newline()?;
421
422        let mut cummulative = 0;
423        for (i, (upper_bound, count)) in buckets.iter().enumerate() {
424            cummulative += count;
425
426            self.write_prefix_name_unit()?;
427            self.write_suffix("bucket")?;
428
429            if *upper_bound == f64::INFINITY {
430                self.encode_labels(Some(&[("le", "+Inf")]))?;
431            } else {
432                self.encode_labels(Some(&[("le", *upper_bound)]))?;
433            }
434
435            self.writer.write_str(" ")?;
436            self.writer
437                .write_str(itoa::Buffer::new().format(cummulative))?;
438
439            if let Some(exemplar) = exemplars.and_then(|e| e.get(&i)) {
440                self.encode_exemplar(exemplar)?
441            }
442
443            self.newline()?;
444        }
445
446        Ok(())
447    }
448
449    pub fn encode_histogram_with_native<S: EncodeLabelSet>(
450        &mut self,
451        sum: f64,
452        count: u64,
453        buckets: &[(f64, u64)],
454        exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
455        _native: NativeHistogram<'_>,
456    ) -> Result<(), std::fmt::Error> {
457        if buckets.is_empty() {
458            return Err(std::fmt::Error);
459        }
460
461        self.encode_histogram(sum, count, buckets, exemplars)
462    }
463
464    /// Encode an exemplar for the given metric.
465    fn encode_exemplar<S: EncodeLabelSet, V: EncodeExemplarValue>(
466        &mut self,
467        exemplar: &Exemplar<S, V>,
468    ) -> Result<(), std::fmt::Error> {
469        self.writer.write_str(" # {")?;
470        exemplar
471            .label_set
472            .encode(&mut LabelSetEncoder::new(self.writer).into())?;
473        self.writer.write_str("} ")?;
474        exemplar.value.encode(
475            ExemplarValueEncoder {
476                writer: self.writer,
477            }
478            .into(),
479        )?;
480        if let Some(timestamp) = exemplar.timestamp {
481            self.writer.write_char(' ')?;
482            timestamp.encode(
483                ExemplarValueEncoder {
484                    writer: self.writer,
485                }
486                .into(),
487            )?;
488        }
489        Ok(())
490    }
491
492    fn newline(&mut self) -> Result<(), std::fmt::Error> {
493        self.writer.write_str("\n")
494    }
495    fn write_prefix_name_unit(&mut self) -> Result<(), std::fmt::Error> {
496        if let Some(prefix) = self.prefix {
497            self.writer.write_str(prefix.as_str())?;
498            self.writer.write_str("_")?;
499        }
500        self.writer.write_str(self.name)?;
501        if let Some(unit) = self.unit {
502            self.writer.write_str("_")?;
503            self.writer.write_str(unit.as_str())?;
504        }
505
506        Ok(())
507    }
508
509    fn write_suffix(&mut self, suffix: &'static str) -> Result<(), std::fmt::Error> {
510        self.writer.write_str("_")?;
511        self.writer.write_str(suffix)?;
512
513        Ok(())
514    }
515
516    // TODO: Consider caching the encoded labels for Histograms as they stay the
517    // same but are currently encoded multiple times.
518    fn encode_labels<S: EncodeLabelSet>(
519        &mut self,
520        additional_labels: Option<&S>,
521    ) -> Result<(), std::fmt::Error> {
522        if self.const_labels.is_empty()
523            && additional_labels.is_none()
524            && self.family_labels.is_none()
525        {
526            return Ok(());
527        }
528
529        self.writer.write_str("{")?;
530
531        self.const_labels
532            .encode(&mut LabelSetEncoder::new(self.writer).into())?;
533
534        if let Some(additional_labels) = additional_labels {
535            if !self.const_labels.is_empty() {
536                self.writer.write_str(",")?;
537            }
538
539            additional_labels.encode(&mut LabelSetEncoder::new(self.writer).into())?;
540        }
541
542        /// Writer impl which prepends a comma on the first call to write output to the wrapped writer
543        struct CommaPrependingWriter<'a> {
544            writer: &'a mut dyn Write,
545            should_prepend: bool,
546        }
547
548        impl Write for CommaPrependingWriter<'_> {
549            fn write_str(&mut self, s: &str) -> std::fmt::Result {
550                if self.should_prepend {
551                    self.writer.write_char(',')?;
552                    self.should_prepend = false;
553                }
554                self.writer.write_str(s)
555            }
556        }
557
558        if let Some(labels) = self.family_labels {
559            // if const labels or additional labels have been written, a comma must be prepended before writing the family labels.
560            // However, it could be the case that the family labels are `Some` and yet empty, so the comma should _only_
561            // be prepended if one of the `Write` methods are actually called when attempting to write the family labels.
562            // Therefore, wrap the writer on `Self` with a CommaPrependingWriter if other labels have been written and
563            // there may be a need to prepend an extra comma before writing additional labels.
564            if !self.const_labels.is_empty() || additional_labels.is_some() {
565                let mut writer = CommaPrependingWriter {
566                    writer: self.writer,
567                    should_prepend: true,
568                };
569                labels.encode(&mut LabelSetEncoder::new(&mut writer).into())?;
570            } else {
571                labels.encode(&mut LabelSetEncoder::new(self.writer).into())?;
572            };
573        }
574
575        self.writer.write_str("}")?;
576
577        Ok(())
578    }
579}
580
581pub(crate) struct CounterValueEncoder<'a> {
582    writer: &'a mut dyn Write,
583}
584
585impl std::fmt::Debug for CounterValueEncoder<'_> {
586    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
587        f.debug_struct("CounterValueEncoder").finish()
588    }
589}
590
591impl CounterValueEncoder<'_> {
592    pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
593        self.writer.write_str(" ")?;
594        self.writer.write_str(dtoa::Buffer::new().format(v))?;
595        Ok(())
596    }
597
598    pub fn encode_u64(&mut self, v: u64) -> Result<(), std::fmt::Error> {
599        self.writer.write_str(" ")?;
600        self.writer.write_str(itoa::Buffer::new().format(v))?;
601        Ok(())
602    }
603}
604
605pub(crate) struct GaugeValueEncoder<'a> {
606    writer: &'a mut dyn Write,
607}
608
609impl std::fmt::Debug for GaugeValueEncoder<'_> {
610    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611        f.debug_struct("GaugeValueEncoder").finish()
612    }
613}
614
615impl GaugeValueEncoder<'_> {
616    pub fn encode_u32(&mut self, v: u32) -> Result<(), std::fmt::Error> {
617        self.writer.write_str(" ")?;
618        self.writer.write_str(itoa::Buffer::new().format(v))?;
619        Ok(())
620    }
621
622    pub fn encode_u64(&mut self, v: u64) -> Result<(), std::fmt::Error> {
623        self.writer.write_str(" ")?;
624        self.writer.write_str(itoa::Buffer::new().format(v))?;
625        Ok(())
626    }
627
628    pub fn encode_i64(&mut self, v: i64) -> Result<(), std::fmt::Error> {
629        self.writer.write_str(" ")?;
630        self.writer.write_str(itoa::Buffer::new().format(v))?;
631        Ok(())
632    }
633
634    pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
635        self.writer.write_str(" ")?;
636        self.writer.write_str(dtoa::Buffer::new().format(v))?;
637        Ok(())
638    }
639}
640
641pub(crate) struct ExemplarValueEncoder<'a> {
642    writer: &'a mut dyn Write,
643}
644
645impl std::fmt::Debug for ExemplarValueEncoder<'_> {
646    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
647        f.debug_struct("ExemplarValueEncoder").finish()
648    }
649}
650
651impl ExemplarValueEncoder<'_> {
652    pub fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> {
653        self.writer.write_str(dtoa::Buffer::new().format(v))
654    }
655}
656
657pub(crate) struct LabelSetEncoder<'a> {
658    writer: &'a mut dyn Write,
659    first: bool,
660}
661
662impl std::fmt::Debug for LabelSetEncoder<'_> {
663    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
664        f.debug_struct("LabelSetEncoder")
665            .field("first", &self.first)
666            .finish()
667    }
668}
669
670impl<'a> LabelSetEncoder<'a> {
671    fn new(writer: &'a mut dyn Write) -> Self {
672        Self {
673            writer,
674            first: true,
675        }
676    }
677
678    pub fn encode_label(&mut self) -> LabelEncoder<'_> {
679        let first = self.first;
680        self.first = false;
681        LabelEncoder {
682            writer: self.writer,
683            first,
684        }
685    }
686}
687
688pub(crate) struct LabelEncoder<'a> {
689    writer: &'a mut dyn Write,
690    first: bool,
691}
692
693impl std::fmt::Debug for LabelEncoder<'_> {
694    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
695        f.debug_struct("LabelEncoder")
696            .field("first", &self.first)
697            .finish()
698    }
699}
700
701impl LabelEncoder<'_> {
702    pub fn encode_label_key(&mut self) -> Result<LabelKeyEncoder<'_>, std::fmt::Error> {
703        if !self.first {
704            self.writer.write_str(",")?;
705        }
706        Ok(LabelKeyEncoder {
707            writer: self.writer,
708        })
709    }
710}
711
712pub(crate) struct LabelKeyEncoder<'a> {
713    writer: &'a mut dyn Write,
714}
715
716impl std::fmt::Debug for LabelKeyEncoder<'_> {
717    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718        f.debug_struct("LabelKeyEncoder").finish()
719    }
720}
721
722impl<'a> LabelKeyEncoder<'a> {
723    pub fn encode_label_value(self) -> Result<LabelValueEncoder<'a>, std::fmt::Error> {
724        self.writer.write_str("=\"")?;
725        Ok(LabelValueEncoder {
726            writer: self.writer,
727        })
728    }
729}
730
731impl std::fmt::Write for LabelKeyEncoder<'_> {
732    fn write_str(&mut self, s: &str) -> std::fmt::Result {
733        self.writer.write_str(s)
734    }
735}
736
737pub(crate) struct LabelValueEncoder<'a> {
738    writer: &'a mut dyn Write,
739}
740
741impl std::fmt::Debug for LabelValueEncoder<'_> {
742    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
743        f.debug_struct("LabelValueEncoder").finish()
744    }
745}
746
747impl LabelValueEncoder<'_> {
748    pub fn finish(self) -> Result<(), std::fmt::Error> {
749        self.writer.write_str("\"")
750    }
751
752    pub(crate) fn write_str_unescaped(&mut self, s: &str) -> Result<(), std::fmt::Error> {
753        self.writer.write_str(s)
754    }
755
756    #[cold]
757    fn write_str_escaped(&mut self, s: &str) -> Result<(), std::fmt::Error> {
758        let mut last = 0;
759        for (index, byte) in s.bytes().enumerate() {
760            let escaped = match byte {
761                b'\\' => "\\\\",
762                b'"' => "\\\"",
763                b'\n' => "\\n",
764                _ => continue,
765            };
766            self.writer.write_str(&s[last..index])?;
767            self.writer.write_str(escaped)?;
768            last = index + 1;
769        }
770        self.writer.write_str(&s[last..])
771    }
772}
773
774impl std::fmt::Write for LabelValueEncoder<'_> {
775    fn write_str(&mut self, s: &str) -> std::fmt::Result {
776        let needs_escaping = s.bytes().fold(false, |found, byte| {
777            found | matches!(byte, b'\\' | b'"' | b'\n')
778        });
779        if needs_escaping {
780            self.write_str_escaped(s)
781        } else {
782            self.write_str_unescaped(s)
783        }
784    }
785}
786
787#[cfg(test)]
788mod tests {
789    use super::*;
790    use crate::metrics::exemplar::HistogramWithExemplars;
791    use crate::metrics::family::Family;
792    use crate::metrics::gauge::Gauge;
793    use crate::metrics::histogram::{exponential_buckets, Histogram, NativeHistogramConfig};
794    use crate::metrics::info::Info;
795    use crate::metrics::{counter::Counter, exemplar::CounterWithExemplar};
796    use pyo3::{prelude::*, types::PyModule};
797    use quickcheck::QuickCheck;
798    use std::borrow::Cow;
799    use std::fmt::Error;
800    use std::sync::atomic::{AtomicI32, AtomicU32};
801    use std::time::{SystemTime, UNIX_EPOCH};
802
803    #[test]
804    fn label_values_escape_special_characters() {
805        let mut encoded = String::new();
806        let mut label_set_encoder = LabelSetEncoder::new(&mut encoded);
807        let mut label_encoder = label_set_encoder.encode_label();
808        let mut key_encoder = label_encoder.encode_label_key().unwrap();
809        key_encoder.write_str("label").unwrap();
810        let mut value_encoder = key_encoder.encode_label_value().unwrap();
811        value_encoder
812            .write_str("plain \\ quoted \" line\ncarriage\r unicode λ")
813            .unwrap();
814        value_encoder.finish().unwrap();
815
816        assert_eq!(
817            concat!(
818                r#"label="plain \\ quoted \" line\ncarriage"#,
819                "\r",
820                r#" unicode λ""#
821            ),
822            encoded
823        );
824    }
825
826    #[test]
827    fn label_value_escaping_matches_reference() {
828        fn reference(s: &str) -> String {
829            let mut escaped = String::new();
830            for character in s.chars() {
831                match character {
832                    '\\' => escaped.push_str("\\\\"),
833                    '"' => escaped.push_str("\\\""),
834                    '\n' => escaped.push_str("\\n"),
835                    _ => escaped.push(character),
836                }
837            }
838            escaped
839        }
840
841        fn encode(value: &str) -> String {
842            let mut encoded = String::new();
843            let mut encoder = LabelValueEncoder {
844                writer: &mut encoded,
845            };
846            encoder.write_str(value).unwrap();
847            encoded
848        }
849
850        fn prop(value: String) -> bool {
851            if encode(&value) != reference(&value) {
852                return false;
853            }
854
855            // Guarantee that QuickCheck exercises a newline without another special
856            // byte that could independently select the escaping path.
857            let newline_only = format!("{}\n", value.replace(['\\', '"', '\n'], ""));
858            encode(&newline_only) == reference(&newline_only)
859        }
860
861        QuickCheck::new()
862            .tests(1_000)
863            .quickcheck(prop as fn(String) -> bool);
864    }
865
866    #[test]
867    fn escaped_label_values_produce_parseable_exposition() {
868        let mut registry = Registry::default();
869        let family = Family::<Vec<(String, String)>, Counter>::default();
870        registry.register("requests", "Requests", family.clone());
871        family
872            .get_or_create(&vec![(
873                "client_version".to_string(),
874                "a\"} evil{x=\"1\\line\nnext".to_string(),
875            )])
876            .inc();
877
878        let mut encoded = String::new();
879        encode(&mut encoded, &registry).unwrap();
880
881        assert_eq!(
882            concat!(
883                "# HELP requests Requests.\n",
884                "# TYPE requests counter\n",
885                r#"requests_total{client_version="a\"} evil{x=\"1\\line\nnext"} 1"#,
886                "\n# EOF\n"
887            ),
888            encoded
889        );
890        parse_with_python_client(encoded);
891    }
892
893    #[test]
894    fn encode_counter() {
895        let counter: Counter = Counter::default();
896        let mut registry = Registry::default();
897        registry.register("my_counter", "My counter", counter);
898
899        let counter_f32 = Counter::<f32, AtomicU32>::default();
900        registry.register("f32_counter", "Counter::<f32, AtomicU32>", counter_f32);
901        let counter_u32 = Counter::<u32, AtomicU32>::default();
902        registry.register("u32_counter", "Counter::<u32, AtomicU32>", counter_u32);
903        let mut encoded = String::new();
904
905        encode(&mut encoded, &registry).unwrap();
906
907        parse_with_python_client(encoded);
908    }
909
910    #[test]
911    fn encode_counter_with_unit() {
912        let mut registry = Registry::default();
913        let counter: Counter = Counter::default();
914        registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter);
915
916        let mut encoded = String::new();
917        encode(&mut encoded, &registry).unwrap();
918
919        let expected = "# HELP my_counter_seconds My counter.\n".to_owned()
920            + "# TYPE my_counter_seconds counter\n"
921            + "# UNIT my_counter_seconds seconds\n"
922            + "my_counter_seconds_total 0\n"
923            + "# EOF\n";
924        assert_eq!(expected, encoded);
925
926        parse_with_python_client(encoded);
927    }
928
929    #[test]
930    fn encode_counter_with_exemplar() {
931        let now = SystemTime::now();
932
933        let mut registry = Registry::default();
934
935        let counter_with_exemplar: CounterWithExemplar<Vec<(String, u64)>> =
936            CounterWithExemplar::default();
937        registry.register_with_unit(
938            "my_counter_with_exemplar",
939            "My counter with exemplar",
940            Unit::Seconds,
941            counter_with_exemplar.clone(),
942        );
943
944        counter_with_exemplar.inc_by(1, Some(vec![("user_id".to_string(), 42)]), None);
945
946        let mut encoded = String::new();
947        encode(&mut encoded, &registry).unwrap();
948
949        let expected = "# HELP my_counter_with_exemplar_seconds My counter with exemplar.\n"
950            .to_owned()
951            + "# TYPE my_counter_with_exemplar_seconds counter\n"
952            + "# UNIT my_counter_with_exemplar_seconds seconds\n"
953            + "my_counter_with_exemplar_seconds_total 1 # {user_id=\"42\"} 1.0\n"
954            + "# EOF\n";
955        assert_eq!(expected, encoded);
956
957        parse_with_python_client(encoded);
958
959        counter_with_exemplar.inc_by(1, Some(vec![("user_id".to_string(), 99)]), Some(now));
960
961        let mut encoded = String::new();
962        encode(&mut encoded, &registry).unwrap();
963
964        let expected = "# HELP my_counter_with_exemplar_seconds My counter with exemplar.\n"
965            .to_owned()
966            + "# TYPE my_counter_with_exemplar_seconds counter\n"
967            + "# UNIT my_counter_with_exemplar_seconds seconds\n"
968            + "my_counter_with_exemplar_seconds_total 2 # {user_id=\"99\"} 1.0 "
969            + dtoa::Buffer::new().format(now.duration_since(UNIX_EPOCH).unwrap().as_secs_f64())
970            + "\n"
971            + "# EOF\n";
972        assert_eq!(expected, encoded);
973
974        parse_with_python_client(encoded);
975    }
976
977    #[test]
978    fn encode_gauge() {
979        let mut registry = Registry::default();
980        let gauge: Gauge = Gauge::default();
981        registry.register("my_gauge", "My gauge", gauge);
982        let gauge = Gauge::<u32, AtomicU32>::default();
983        registry.register("u32_gauge", "Gauge::<u32, AtomicU32>", gauge);
984
985        let gauge_f32 = Gauge::<f32, AtomicU32>::default();
986        registry.register("f32_gauge", "Gauge::<f32, AtomicU32>", gauge_f32);
987
988        let gauge_i32 = Gauge::<i32, AtomicI32>::default();
989        registry.register("i32_gauge", "Gauge::<i32, AtomicU32>", gauge_i32);
990
991        let mut encoded = String::new();
992
993        encode(&mut encoded, &registry).unwrap();
994
995        parse_with_python_client(encoded);
996    }
997
998    #[test]
999    fn encode_counter_family() {
1000        let mut registry = Registry::default();
1001        let family = Family::<Vec<(String, String)>, Counter>::default();
1002        registry.register("my_counter_family", "My counter family", family.clone());
1003
1004        family
1005            .get_or_create(&vec![
1006                ("method".to_string(), "GET".to_string()),
1007                ("status".to_string(), "200".to_string()),
1008            ])
1009            .inc();
1010
1011        let mut encoded = String::new();
1012
1013        encode(&mut encoded, &registry).unwrap();
1014
1015        parse_with_python_client(encoded);
1016    }
1017
1018    #[test]
1019    fn encode_counter_family_with_prefix_with_label() {
1020        let mut registry = Registry::default();
1021        let sub_registry = registry.sub_registry_with_prefix("my_prefix");
1022        let sub_sub_registry = sub_registry
1023            .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
1024        let family = Family::<Vec<(String, String)>, Counter>::default();
1025        sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
1026
1027        family
1028            .get_or_create(&vec![
1029                ("method".to_string(), "GET".to_string()),
1030                ("status".to_string(), "200".to_string()),
1031            ])
1032            .inc();
1033
1034        let mut encoded = String::new();
1035
1036        encode(&mut encoded, &registry).unwrap();
1037
1038        let expected = "# HELP my_prefix_my_counter_family My counter family.\n"
1039            .to_owned()
1040            + "# TYPE my_prefix_my_counter_family counter\n"
1041            + "my_prefix_my_counter_family_total{my_key=\"my_value\",method=\"GET\",status=\"200\"} 1\n"
1042            + "# EOF\n";
1043        assert_eq!(expected, encoded);
1044
1045        parse_with_python_client(encoded);
1046    }
1047
1048    #[test]
1049    fn encode_info() {
1050        let mut registry = Registry::default();
1051        let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
1052        registry.register("my_info_metric", "My info metric", info);
1053
1054        let mut encoded = String::new();
1055        encode(&mut encoded, &registry).unwrap();
1056
1057        let expected = "# HELP my_info_metric My info metric.\n".to_owned()
1058            + "# TYPE my_info_metric info\n"
1059            + "my_info_metric_info{os=\"GNU/linux\"} 1\n"
1060            + "# EOF\n";
1061        assert_eq!(expected, encoded);
1062
1063        parse_with_python_client(encoded);
1064    }
1065
1066    #[test]
1067    fn encode_histogram() {
1068        let mut registry = Registry::default();
1069        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
1070        registry.register("my_histogram", "My histogram", histogram.clone());
1071        histogram.observe(1.0);
1072
1073        let mut encoded = String::new();
1074
1075        encode(&mut encoded, &registry).unwrap();
1076
1077        parse_with_python_client(encoded);
1078    }
1079
1080    #[test]
1081    fn encode_classic_and_native_histogram_as_classic_text() {
1082        let mut registry = Registry::default();
1083        let histogram = Histogram::new_classic_and_native(
1084            [1.0, 2.0],
1085            NativeHistogramConfig::with_bucket_factor(1.1),
1086        );
1087        registry.register("my_histogram", "My histogram", histogram.clone());
1088        histogram.observe(1.0);
1089
1090        let mut encoded = String::new();
1091        encode(&mut encoded, &registry).unwrap();
1092
1093        let expected = "# HELP my_histogram My histogram.\n".to_owned()
1094            + "# TYPE my_histogram histogram\n"
1095            + "my_histogram_sum 1.0\n"
1096            + "my_histogram_count 1\n"
1097            + "my_histogram_bucket{le=\"1.0\"} 1\n"
1098            + "my_histogram_bucket{le=\"2.0\"} 1\n"
1099            + "my_histogram_bucket{le=\"+Inf\"} 1\n"
1100            + "# EOF\n";
1101        assert_eq!(expected, encoded);
1102    }
1103
1104    #[test]
1105    fn encode_native_only_histogram_errors() {
1106        let mut registry = Registry::default();
1107        let histogram = Histogram::new_native(NativeHistogramConfig::with_schema(0));
1108        registry.register("my_histogram", "My histogram", histogram.clone());
1109        histogram.observe(1.0);
1110
1111        let mut encoded = String::new();
1112        assert!(encode(&mut encoded, &registry).is_err());
1113    }
1114
1115    #[test]
1116    fn encode_histogram_family() {
1117        let mut registry = Registry::default();
1118        let family =
1119            Family::new_with_constructor(|| Histogram::new(exponential_buckets(1.0, 2.0, 10)));
1120        registry.register("my_histogram", "My histogram", family.clone());
1121        family
1122            .get_or_create(&vec![
1123                ("method".to_string(), "GET".to_string()),
1124                ("status".to_string(), "200".to_string()),
1125            ])
1126            .observe(1.0);
1127
1128        let mut encoded = String::new();
1129
1130        encode(&mut encoded, &registry).unwrap();
1131
1132        parse_with_python_client(encoded);
1133    }
1134
1135    #[test]
1136    fn encode_histogram_family_with_empty_struct_family_labels() {
1137        let mut registry = Registry::default();
1138        let family =
1139            Family::new_with_constructor(|| Histogram::new(exponential_buckets(1.0, 2.0, 10)));
1140        registry.register("my_histogram", "My histogram", family.clone());
1141
1142        #[derive(Eq, PartialEq, Hash, Debug, Clone)]
1143        struct EmptyLabels {}
1144
1145        impl EncodeLabelSet for EmptyLabels {
1146            fn encode(&self, _encoder: &mut crate::encoding::LabelSetEncoder) -> Result<(), Error> {
1147                Ok(())
1148            }
1149        }
1150
1151        family.get_or_create(&EmptyLabels {}).observe(1.0);
1152
1153        let mut encoded = String::new();
1154
1155        encode(&mut encoded, &registry).unwrap();
1156
1157        parse_with_python_client(encoded);
1158    }
1159
1160    #[test]
1161    fn encode_histogram_with_exemplars() {
1162        let now = SystemTime::now();
1163
1164        let mut registry = Registry::default();
1165        let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
1166        registry.register("my_histogram", "My histogram", histogram.clone());
1167
1168        histogram.observe(1.0, Some([("user_id".to_string(), 42u64)]), Some(now));
1169        histogram.observe(2.0, Some([("user_id".to_string(), 99u64)]), None);
1170
1171        let mut encoded = String::new();
1172        encode(&mut encoded, &registry).unwrap();
1173
1174        let expected = "# HELP my_histogram My histogram.\n".to_owned()
1175            + "# TYPE my_histogram histogram\n"
1176            + "my_histogram_sum 3.0\n"
1177            + "my_histogram_count 2\n"
1178            + "my_histogram_bucket{le=\"1.0\"} 1 # {user_id=\"42\"} 1.0 "
1179            + dtoa::Buffer::new().format(now.duration_since(UNIX_EPOCH).unwrap().as_secs_f64())
1180            + "\n"
1181            + "my_histogram_bucket{le=\"2.0\"} 2 # {user_id=\"99\"} 2.0\n"
1182            + "my_histogram_bucket{le=\"4.0\"} 2\n"
1183            + "my_histogram_bucket{le=\"8.0\"} 2\n"
1184            + "my_histogram_bucket{le=\"16.0\"} 2\n"
1185            + "my_histogram_bucket{le=\"32.0\"} 2\n"
1186            + "my_histogram_bucket{le=\"64.0\"} 2\n"
1187            + "my_histogram_bucket{le=\"128.0\"} 2\n"
1188            + "my_histogram_bucket{le=\"256.0\"} 2\n"
1189            + "my_histogram_bucket{le=\"512.0\"} 2\n"
1190            + "my_histogram_bucket{le=\"+Inf\"} 2\n"
1191            + "# EOF\n";
1192        assert_eq!(expected, encoded);
1193
1194        parse_with_python_client(encoded);
1195    }
1196
1197    #[test]
1198    fn sub_registry_with_prefix_and_label() {
1199        let top_level_metric_name = "my_top_level_metric";
1200        let mut registry = Registry::default();
1201        let counter: Counter = Counter::default();
1202        registry.register(top_level_metric_name, "some help", counter.clone());
1203
1204        let prefix_1 = "prefix_1";
1205        let prefix_1_metric_name = "my_prefix_1_metric";
1206        let sub_registry = registry.sub_registry_with_prefix(prefix_1);
1207        sub_registry.register(prefix_1_metric_name, "some help", counter.clone());
1208
1209        let prefix_1_1 = "prefix_1_1";
1210        let prefix_1_1_metric_name = "my_prefix_1_1_metric";
1211        let sub_sub_registry = sub_registry.sub_registry_with_prefix(prefix_1_1);
1212        sub_sub_registry.register(prefix_1_1_metric_name, "some help", counter.clone());
1213
1214        let label_1_2 = (Cow::Borrowed("registry"), Cow::Borrowed("1_2"));
1215        let prefix_1_2_metric_name = "my_prefix_1_2_metric";
1216        let sub_sub_registry = sub_registry.sub_registry_with_label(label_1_2.clone());
1217        sub_sub_registry.register(prefix_1_2_metric_name, "some help", counter.clone());
1218
1219        let labels_1_3 = vec![
1220            (Cow::Borrowed("label_1_3_1"), Cow::Borrowed("value_1_3_1")),
1221            (Cow::Borrowed("label_1_3_2"), Cow::Borrowed("value_1_3_2")),
1222        ];
1223        let prefix_1_3_metric_name = "my_prefix_1_3_metric";
1224        let sub_sub_registry =
1225            sub_registry.sub_registry_with_labels(labels_1_3.clone().into_iter());
1226        sub_sub_registry.register(prefix_1_3_metric_name, "some help", counter.clone());
1227
1228        let prefix_1_3_1 = "prefix_1_3_1";
1229        let prefix_1_3_1_metric_name = "my_prefix_1_3_1_metric";
1230        let sub_sub_sub_registry = sub_sub_registry.sub_registry_with_prefix(prefix_1_3_1);
1231        sub_sub_sub_registry.register(prefix_1_3_1_metric_name, "some help", counter.clone());
1232
1233        let prefix_2 = "prefix_2";
1234        let _ = registry.sub_registry_with_prefix(prefix_2);
1235
1236        let prefix_3 = "prefix_3";
1237        let prefix_3_metric_name = "my_prefix_3_metric";
1238        let sub_registry = registry.sub_registry_with_prefix(prefix_3);
1239        sub_registry.register(prefix_3_metric_name, "some help", counter);
1240
1241        let mut encoded = String::new();
1242        encode(&mut encoded, &registry).unwrap();
1243
1244        let expected = "# HELP my_top_level_metric some help.\n".to_owned()
1245            + "# TYPE my_top_level_metric counter\n"
1246            + "my_top_level_metric_total 0\n"
1247            + "# HELP prefix_1_my_prefix_1_metric some help.\n"
1248            + "# TYPE prefix_1_my_prefix_1_metric counter\n"
1249            + "prefix_1_my_prefix_1_metric_total 0\n"
1250            + "# HELP prefix_1_prefix_1_1_my_prefix_1_1_metric some help.\n"
1251            + "# TYPE prefix_1_prefix_1_1_my_prefix_1_1_metric counter\n"
1252            + "prefix_1_prefix_1_1_my_prefix_1_1_metric_total 0\n"
1253            + "# HELP prefix_1_my_prefix_1_2_metric some help.\n"
1254            + "# TYPE prefix_1_my_prefix_1_2_metric counter\n"
1255            + "prefix_1_my_prefix_1_2_metric_total{registry=\"1_2\"} 0\n"
1256            + "# HELP prefix_1_my_prefix_1_3_metric some help.\n"
1257            + "# TYPE prefix_1_my_prefix_1_3_metric counter\n"
1258            + "prefix_1_my_prefix_1_3_metric_total{label_1_3_1=\"value_1_3_1\",label_1_3_2=\"value_1_3_2\"} 0\n"
1259            + "# HELP prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric some help.\n"
1260            + "# TYPE prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric counter\n"
1261            + "prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric_total{label_1_3_1=\"value_1_3_1\",label_1_3_2=\"value_1_3_2\"} 0\n"
1262            + "# HELP prefix_3_my_prefix_3_metric some help.\n"
1263            + "# TYPE prefix_3_my_prefix_3_metric counter\n"
1264            + "prefix_3_my_prefix_3_metric_total 0\n"
1265            + "# EOF\n";
1266        assert_eq!(expected, encoded);
1267
1268        parse_with_python_client(encoded);
1269    }
1270
1271    #[test]
1272    fn sub_registry_collector() {
1273        use crate::encoding::EncodeMetric;
1274
1275        #[derive(Debug)]
1276        struct Collector {
1277            name: String,
1278        }
1279
1280        impl Collector {
1281            fn new(name: impl Into<String>) -> Self {
1282                Self { name: name.into() }
1283            }
1284        }
1285
1286        impl crate::collector::Collector for Collector {
1287            fn encode(
1288                &self,
1289                mut encoder: crate::encoding::DescriptorEncoder,
1290            ) -> Result<(), std::fmt::Error> {
1291                let counter = crate::metrics::counter::ConstCounter::new(42u64);
1292                let metric_encoder = encoder.encode_descriptor(
1293                    &self.name,
1294                    "some help",
1295                    None,
1296                    counter.metric_type(),
1297                )?;
1298                counter.encode(metric_encoder)?;
1299                Ok(())
1300            }
1301        }
1302
1303        let mut registry = Registry::default();
1304        registry.register_collector(Box::new(Collector::new("top_level")));
1305
1306        let sub_registry = registry.sub_registry_with_prefix("prefix_1");
1307        sub_registry.register_collector(Box::new(Collector::new("sub_level")));
1308
1309        let sub_sub_registry = sub_registry.sub_registry_with_prefix("prefix_1_2");
1310        sub_sub_registry.register_collector(Box::new(Collector::new("sub_sub_level")));
1311
1312        let mut encoded = String::new();
1313        encode(&mut encoded, &registry).unwrap();
1314
1315        let expected = "# HELP top_level some help\n".to_owned()
1316            + "# TYPE top_level counter\n"
1317            + "top_level_total 42\n"
1318            + "# HELP prefix_1_sub_level some help\n"
1319            + "# TYPE prefix_1_sub_level counter\n"
1320            + "prefix_1_sub_level_total 42\n"
1321            + "# HELP prefix_1_prefix_1_2_sub_sub_level some help\n"
1322            + "# TYPE prefix_1_prefix_1_2_sub_sub_level counter\n"
1323            + "prefix_1_prefix_1_2_sub_sub_level_total 42\n"
1324            + "# EOF\n";
1325        assert_eq!(expected, encoded);
1326
1327        parse_with_python_client(encoded);
1328    }
1329
1330    #[test]
1331    fn label_sets_can_be_composed() {
1332        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
1333        struct Color(&'static str);
1334        impl EncodeLabelSet for Color {
1335            fn encode(
1336                &self,
1337                encoder: &mut crate::encoding::LabelSetEncoder,
1338            ) -> Result<(), std::fmt::Error> {
1339                use crate::encoding::EncodeLabel;
1340                let Self(color) = *self;
1341                let labels = ("color", color);
1342                let encoder = encoder.encode_label();
1343                labels.encode(encoder)
1344            }
1345        }
1346
1347        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
1348        struct Size(&'static str);
1349        impl EncodeLabelSet for Size {
1350            fn encode(
1351                &self,
1352                encoder: &mut crate::encoding::LabelSetEncoder,
1353            ) -> Result<(), std::fmt::Error> {
1354                use crate::encoding::EncodeLabel;
1355                let Self(size) = *self;
1356                let labels = ("size", size);
1357                let encoder = encoder.encode_label();
1358                labels.encode(encoder)
1359            }
1360        }
1361
1362        type Labels = (Color, Size);
1363
1364        let mut registry = Registry::default();
1365        let family = Family::<Labels, Counter>::default();
1366        registry.register("items", "Example metric", family.clone());
1367
1368        let labels = (Color("red"), Size("large"));
1369        let counter = family.get_or_create(&labels);
1370        counter.inc();
1371
1372        let mut encoded = String::new();
1373        encode(&mut encoded, &registry).unwrap();
1374
1375        let expected = "# HELP items Example metric.\n\
1376                        # TYPE items counter\n\
1377                        items_total{color=\"red\",size=\"large\"} 1\n\
1378                        # EOF\n";
1379        assert_eq!(expected, encoded);
1380
1381        parse_with_python_client(encoded);
1382    }
1383
1384    #[test]
1385    fn encode_registry_eof() {
1386        let mut orders_registry = Registry::default();
1387
1388        let total_orders: Counter<u64> = Default::default();
1389        orders_registry.register("orders", "Total orders received", total_orders.clone());
1390        total_orders.inc();
1391
1392        let processing_times = Histogram::new(exponential_buckets(1.0, 2.0, 10));
1393        orders_registry.register_with_unit(
1394            "processing_times",
1395            "Order times",
1396            Unit::Seconds,
1397            processing_times.clone(),
1398        );
1399        processing_times.observe(2.4);
1400
1401        let mut user_auth_registry = Registry::default();
1402
1403        let successful_logins: Counter<u64> = Default::default();
1404        user_auth_registry.register(
1405            "successful_logins",
1406            "Total successful logins",
1407            successful_logins.clone(),
1408        );
1409        successful_logins.inc();
1410
1411        let failed_logins: Counter<u64> = Default::default();
1412        user_auth_registry.register(
1413            "failed_logins",
1414            "Total failed logins",
1415            failed_logins.clone(),
1416        );
1417
1418        let mut response = String::new();
1419
1420        encode_registry(&mut response, &orders_registry).unwrap();
1421        assert_eq!(&response[response.len() - 20..], "bucket{le=\"+Inf\"} 1\n");
1422
1423        encode_registry(&mut response, &user_auth_registry).unwrap();
1424        assert_eq!(&response[response.len() - 20..], "iled_logins_total 0\n");
1425
1426        encode_eof(&mut response).unwrap();
1427        assert_eq!(&response[response.len() - 20..], "ogins_total 0\n# EOF\n");
1428    }
1429
1430    fn parse_with_python_client(input: String) {
1431        pyo3::Python::initialize();
1432
1433        println!("{input:?}");
1434        Python::attach(|py| {
1435            let parser = PyModule::from_code(
1436                py,
1437                c"
1438from prometheus_client.openmetrics.parser import text_string_to_metric_families
1439
1440def parse(input):
1441    families = text_string_to_metric_families(input)
1442    list(families)
1443",
1444                c"parser.py",
1445                c"parser",
1446            )
1447            .map_err(|e| e.to_string())
1448            .unwrap();
1449
1450            parser
1451                .getattr("parse")
1452                .expect("`parse` to exist.")
1453                .call1((input.clone(),))
1454                .map_err(|e| e.to_string())
1455                .unwrap();
1456        })
1457    }
1458
1459    #[test]
1460    fn encode_omit_empty() {
1461        let mut registry = Registry::default();
1462        let counter1: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1463        let counter2: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1464        let counter3: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1465
1466        registry.register("counter1", "First counter", counter1.clone());
1467        registry.register("counter2", "Second counter", counter2.clone());
1468        registry.register("counter3", "Third counter", counter3.clone());
1469
1470        counter1.get_or_create(&vec![("label", "value")]).inc();
1471
1472        let mut encoded = String::new();
1473        encode(&mut encoded, &registry).unwrap();
1474
1475        let expected = "# HELP counter1 First counter.\n".to_owned()
1476            + "# TYPE counter1 counter\n"
1477            + "counter1_total{label=\"value\"} 1\n"
1478            + "# EOF\n";
1479        assert_eq!(expected, encoded);
1480
1481        counter2.get_or_create(&vec![("label", "value")]).inc();
1482
1483        let mut encoded = String::new();
1484        encode(&mut encoded, &registry).unwrap();
1485
1486        let expected = "# HELP counter1 First counter.\n".to_owned()
1487            + "# TYPE counter1 counter\n"
1488            + "counter1_total{label=\"value\"} 1\n"
1489            + "# HELP counter2 Second counter.\n"
1490            + "# TYPE counter2 counter\n"
1491            + "counter2_total{label=\"value\"} 1\n"
1492            + "# EOF\n";
1493        assert_eq!(expected, encoded);
1494    }
1495}