1use 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
51pub 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
97pub 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
150pub 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
280pub(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 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 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 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 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 !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 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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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}