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::MAX {
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
753impl std::fmt::Write for LabelValueEncoder<'_> {
754 fn write_str(&mut self, s: &str) -> std::fmt::Result {
755 self.writer.write_str(s)
756 }
757}
758
759#[cfg(test)]
760mod tests {
761 use super::*;
762 use crate::metrics::exemplar::HistogramWithExemplars;
763 use crate::metrics::family::Family;
764 use crate::metrics::gauge::Gauge;
765 use crate::metrics::histogram::{exponential_buckets, Histogram, NativeHistogramConfig};
766 use crate::metrics::info::Info;
767 use crate::metrics::{counter::Counter, exemplar::CounterWithExemplar};
768 use pyo3::{prelude::*, types::PyModule};
769 use std::borrow::Cow;
770 use std::fmt::Error;
771 use std::sync::atomic::{AtomicI32, AtomicU32};
772 use std::time::{SystemTime, UNIX_EPOCH};
773
774 #[test]
775 fn encode_counter() {
776 let counter: Counter = Counter::default();
777 let mut registry = Registry::default();
778 registry.register("my_counter", "My counter", counter);
779
780 let counter_f32 = Counter::<f32, AtomicU32>::default();
781 registry.register("f32_counter", "Counter::<f32, AtomicU32>", counter_f32);
782 let counter_u32 = Counter::<u32, AtomicU32>::default();
783 registry.register("u32_counter", "Counter::<u32, AtomicU32>", counter_u32);
784 let mut encoded = String::new();
785
786 encode(&mut encoded, ®istry).unwrap();
787
788 parse_with_python_client(encoded);
789 }
790
791 #[test]
792 fn encode_counter_with_unit() {
793 let mut registry = Registry::default();
794 let counter: Counter = Counter::default();
795 registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter);
796
797 let mut encoded = String::new();
798 encode(&mut encoded, ®istry).unwrap();
799
800 let expected = "# HELP my_counter_seconds My counter.\n".to_owned()
801 + "# TYPE my_counter_seconds counter\n"
802 + "# UNIT my_counter_seconds seconds\n"
803 + "my_counter_seconds_total 0\n"
804 + "# EOF\n";
805 assert_eq!(expected, encoded);
806
807 parse_with_python_client(encoded);
808 }
809
810 #[test]
811 fn encode_counter_with_exemplar() {
812 let now = SystemTime::now();
813
814 let mut registry = Registry::default();
815
816 let counter_with_exemplar: CounterWithExemplar<Vec<(String, u64)>> =
817 CounterWithExemplar::default();
818 registry.register_with_unit(
819 "my_counter_with_exemplar",
820 "My counter with exemplar",
821 Unit::Seconds,
822 counter_with_exemplar.clone(),
823 );
824
825 counter_with_exemplar.inc_by(1, Some(vec![("user_id".to_string(), 42)]), None);
826
827 let mut encoded = String::new();
828 encode(&mut encoded, ®istry).unwrap();
829
830 let expected = "# HELP my_counter_with_exemplar_seconds My counter with exemplar.\n"
831 .to_owned()
832 + "# TYPE my_counter_with_exemplar_seconds counter\n"
833 + "# UNIT my_counter_with_exemplar_seconds seconds\n"
834 + "my_counter_with_exemplar_seconds_total 1 # {user_id=\"42\"} 1.0\n"
835 + "# EOF\n";
836 assert_eq!(expected, encoded);
837
838 parse_with_python_client(encoded);
839
840 counter_with_exemplar.inc_by(1, Some(vec![("user_id".to_string(), 99)]), Some(now));
841
842 let mut encoded = String::new();
843 encode(&mut encoded, ®istry).unwrap();
844
845 let expected = "# HELP my_counter_with_exemplar_seconds My counter with exemplar.\n"
846 .to_owned()
847 + "# TYPE my_counter_with_exemplar_seconds counter\n"
848 + "# UNIT my_counter_with_exemplar_seconds seconds\n"
849 + "my_counter_with_exemplar_seconds_total 2 # {user_id=\"99\"} 1.0 "
850 + dtoa::Buffer::new().format(now.duration_since(UNIX_EPOCH).unwrap().as_secs_f64())
851 + "\n"
852 + "# EOF\n";
853 assert_eq!(expected, encoded);
854
855 parse_with_python_client(encoded);
856 }
857
858 #[test]
859 fn encode_gauge() {
860 let mut registry = Registry::default();
861 let gauge: Gauge = Gauge::default();
862 registry.register("my_gauge", "My gauge", gauge);
863 let gauge = Gauge::<u32, AtomicU32>::default();
864 registry.register("u32_gauge", "Gauge::<u32, AtomicU32>", gauge);
865
866 let gauge_f32 = Gauge::<f32, AtomicU32>::default();
867 registry.register("f32_gauge", "Gauge::<f32, AtomicU32>", gauge_f32);
868
869 let gauge_i32 = Gauge::<i32, AtomicI32>::default();
870 registry.register("i32_gauge", "Gauge::<i32, AtomicU32>", gauge_i32);
871
872 let mut encoded = String::new();
873
874 encode(&mut encoded, ®istry).unwrap();
875
876 parse_with_python_client(encoded);
877 }
878
879 #[test]
880 fn encode_counter_family() {
881 let mut registry = Registry::default();
882 let family = Family::<Vec<(String, String)>, Counter>::default();
883 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, ®istry).unwrap();
895
896 parse_with_python_client(encoded);
897 }
898
899 #[test]
900 fn encode_counter_family_with_prefix_with_label() {
901 let mut registry = Registry::default();
902 let sub_registry = registry.sub_registry_with_prefix("my_prefix");
903 let sub_sub_registry = sub_registry
904 .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
905 let family = Family::<Vec<(String, String)>, Counter>::default();
906 sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
907
908 family
909 .get_or_create(&vec![
910 ("method".to_string(), "GET".to_string()),
911 ("status".to_string(), "200".to_string()),
912 ])
913 .inc();
914
915 let mut encoded = String::new();
916
917 encode(&mut encoded, ®istry).unwrap();
918
919 let expected = "# HELP my_prefix_my_counter_family My counter family.\n"
920 .to_owned()
921 + "# TYPE my_prefix_my_counter_family counter\n"
922 + "my_prefix_my_counter_family_total{my_key=\"my_value\",method=\"GET\",status=\"200\"} 1\n"
923 + "# EOF\n";
924 assert_eq!(expected, encoded);
925
926 parse_with_python_client(encoded);
927 }
928
929 #[test]
930 fn encode_info() {
931 let mut registry = Registry::default();
932 let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
933 registry.register("my_info_metric", "My info metric", info);
934
935 let mut encoded = String::new();
936 encode(&mut encoded, ®istry).unwrap();
937
938 let expected = "# HELP my_info_metric My info metric.\n".to_owned()
939 + "# TYPE my_info_metric info\n"
940 + "my_info_metric_info{os=\"GNU/linux\"} 1\n"
941 + "# EOF\n";
942 assert_eq!(expected, encoded);
943
944 parse_with_python_client(encoded);
945 }
946
947 #[test]
948 fn encode_histogram() {
949 let mut registry = Registry::default();
950 let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
951 registry.register("my_histogram", "My histogram", histogram.clone());
952 histogram.observe(1.0);
953
954 let mut encoded = String::new();
955
956 encode(&mut encoded, ®istry).unwrap();
957
958 parse_with_python_client(encoded);
959 }
960
961 #[test]
962 fn encode_classic_and_native_histogram_as_classic_text() {
963 let mut registry = Registry::default();
964 let histogram = Histogram::new_classic_and_native(
965 [1.0, 2.0],
966 NativeHistogramConfig::with_bucket_factor(1.1),
967 );
968 registry.register("my_histogram", "My histogram", histogram.clone());
969 histogram.observe(1.0);
970
971 let mut encoded = String::new();
972 encode(&mut encoded, ®istry).unwrap();
973
974 let expected = "# HELP my_histogram My histogram.\n".to_owned()
975 + "# TYPE my_histogram histogram\n"
976 + "my_histogram_sum 1.0\n"
977 + "my_histogram_count 1\n"
978 + "my_histogram_bucket{le=\"1.0\"} 1\n"
979 + "my_histogram_bucket{le=\"2.0\"} 1\n"
980 + "my_histogram_bucket{le=\"+Inf\"} 1\n"
981 + "# EOF\n";
982 assert_eq!(expected, encoded);
983 }
984
985 #[test]
986 fn encode_native_only_histogram_errors() {
987 let mut registry = Registry::default();
988 let histogram = Histogram::new_native(NativeHistogramConfig::with_schema(0));
989 registry.register("my_histogram", "My histogram", histogram.clone());
990 histogram.observe(1.0);
991
992 let mut encoded = String::new();
993 assert!(encode(&mut encoded, ®istry).is_err());
994 }
995
996 #[test]
997 fn encode_histogram_family() {
998 let mut registry = Registry::default();
999 let family =
1000 Family::new_with_constructor(|| Histogram::new(exponential_buckets(1.0, 2.0, 10)));
1001 registry.register("my_histogram", "My histogram", family.clone());
1002 family
1003 .get_or_create(&vec![
1004 ("method".to_string(), "GET".to_string()),
1005 ("status".to_string(), "200".to_string()),
1006 ])
1007 .observe(1.0);
1008
1009 let mut encoded = String::new();
1010
1011 encode(&mut encoded, ®istry).unwrap();
1012
1013 parse_with_python_client(encoded);
1014 }
1015
1016 #[test]
1017 fn encode_histogram_family_with_empty_struct_family_labels() {
1018 let mut registry = Registry::default();
1019 let family =
1020 Family::new_with_constructor(|| Histogram::new(exponential_buckets(1.0, 2.0, 10)));
1021 registry.register("my_histogram", "My histogram", family.clone());
1022
1023 #[derive(Eq, PartialEq, Hash, Debug, Clone)]
1024 struct EmptyLabels {}
1025
1026 impl EncodeLabelSet for EmptyLabels {
1027 fn encode(&self, _encoder: &mut crate::encoding::LabelSetEncoder) -> Result<(), Error> {
1028 Ok(())
1029 }
1030 }
1031
1032 family.get_or_create(&EmptyLabels {}).observe(1.0);
1033
1034 let mut encoded = String::new();
1035
1036 encode(&mut encoded, ®istry).unwrap();
1037
1038 parse_with_python_client(encoded);
1039 }
1040
1041 #[test]
1042 fn encode_histogram_with_exemplars() {
1043 let now = SystemTime::now();
1044
1045 let mut registry = Registry::default();
1046 let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
1047 registry.register("my_histogram", "My histogram", histogram.clone());
1048
1049 histogram.observe(1.0, Some([("user_id".to_string(), 42u64)]), Some(now));
1050 histogram.observe(2.0, Some([("user_id".to_string(), 99u64)]), None);
1051
1052 let mut encoded = String::new();
1053 encode(&mut encoded, ®istry).unwrap();
1054
1055 let expected = "# HELP my_histogram My histogram.\n".to_owned()
1056 + "# TYPE my_histogram histogram\n"
1057 + "my_histogram_sum 3.0\n"
1058 + "my_histogram_count 2\n"
1059 + "my_histogram_bucket{le=\"1.0\"} 1 # {user_id=\"42\"} 1.0 "
1060 + dtoa::Buffer::new().format(now.duration_since(UNIX_EPOCH).unwrap().as_secs_f64())
1061 + "\n"
1062 + "my_histogram_bucket{le=\"2.0\"} 2 # {user_id=\"99\"} 2.0\n"
1063 + "my_histogram_bucket{le=\"4.0\"} 2\n"
1064 + "my_histogram_bucket{le=\"8.0\"} 2\n"
1065 + "my_histogram_bucket{le=\"16.0\"} 2\n"
1066 + "my_histogram_bucket{le=\"32.0\"} 2\n"
1067 + "my_histogram_bucket{le=\"64.0\"} 2\n"
1068 + "my_histogram_bucket{le=\"128.0\"} 2\n"
1069 + "my_histogram_bucket{le=\"256.0\"} 2\n"
1070 + "my_histogram_bucket{le=\"512.0\"} 2\n"
1071 + "my_histogram_bucket{le=\"+Inf\"} 2\n"
1072 + "# EOF\n";
1073 assert_eq!(expected, encoded);
1074
1075 parse_with_python_client(encoded);
1076 }
1077
1078 #[test]
1079 fn sub_registry_with_prefix_and_label() {
1080 let top_level_metric_name = "my_top_level_metric";
1081 let mut registry = Registry::default();
1082 let counter: Counter = Counter::default();
1083 registry.register(top_level_metric_name, "some help", counter.clone());
1084
1085 let prefix_1 = "prefix_1";
1086 let prefix_1_metric_name = "my_prefix_1_metric";
1087 let sub_registry = registry.sub_registry_with_prefix(prefix_1);
1088 sub_registry.register(prefix_1_metric_name, "some help", counter.clone());
1089
1090 let prefix_1_1 = "prefix_1_1";
1091 let prefix_1_1_metric_name = "my_prefix_1_1_metric";
1092 let sub_sub_registry = sub_registry.sub_registry_with_prefix(prefix_1_1);
1093 sub_sub_registry.register(prefix_1_1_metric_name, "some help", counter.clone());
1094
1095 let label_1_2 = (Cow::Borrowed("registry"), Cow::Borrowed("1_2"));
1096 let prefix_1_2_metric_name = "my_prefix_1_2_metric";
1097 let sub_sub_registry = sub_registry.sub_registry_with_label(label_1_2.clone());
1098 sub_sub_registry.register(prefix_1_2_metric_name, "some help", counter.clone());
1099
1100 let labels_1_3 = vec![
1101 (Cow::Borrowed("label_1_3_1"), Cow::Borrowed("value_1_3_1")),
1102 (Cow::Borrowed("label_1_3_2"), Cow::Borrowed("value_1_3_2")),
1103 ];
1104 let prefix_1_3_metric_name = "my_prefix_1_3_metric";
1105 let sub_sub_registry =
1106 sub_registry.sub_registry_with_labels(labels_1_3.clone().into_iter());
1107 sub_sub_registry.register(prefix_1_3_metric_name, "some help", counter.clone());
1108
1109 let prefix_1_3_1 = "prefix_1_3_1";
1110 let prefix_1_3_1_metric_name = "my_prefix_1_3_1_metric";
1111 let sub_sub_sub_registry = sub_sub_registry.sub_registry_with_prefix(prefix_1_3_1);
1112 sub_sub_sub_registry.register(prefix_1_3_1_metric_name, "some help", counter.clone());
1113
1114 let prefix_2 = "prefix_2";
1115 let _ = registry.sub_registry_with_prefix(prefix_2);
1116
1117 let prefix_3 = "prefix_3";
1118 let prefix_3_metric_name = "my_prefix_3_metric";
1119 let sub_registry = registry.sub_registry_with_prefix(prefix_3);
1120 sub_registry.register(prefix_3_metric_name, "some help", counter);
1121
1122 let mut encoded = String::new();
1123 encode(&mut encoded, ®istry).unwrap();
1124
1125 let expected = "# HELP my_top_level_metric some help.\n".to_owned()
1126 + "# TYPE my_top_level_metric counter\n"
1127 + "my_top_level_metric_total 0\n"
1128 + "# HELP prefix_1_my_prefix_1_metric some help.\n"
1129 + "# TYPE prefix_1_my_prefix_1_metric counter\n"
1130 + "prefix_1_my_prefix_1_metric_total 0\n"
1131 + "# HELP prefix_1_prefix_1_1_my_prefix_1_1_metric some help.\n"
1132 + "# TYPE prefix_1_prefix_1_1_my_prefix_1_1_metric counter\n"
1133 + "prefix_1_prefix_1_1_my_prefix_1_1_metric_total 0\n"
1134 + "# HELP prefix_1_my_prefix_1_2_metric some help.\n"
1135 + "# TYPE prefix_1_my_prefix_1_2_metric counter\n"
1136 + "prefix_1_my_prefix_1_2_metric_total{registry=\"1_2\"} 0\n"
1137 + "# HELP prefix_1_my_prefix_1_3_metric some help.\n"
1138 + "# TYPE prefix_1_my_prefix_1_3_metric counter\n"
1139 + "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"
1140 + "# HELP prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric some help.\n"
1141 + "# TYPE prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric counter\n"
1142 + "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"
1143 + "# HELP prefix_3_my_prefix_3_metric some help.\n"
1144 + "# TYPE prefix_3_my_prefix_3_metric counter\n"
1145 + "prefix_3_my_prefix_3_metric_total 0\n"
1146 + "# EOF\n";
1147 assert_eq!(expected, encoded);
1148
1149 parse_with_python_client(encoded);
1150 }
1151
1152 #[test]
1153 fn sub_registry_collector() {
1154 use crate::encoding::EncodeMetric;
1155
1156 #[derive(Debug)]
1157 struct Collector {
1158 name: String,
1159 }
1160
1161 impl Collector {
1162 fn new(name: impl Into<String>) -> Self {
1163 Self { name: name.into() }
1164 }
1165 }
1166
1167 impl crate::collector::Collector for Collector {
1168 fn encode(
1169 &self,
1170 mut encoder: crate::encoding::DescriptorEncoder,
1171 ) -> Result<(), std::fmt::Error> {
1172 let counter = crate::metrics::counter::ConstCounter::new(42u64);
1173 let metric_encoder = encoder.encode_descriptor(
1174 &self.name,
1175 "some help",
1176 None,
1177 counter.metric_type(),
1178 )?;
1179 counter.encode(metric_encoder)?;
1180 Ok(())
1181 }
1182 }
1183
1184 let mut registry = Registry::default();
1185 registry.register_collector(Box::new(Collector::new("top_level")));
1186
1187 let sub_registry = registry.sub_registry_with_prefix("prefix_1");
1188 sub_registry.register_collector(Box::new(Collector::new("sub_level")));
1189
1190 let sub_sub_registry = sub_registry.sub_registry_with_prefix("prefix_1_2");
1191 sub_sub_registry.register_collector(Box::new(Collector::new("sub_sub_level")));
1192
1193 let mut encoded = String::new();
1194 encode(&mut encoded, ®istry).unwrap();
1195
1196 let expected = "# HELP top_level some help\n".to_owned()
1197 + "# TYPE top_level counter\n"
1198 + "top_level_total 42\n"
1199 + "# HELP prefix_1_sub_level some help\n"
1200 + "# TYPE prefix_1_sub_level counter\n"
1201 + "prefix_1_sub_level_total 42\n"
1202 + "# HELP prefix_1_prefix_1_2_sub_sub_level some help\n"
1203 + "# TYPE prefix_1_prefix_1_2_sub_sub_level counter\n"
1204 + "prefix_1_prefix_1_2_sub_sub_level_total 42\n"
1205 + "# EOF\n";
1206 assert_eq!(expected, encoded);
1207
1208 parse_with_python_client(encoded);
1209 }
1210
1211 #[test]
1212 fn label_sets_can_be_composed() {
1213 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
1214 struct Color(&'static str);
1215 impl EncodeLabelSet for Color {
1216 fn encode(
1217 &self,
1218 encoder: &mut crate::encoding::LabelSetEncoder,
1219 ) -> Result<(), std::fmt::Error> {
1220 use crate::encoding::EncodeLabel;
1221 let Self(color) = *self;
1222 let labels = ("color", color);
1223 let encoder = encoder.encode_label();
1224 labels.encode(encoder)
1225 }
1226 }
1227
1228 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
1229 struct Size(&'static str);
1230 impl EncodeLabelSet for Size {
1231 fn encode(
1232 &self,
1233 encoder: &mut crate::encoding::LabelSetEncoder,
1234 ) -> Result<(), std::fmt::Error> {
1235 use crate::encoding::EncodeLabel;
1236 let Self(size) = *self;
1237 let labels = ("size", size);
1238 let encoder = encoder.encode_label();
1239 labels.encode(encoder)
1240 }
1241 }
1242
1243 type Labels = (Color, Size);
1244
1245 let mut registry = Registry::default();
1246 let family = Family::<Labels, Counter>::default();
1247 registry.register("items", "Example metric", family.clone());
1248
1249 let labels = (Color("red"), Size("large"));
1250 let counter = family.get_or_create(&labels);
1251 counter.inc();
1252
1253 let mut encoded = String::new();
1254 encode(&mut encoded, ®istry).unwrap();
1255
1256 let expected = "# HELP items Example metric.\n\
1257 # TYPE items counter\n\
1258 items_total{color=\"red\",size=\"large\"} 1\n\
1259 # EOF\n";
1260 assert_eq!(expected, encoded);
1261
1262 parse_with_python_client(encoded);
1263 }
1264
1265 #[test]
1266 fn encode_registry_eof() {
1267 let mut orders_registry = Registry::default();
1268
1269 let total_orders: Counter<u64> = Default::default();
1270 orders_registry.register("orders", "Total orders received", total_orders.clone());
1271 total_orders.inc();
1272
1273 let processing_times = Histogram::new(exponential_buckets(1.0, 2.0, 10));
1274 orders_registry.register_with_unit(
1275 "processing_times",
1276 "Order times",
1277 Unit::Seconds,
1278 processing_times.clone(),
1279 );
1280 processing_times.observe(2.4);
1281
1282 let mut user_auth_registry = Registry::default();
1283
1284 let successful_logins: Counter<u64> = Default::default();
1285 user_auth_registry.register(
1286 "successful_logins",
1287 "Total successful logins",
1288 successful_logins.clone(),
1289 );
1290 successful_logins.inc();
1291
1292 let failed_logins: Counter<u64> = Default::default();
1293 user_auth_registry.register(
1294 "failed_logins",
1295 "Total failed logins",
1296 failed_logins.clone(),
1297 );
1298
1299 let mut response = String::new();
1300
1301 encode_registry(&mut response, &orders_registry).unwrap();
1302 assert_eq!(&response[response.len() - 20..], "bucket{le=\"+Inf\"} 1\n");
1303
1304 encode_registry(&mut response, &user_auth_registry).unwrap();
1305 assert_eq!(&response[response.len() - 20..], "iled_logins_total 0\n");
1306
1307 encode_eof(&mut response).unwrap();
1308 assert_eq!(&response[response.len() - 20..], "ogins_total 0\n# EOF\n");
1309 }
1310
1311 fn parse_with_python_client(input: String) {
1312 pyo3::Python::initialize();
1313
1314 println!("{input:?}");
1315 Python::attach(|py| {
1316 let parser = PyModule::from_code(
1317 py,
1318 c"
1319from prometheus_client.openmetrics.parser import text_string_to_metric_families
1320
1321def parse(input):
1322 families = text_string_to_metric_families(input)
1323 list(families)
1324",
1325 c"parser.py",
1326 c"parser",
1327 )
1328 .map_err(|e| e.to_string())
1329 .unwrap();
1330
1331 parser
1332 .getattr("parse")
1333 .expect("`parse` to exist.")
1334 .call1((input.clone(),))
1335 .map_err(|e| e.to_string())
1336 .unwrap();
1337 })
1338 }
1339
1340 #[test]
1341 fn encode_omit_empty() {
1342 let mut registry = Registry::default();
1343 let counter1: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1344 let counter2: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1345 let counter3: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1346
1347 registry.register("counter1", "First counter", counter1.clone());
1348 registry.register("counter2", "Second counter", counter2.clone());
1349 registry.register("counter3", "Third counter", counter3.clone());
1350
1351 counter1.get_or_create(&vec![("label", "value")]).inc();
1352
1353 let mut encoded = String::new();
1354 encode(&mut encoded, ®istry).unwrap();
1355
1356 let expected = "# HELP counter1 First counter.\n".to_owned()
1357 + "# TYPE counter1 counter\n"
1358 + "counter1_total{label=\"value\"} 1\n"
1359 + "# EOF\n";
1360 assert_eq!(expected, encoded);
1361
1362 counter2.get_or_create(&vec![("label", "value")]).inc();
1363
1364 let mut encoded = String::new();
1365 encode(&mut encoded, ®istry).unwrap();
1366
1367 let expected = "# HELP counter1 First counter.\n".to_owned()
1368 + "# TYPE counter1 counter\n"
1369 + "counter1_total{label=\"value\"} 1\n"
1370 + "# HELP counter2 Second counter.\n"
1371 + "# TYPE counter2 counter\n"
1372 + "counter2_total{label=\"value\"} 1\n"
1373 + "# EOF\n";
1374 assert_eq!(expected, encoded);
1375 }
1376}