1use 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
49pub 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
95pub 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
148pub 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
278pub(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 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 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 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 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 !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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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, ®istry).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}