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