1#[allow(missing_docs, clippy::derive_partial_eq_without_eq)]
32pub mod prometheus_data_model {
34 include!(concat!(env!("OUT_DIR"), "/io.prometheus.client.rs"));
35}
36
37use prost::Message;
38use std::{
39 borrow::Cow,
40 collections::HashMap,
41 time::{SystemTime, UNIX_EPOCH},
42};
43
44use crate::metrics::MetricType;
45use crate::registry::{Registry, Unit};
46use crate::{metrics::exemplar::Exemplar, registry::Prefix};
47
48use super::{
49 EncodeCounterValue, EncodeExemplarValue, EncodeGaugeValue, EncodeLabelSet, NativeHistogram,
50};
51
52pub fn encode(
55 registry: &Registry,
56) -> Result<Vec<prometheus_data_model::MetricFamily>, std::fmt::Error> {
57 let mut metric_families = Vec::new();
58 let mut descriptor_encoder = DescriptorEncoder::new(&mut metric_families).into();
59 registry.encode(&mut descriptor_encoder)?;
60 Ok(metric_families)
61}
62
63pub fn encode_to_vec(registry: &Registry) -> Result<Vec<u8>, EncodeError> {
66 let metric_families = encode(registry)?;
67 let mut encoded = Vec::new();
68
69 for metric_family in metric_families {
70 metric_family.encode_length_delimited(&mut encoded)?;
71 }
72
73 Ok(encoded)
74}
75
76#[derive(Debug)]
78pub enum EncodeError {
79 Fmt(std::fmt::Error),
81 Protobuf(prost::EncodeError),
83}
84
85impl std::fmt::Display for EncodeError {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 EncodeError::Fmt(_) => f.write_str("failed to encode metrics into Prometheus protobuf"),
89 EncodeError::Protobuf(err) => err.fmt(f),
90 }
91 }
92}
93
94impl std::error::Error for EncodeError {
95 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
96 match self {
97 EncodeError::Fmt(err) => Some(err),
98 EncodeError::Protobuf(err) => Some(err),
99 }
100 }
101}
102
103impl From<std::fmt::Error> for EncodeError {
104 fn from(err: std::fmt::Error) -> Self {
105 EncodeError::Fmt(err)
106 }
107}
108
109impl From<prost::EncodeError> for EncodeError {
110 fn from(err: prost::EncodeError) -> Self {
111 EncodeError::Protobuf(err)
112 }
113}
114
115impl From<MetricType> for prometheus_data_model::MetricType {
116 fn from(metric_type: MetricType) -> Self {
117 match metric_type {
118 MetricType::Counter => prometheus_data_model::MetricType::Counter,
119 MetricType::Gauge => prometheus_data_model::MetricType::Gauge,
120 MetricType::Histogram => prometheus_data_model::MetricType::Histogram,
121 MetricType::Info => prometheus_data_model::MetricType::Gauge,
124 MetricType::Unknown => prometheus_data_model::MetricType::Untyped,
125 }
126 }
127}
128
129fn metric_family_name(
130 prefix: Option<&Prefix>,
131 name: &str,
132 unit: Option<&Unit>,
133 metric_type: MetricType,
134) -> String {
135 let mut full_name = String::new();
136
137 if let Some(prefix) = prefix {
138 full_name.push_str(prefix.as_str());
139 full_name.push('_');
140 }
141
142 full_name.push_str(name);
143
144 if let Some(unit) = unit {
145 full_name.push('_');
146 full_name.push_str(unit.as_str());
147 }
148
149 match metric_type {
150 MetricType::Counter => full_name.push_str("_total"),
151 MetricType::Info => full_name.push_str("_info"),
152 MetricType::Gauge | MetricType::Histogram | MetricType::Unknown => {}
153 }
154
155 full_name
156}
157
158#[derive(Debug)]
160pub(crate) struct DescriptorEncoder<'a> {
161 metric_families: &'a mut Vec<prometheus_data_model::MetricFamily>,
162 prefix: Option<&'a Prefix>,
163 labels: &'a [(Cow<'static, str>, Cow<'static, str>)],
164}
165
166impl DescriptorEncoder<'_> {
167 pub(crate) fn new(
168 metric_families: &mut Vec<prometheus_data_model::MetricFamily>,
169 ) -> DescriptorEncoder<'_> {
170 DescriptorEncoder {
171 metric_families,
172 prefix: Default::default(),
173 labels: Default::default(),
174 }
175 }
176
177 pub(crate) fn with_prefix_and_labels<'s>(
178 &'s mut self,
179 prefix: Option<&'s Prefix>,
180 labels: &'s [(Cow<'static, str>, Cow<'static, str>)],
181 ) -> DescriptorEncoder<'s> {
182 DescriptorEncoder {
183 prefix,
184 labels,
185 metric_families: self.metric_families,
186 }
187 }
188
189 pub fn encode_descriptor<'s>(
190 &'s mut self,
191 name: &str,
192 help: &str,
193 unit: Option<&Unit>,
194 metric_type: MetricType,
195 ) -> Result<MetricEncoder<'s>, std::fmt::Error> {
196 let family = prometheus_data_model::MetricFamily {
197 name: metric_family_name(self.prefix, name, unit, metric_type),
198 r#type: prometheus_data_model::MetricType::from(metric_type) as i32,
199 unit: unit
200 .map(|unit| unit.as_str().to_string())
201 .unwrap_or_default(),
202 help: help.to_string(),
203 ..Default::default()
204 };
205 let mut labels = vec![];
206 self.labels.encode(
207 &mut LabelSetEncoder {
208 labels: &mut labels,
209 }
210 .into(),
211 )?;
212 self.metric_families.push(family);
213
214 Ok(MetricEncoder {
215 family: &mut self
216 .metric_families
217 .last_mut()
218 .expect("previous push")
219 .metric,
220 metric_type,
221 labels,
222 })
223 }
224}
225
226#[derive(Debug)]
230pub(crate) struct MetricEncoder<'f> {
231 metric_type: MetricType,
233 family: &'f mut Vec<prometheus_data_model::Metric>,
235 labels: Vec<prometheus_data_model::LabelPair>,
237}
238
239impl MetricEncoder<'_> {
240 pub fn encode_counter<
241 S: EncodeLabelSet,
242 CounterValue: EncodeCounterValue,
243 ExemplarValue: EncodeExemplarValue,
244 >(
245 &mut self,
246 v: &CounterValue,
247 exemplar: Option<&Exemplar<S, ExemplarValue>>,
248 ) -> Result<(), std::fmt::Error> {
249 let mut value = 0.0;
250 let mut e = CounterValueEncoder { value: &mut value }.into();
251 v.encode(&mut e)?;
252
253 self.family.push(prometheus_data_model::Metric {
254 label: self.labels.clone(),
255 counter: Some(prometheus_data_model::Counter {
256 value,
257 exemplar: exemplar.map(TryInto::try_into).transpose()?,
258 start_timestamp: None,
259 }),
260 ..Default::default()
261 });
262
263 Ok(())
264 }
265
266 pub fn encode_gauge<GaugeValue: EncodeGaugeValue>(
267 &mut self,
268 v: &GaugeValue,
269 ) -> Result<(), std::fmt::Error> {
270 let mut value = 0.0;
271 let mut e = GaugeValueEncoder { value: &mut value }.into();
272 v.encode(&mut e)?;
273
274 self.family.push(prometheus_data_model::Metric {
275 label: self.labels.clone(),
276 gauge: Some(prometheus_data_model::Gauge { value }),
277 ..Default::default()
278 });
279
280 Ok(())
281 }
282
283 pub fn encode_info(
284 &mut self,
285 label_set: &impl super::EncodeLabelSet,
286 ) -> Result<(), std::fmt::Error> {
287 let mut labels = self.labels.clone();
288 label_set.encode(
289 &mut LabelSetEncoder {
290 labels: &mut labels,
291 }
292 .into(),
293 )?;
294
295 self.family.push(prometheus_data_model::Metric {
296 label: labels,
297 gauge: Some(prometheus_data_model::Gauge { value: 1.0 }),
298 ..Default::default()
299 });
300
301 Ok(())
302 }
303
304 pub fn encode_family<S: EncodeLabelSet>(
305 &mut self,
306 label_set: &S,
307 ) -> Result<MetricEncoder<'_>, std::fmt::Error> {
308 let mut labels = self.labels.clone();
309 label_set.encode(
310 &mut LabelSetEncoder {
311 labels: &mut labels,
312 }
313 .into(),
314 )?;
315
316 Ok(MetricEncoder {
317 metric_type: self.metric_type,
318 family: self.family,
319 labels,
320 })
321 }
322
323 pub fn encode_histogram<S: EncodeLabelSet>(
324 &mut self,
325 sum: f64,
326 count: u64,
327 buckets: &[(f64, u64)],
328 exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
329 ) -> Result<(), std::fmt::Error> {
330 let bucket = classic_buckets(buckets, exemplars)?;
331
332 self.family.push(prometheus_data_model::Metric {
333 label: self.labels.clone(),
334 histogram: Some(prometheus_data_model::Histogram {
335 sample_count: count,
336 sample_count_float: 0.0,
337 sample_sum: sum,
338 bucket,
339 start_timestamp: None,
340 ..Default::default()
341 }),
342 ..Default::default()
343 });
344
345 Ok(())
346 }
347
348 pub fn encode_histogram_with_native<S: EncodeLabelSet>(
349 &mut self,
350 sum: f64,
351 count: u64,
352 buckets: &[(f64, u64)],
353 exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
354 native: NativeHistogram<'_>,
355 ) -> Result<(), std::fmt::Error> {
356 let bucket = classic_buckets(buckets, exemplars)?;
357 let start_timestamp = native.created.map(system_time_to_timestamp).transpose()?;
358
359 let negative_span = native
360 .negative
361 .spans
362 .iter()
363 .map(|(offset, length)| prometheus_data_model::BucketSpan {
364 offset: *offset,
365 length: *length,
366 })
367 .collect();
368
369 let positive_span = native
370 .positive
371 .spans
372 .iter()
373 .map(|(offset, length)| prometheus_data_model::BucketSpan {
374 offset: *offset,
375 length: *length,
376 })
377 .collect();
378
379 self.family.push(prometheus_data_model::Metric {
380 label: self.labels.clone(),
381 histogram: Some(prometheus_data_model::Histogram {
382 sample_count: count,
383 sample_count_float: 0.0,
384 sample_sum: sum,
385 bucket,
386 start_timestamp,
387 schema: native.schema,
388 zero_threshold: native.zero_threshold,
389 zero_count: native.zero_count,
390 zero_count_float: 0.0,
391 negative_span,
392 negative_delta: native.negative.deltas.to_vec(),
393 negative_count: Vec::new(),
394 positive_span,
395 positive_delta: native.positive.deltas.to_vec(),
396 positive_count: Vec::new(),
397 exemplars: Vec::new(),
398 }),
399 ..Default::default()
400 });
401
402 Ok(())
403 }
404}
405
406fn classic_buckets<S: EncodeLabelSet>(
407 buckets: &[(f64, u64)],
408 exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
409) -> Result<Vec<prometheus_data_model::Bucket>, std::fmt::Error> {
410 let mut cumulative_count = 0;
411 buckets
412 .iter()
413 .enumerate()
414 .map(|(i, (upper_bound, count))| {
415 cumulative_count += count;
416 Ok(prometheus_data_model::Bucket {
417 cumulative_count,
418 cumulative_count_float: 0.0,
420 upper_bound: *upper_bound,
421 exemplar: exemplars
422 .and_then(|exemplars| exemplars.get(&i).map(|exemplar| exemplar.try_into()))
423 .transpose()?,
424 })
425 })
426 .collect()
427}
428
429fn system_time_to_timestamp(
430 system_time: SystemTime,
431) -> Result<prost_types::Timestamp, std::fmt::Error> {
432 let duration = system_time
433 .duration_since(UNIX_EPOCH)
434 .map_err(|_| std::fmt::Error)?;
435
436 Ok(prost_types::Timestamp {
437 seconds: duration.as_secs() as i64,
438 nanos: duration.subsec_nanos() as i32,
439 })
440}
441
442impl<S: EncodeLabelSet, V: EncodeExemplarValue> TryFrom<&Exemplar<S, V>>
443 for prometheus_data_model::Exemplar
444{
445 type Error = std::fmt::Error;
446
447 fn try_from(exemplar: &Exemplar<S, V>) -> Result<Self, Self::Error> {
448 let mut value = f64::default();
449 exemplar
450 .value
451 .encode(ExemplarValueEncoder { value: &mut value }.into())?;
452
453 let mut label = vec![];
454 exemplar
455 .label_set
456 .encode(&mut LabelSetEncoder { labels: &mut label }.into())?;
457
458 Ok(prometheus_data_model::Exemplar {
459 label,
460 value,
461 timestamp: exemplar.timestamp.map(Into::into),
462 })
463 }
464}
465
466#[derive(Debug)]
467pub(crate) struct GaugeValueEncoder<'a> {
468 value: &'a mut f64,
469}
470
471impl GaugeValueEncoder<'_> {
472 pub fn encode_u32(&mut self, v: u32) -> Result<(), std::fmt::Error> {
473 self.encode_f64(f64::from(v))
474 }
475
476 pub fn encode_u64(&mut self, v: u64) -> Result<(), std::fmt::Error> {
477 self.encode_f64(v as f64)
478 }
479
480 pub fn encode_i64(&mut self, v: i64) -> Result<(), std::fmt::Error> {
481 self.encode_f64(v as f64)
482 }
483
484 pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
485 *self.value = v;
486 Ok(())
487 }
488}
489
490#[derive(Debug)]
491pub(crate) struct ExemplarValueEncoder<'a> {
492 value: &'a mut f64,
493}
494
495impl ExemplarValueEncoder<'_> {
496 pub fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> {
497 *self.value = v;
498 Ok(())
499 }
500}
501
502impl<K: ToString, V: ToString> From<&(K, V)> for prometheus_data_model::LabelPair {
503 fn from(kv: &(K, V)) -> Self {
504 prometheus_data_model::LabelPair {
505 name: kv.0.to_string(),
506 value: kv.1.to_string(),
507 }
508 }
509}
510
511#[derive(Debug)]
512pub(crate) struct CounterValueEncoder<'a> {
513 value: &'a mut f64,
514}
515
516impl CounterValueEncoder<'_> {
517 pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
518 *self.value = v;
519 Ok(())
520 }
521
522 pub fn encode_u64(&mut self, v: u64) -> Result<(), std::fmt::Error> {
523 *self.value = v as f64;
524 Ok(())
525 }
526}
527
528#[derive(Debug)]
529pub(crate) struct LabelSetEncoder<'a> {
530 labels: &'a mut Vec<prometheus_data_model::LabelPair>,
531}
532
533impl LabelSetEncoder<'_> {
534 pub fn encode_label(&mut self) -> LabelEncoder<'_> {
535 LabelEncoder {
536 labels: self.labels,
537 }
538 }
539}
540
541#[derive(Debug)]
542pub(crate) struct LabelEncoder<'a> {
543 labels: &'a mut Vec<prometheus_data_model::LabelPair>,
544}
545
546impl LabelEncoder<'_> {
547 pub fn encode_label_key(&mut self) -> Result<LabelKeyEncoder<'_>, std::fmt::Error> {
548 self.labels
549 .push(prometheus_data_model::LabelPair::default());
550
551 Ok(LabelKeyEncoder {
552 label: self.labels.last_mut().expect("To find pushed label."),
553 })
554 }
555}
556
557#[derive(Debug)]
558pub(crate) struct LabelKeyEncoder<'a> {
559 label: &'a mut prometheus_data_model::LabelPair,
560}
561
562impl std::fmt::Write for LabelKeyEncoder<'_> {
563 fn write_str(&mut self, s: &str) -> std::fmt::Result {
564 self.label.name.write_str(s)
565 }
566}
567
568impl<'a> LabelKeyEncoder<'a> {
569 pub fn encode_label_value(self) -> Result<LabelValueEncoder<'a>, std::fmt::Error> {
570 Ok(LabelValueEncoder {
571 label_value: &mut self.label.value,
572 })
573 }
574}
575
576#[derive(Debug)]
577pub(crate) struct LabelValueEncoder<'a> {
578 label_value: &'a mut String,
579}
580
581impl LabelValueEncoder<'_> {
582 pub fn finish(self) -> Result<(), std::fmt::Error> {
583 Ok(())
584 }
585
586 pub(crate) fn write_str_unescaped(&mut self, s: &str) -> Result<(), std::fmt::Error> {
587 self.label_value.push_str(s);
588 Ok(())
589 }
590}
591
592impl std::fmt::Write for LabelValueEncoder<'_> {
593 fn write_str(&mut self, s: &str) -> std::fmt::Result {
594 self.label_value.write_str(s)
595 }
596}
597
598#[cfg(test)]
599mod tests {
600 use prost_types::Timestamp;
601
602 use super::*;
603 use crate::metrics::counter::Counter;
604 use crate::metrics::exemplar::{CounterWithExemplar, HistogramWithExemplars};
605 use crate::metrics::family::Family;
606 use crate::metrics::gauge::Gauge;
607 use crate::metrics::histogram::{
608 exponential_buckets, Histogram, NativeHistogramConfig, NATIVE_HISTOGRAM_ZERO_THRESHOLD_ZERO,
609 };
610 use crate::metrics::info::Info;
611 use crate::registry::Unit;
612 use std::borrow::Cow;
613 use std::collections::HashSet;
614 use std::sync::atomic::AtomicI64;
615 use std::sync::atomic::AtomicU64;
616 use std::time::SystemTime;
617
618 #[test]
619 fn encode_counter_int() {
620 let counter: Counter = Counter::default();
621 let mut registry = Registry::default();
622 registry.register("my_counter", "My counter", counter.clone());
623 counter.inc();
624
625 let metric_families = encode(®istry).unwrap();
626 let family = metric_families.first().unwrap();
627 assert_eq!("my_counter_total", family.name);
628 assert_eq!("My counter.", family.help);
629 assert_eq!(
630 prometheus_data_model::MetricType::Counter as i32,
631 family.r#type
632 );
633
634 let metric = family.metric.first().unwrap();
635 assert_eq!(1.0, metric.counter.as_ref().unwrap().value);
636 }
637
638 #[test]
639 fn encode_counter_with_unit() {
640 let counter: Counter = Counter::default();
641 let mut registry = Registry::default();
642 registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter);
643
644 let metric_families = encode(®istry).unwrap();
645 let family = metric_families.first().unwrap();
646 assert_eq!("my_counter_seconds_total", family.name);
647 assert_eq!("seconds", family.unit);
648 }
649
650 #[test]
651 fn encode_counter_with_exemplar() {
652 let now = SystemTime::now();
653 let now_ts: Timestamp = now.into();
654
655 let mut registry = Registry::default();
656 let counter: CounterWithExemplar<Vec<(String, f64)>, f64> = CounterWithExemplar::default();
657 registry.register("my_counter", "My counter", counter.clone());
658
659 counter.inc_by(1.0, Some(vec![("user_id".to_string(), 42.0)]), None);
660
661 let metric_families = encode(®istry).unwrap();
662 let exemplar = metric_families[0].metric[0]
663 .counter
664 .as_ref()
665 .unwrap()
666 .exemplar
667 .as_ref()
668 .unwrap();
669 assert_eq!(1.0, exemplar.value);
670 assert_eq!(None, exemplar.timestamp);
671 assert_eq!("user_id", exemplar.label[0].name);
672 assert_eq!("42.0", exemplar.label[0].value);
673
674 counter.inc_by(1.0, Some(vec![("user_id".to_string(), 99.0)]), Some(now));
675
676 let metric_families = encode(®istry).unwrap();
677 let counter = metric_families[0].metric[0].counter.as_ref().unwrap();
678 assert_eq!(2.0, counter.value);
679 let exemplar = counter.exemplar.as_ref().unwrap();
680 assert_eq!(1.0, exemplar.value);
681 assert_eq!(Some(now_ts), exemplar.timestamp.clone());
682 assert_eq!("99.0", exemplar.label[0].value);
683 }
684
685 #[test]
686 fn encode_gauge() {
687 let gauge = Gauge::<i64, AtomicI64>::default();
688 let mut registry = Registry::default();
689 registry.register("my_gauge", "My gauge", gauge.clone());
690 gauge.inc();
691
692 let metric_families = encode(®istry).unwrap();
693 let family = metric_families.first().unwrap();
694 assert_eq!("my_gauge", family.name.as_str());
695 assert_eq!(
696 prometheus_data_model::MetricType::Gauge as i32,
697 family.r#type
698 );
699 assert_eq!(1.0, family.metric[0].gauge.as_ref().unwrap().value);
700 }
701
702 #[test]
703 fn encode_gauge_u64_max() {
704 let gauge = Gauge::<u64, AtomicU64>::default();
705 let mut registry = Registry::default();
706 registry.register("my_gauge", "My gauge", gauge.clone());
707 gauge.set(u64::MAX);
708
709 let metric_families = encode(®istry).unwrap();
710 assert_eq!(
711 u64::MAX as f64,
712 metric_families[0].metric[0].gauge.as_ref().unwrap().value
713 );
714 }
715
716 #[test]
717 fn encode_counter_family() {
718 let mut registry = Registry::default();
719
720 let family = Family::<Vec<(String, String)>, Counter>::default();
721 registry.register("my_counter_family", "My counter family", family.clone());
722
723 family
724 .get_or_create(&vec![
725 ("method".to_string(), "GET".to_string()),
726 ("status".to_string(), "200".to_string()),
727 ])
728 .inc();
729
730 family
731 .get_or_create(&vec![
732 ("method".to_string(), "POST".to_string()),
733 ("status".to_string(), "200".to_string()),
734 ])
735 .inc();
736
737 let metric_families = encode(®istry).unwrap();
738 let family = metric_families.first().unwrap();
739 assert_eq!("my_counter_family_total", family.name.as_str());
740 assert_eq!(2, family.metric.len());
741
742 let mut potential_method_value = HashSet::new();
743 potential_method_value.insert("GET");
744 potential_method_value.insert("POST");
745
746 let metric = family.metric.first().unwrap();
747 assert_eq!(2, metric.label.len());
748 assert_eq!("method", metric.label[0].name);
749 assert!(potential_method_value.remove(metric.label[0].value.as_str()));
750 assert_eq!("status", metric.label[1].name);
751 assert_eq!("200", metric.label[1].value);
752
753 let metric2 = &family.metric[1];
754 assert_eq!(2, metric2.label.len());
755 assert_eq!("method", metric2.label[0].name);
756 assert!(potential_method_value.remove(metric2.label[0].value.as_str()));
757 assert_eq!("status", metric2.label[1].name);
758 assert_eq!("200", metric2.label[1].value);
759 }
760
761 #[test]
762 fn encode_counter_family_with_prefix_and_label() {
763 let mut registry = Registry::default();
764 let sub_registry = registry.sub_registry_with_prefix("my_prefix");
765 let sub_sub_registry = sub_registry
766 .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
767 let family = Family::<Vec<(String, String)>, Counter>::default();
768 sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
769
770 family
771 .get_or_create(&vec![
772 ("method".to_string(), "GET".to_string()),
773 ("status".to_string(), "200".to_string()),
774 ])
775 .inc();
776
777 let metric_families = encode(®istry).unwrap();
778 let family = metric_families.first().unwrap();
779 assert_eq!("my_prefix_my_counter_family_total", family.name.as_str());
780
781 let metric = family.metric.first().unwrap();
782 assert_eq!("my_key", metric.label[0].name);
783 assert_eq!("my_value", metric.label[0].value);
784 assert_eq!("method", metric.label[1].name);
785 assert_eq!("GET", metric.label[1].value);
786 assert_eq!("status", metric.label[2].name);
787 assert_eq!("200", metric.label[2].value);
788 }
789
790 #[test]
791 fn encode_histogram() {
792 let mut registry = Registry::default();
793 let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
794 registry.register("my_histogram", "My histogram", histogram.clone());
795 histogram.observe(1.0);
796
797 let metric_families = encode(®istry).unwrap();
798 let family = metric_families.first().unwrap();
799 assert_eq!("my_histogram", family.name.as_str());
800 assert_eq!(
801 prometheus_data_model::MetricType::Histogram as i32,
802 family.r#type
803 );
804
805 let histogram = family.metric[0].histogram.as_ref().unwrap();
806 assert_eq!(1, histogram.sample_count);
807 assert_eq!(1.0, histogram.sample_sum);
808 assert_eq!(11, histogram.bucket.len());
809 assert_eq!(1, histogram.bucket[0].cumulative_count);
810 assert_eq!(1.0, histogram.bucket[0].upper_bound);
811 assert_eq!(
812 f64::INFINITY,
813 histogram.bucket.last().unwrap().upper_bound,
814 "the implicit overflow bucket must be exposed as +Inf"
815 );
816 }
817
818 #[test]
819 fn encode_histogram_with_exemplars() {
820 let now = SystemTime::now();
821 let now_ts: Timestamp = now.into();
822
823 let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
824 let mut registry = Registry::default();
825 registry.register("my_histogram", "My histogram", histogram.clone());
826
827 histogram.observe(1.0, Some(vec![("user_id".to_string(), 42u64)]), None);
828
829 let metric_families = encode(®istry).unwrap();
830 let exemplar = metric_families[0].metric[0]
831 .histogram
832 .as_ref()
833 .unwrap()
834 .bucket[0]
835 .exemplar
836 .as_ref()
837 .unwrap();
838 assert_eq!(1.0, exemplar.value);
839 assert_eq!(None, exemplar.timestamp);
840 assert_eq!("42", exemplar.label[0].value);
841
842 histogram.observe(2.0, Some(vec![("user_id".to_string(), 99u64)]), Some(now));
843
844 let metric_families = encode(®istry).unwrap();
845 let exemplar = metric_families[0].metric[0]
846 .histogram
847 .as_ref()
848 .unwrap()
849 .bucket[1]
850 .exemplar
851 .as_ref()
852 .unwrap();
853 assert_eq!(2.0, exemplar.value);
854 assert_eq!(Some(now_ts), exemplar.timestamp.clone());
855 assert_eq!("99", exemplar.label[0].value);
856 }
857
858 #[test]
859 fn encode_native_histogram() {
860 let histogram = Histogram::new_native(NativeHistogramConfig::with_schema(0));
861 let mut registry = Registry::default();
862 registry.register("my_histogram", "My histogram", histogram.clone());
863
864 histogram.observe(1.0);
865 histogram.observe(4.0);
866 histogram.observe(-2.0);
867
868 let metric_families = encode(®istry).unwrap();
869 let histogram = metric_families[0].metric[0].histogram.as_ref().unwrap();
870
871 assert_eq!(3, histogram.sample_count);
872 assert_eq!(3.0, histogram.sample_sum);
873 assert_eq!(0, histogram.schema);
874 assert_eq!(1, histogram.negative_span.len());
875 assert_eq!(1, histogram.positive_span.len());
876 assert_eq!(vec![1], histogram.negative_delta);
877 assert_eq!(vec![1, -1, 1], histogram.positive_delta);
878 assert!(histogram.bucket.is_empty());
879 assert!(histogram.start_timestamp.is_some());
880 }
881
882 #[test]
883 fn encode_native_histogram_nan_only_has_no_op_span() {
884 let histogram = Histogram::new_native(
885 NativeHistogramConfig::with_schema(0)
886 .zero_threshold(NATIVE_HISTOGRAM_ZERO_THRESHOLD_ZERO),
887 );
888 let mut registry = Registry::default();
889 registry.register("my_histogram", "My histogram", histogram.clone());
890
891 histogram.observe(f64::NAN);
892
893 let metric_families = encode(®istry).unwrap();
894 let histogram = metric_families[0].metric[0].histogram.as_ref().unwrap();
895
896 assert_eq!(1, histogram.sample_count);
897 assert!(histogram.sample_sum.is_nan());
898 assert_eq!(0.0, histogram.zero_threshold);
899 assert_eq!(0, histogram.zero_count);
900 assert_eq!(1, histogram.positive_span.len());
901 assert_eq!(0, histogram.positive_span[0].offset);
902 assert_eq!(0, histogram.positive_span[0].length);
903 assert!(histogram.positive_delta.is_empty());
904 assert!(histogram.negative_span.is_empty());
905 }
906
907 #[test]
908 fn encode_classic_and_native_histogram() {
909 let histogram =
910 Histogram::new_classic_and_native([1.0, 2.0], NativeHistogramConfig::with_schema(0));
911 let mut registry = Registry::default();
912 registry.register("my_histogram", "My histogram", histogram.clone());
913
914 histogram.observe(1.0);
915 histogram.observe(4.0);
916
917 let metric_families = encode(®istry).unwrap();
918 let histogram = metric_families[0].metric[0].histogram.as_ref().unwrap();
919
920 assert_eq!(2, histogram.sample_count);
921 assert_eq!(5.0, histogram.sample_sum);
922 assert_eq!(3, histogram.bucket.len());
923 assert_eq!(1, histogram.bucket[0].cumulative_count);
924 assert_eq!(2, histogram.bucket[2].cumulative_count);
925 assert_eq!(0, histogram.schema);
926 assert!(!histogram.positive_span.is_empty());
927 assert!(!histogram.positive_delta.is_empty());
928 }
929
930 #[test]
931 fn encode_family_and_counter_and_histogram() {
932 let mut registry = Registry::default();
933
934 let counter_family = Family::<Vec<(String, String)>, Counter>::default();
935 let histogram_family =
936 Family::<Vec<(String, String)>, Histogram>::new_with_constructor(|| {
937 Histogram::new(exponential_buckets(1.0, 2.0, 10))
938 });
939
940 registry.register("my_family_counter", "My counter", counter_family.clone());
941 registry.register(
942 "my_family_histogram",
943 "My histogram",
944 histogram_family.clone(),
945 );
946
947 counter_family
948 .get_or_create(&vec![("path".to_string(), "/".to_string())])
949 .inc();
950
951 histogram_family
952 .get_or_create(&vec![("path".to_string(), "/".to_string())])
953 .observe(1.0);
954
955 let counter: Counter = Counter::default();
956 registry.register("my_counter", "My counter", counter.clone());
957 counter.inc();
958
959 let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
960 registry.register("my_histogram", "My histogram", histogram.clone());
961 histogram.observe(1.0);
962
963 let metric_families = encode(®istry).unwrap();
964 assert_eq!("my_family_counter_total", metric_families[0].name);
965 assert_eq!("my_family_histogram", metric_families[1].name);
966 assert_eq!("my_counter_total", metric_families[2].name);
967 assert_eq!("my_histogram", metric_families[3].name);
968 }
969
970 #[test]
971 fn encode_info() {
972 let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
973 let mut registry = Registry::default();
974 registry.register("my_info_metric", "My info metric", info);
975
976 let metric_families = encode(®istry).unwrap();
977 let family = metric_families.first().unwrap();
978 assert_eq!("my_info_metric_info", family.name.as_str());
979 assert_eq!(
980 prometheus_data_model::MetricType::Gauge as i32,
981 family.r#type
982 );
983
984 let metric = family.metric.first().unwrap();
985 assert_eq!(1.0, metric.gauge.as_ref().unwrap().value);
986 assert_eq!("os", metric.label[0].name);
987 assert_eq!("GNU/linux", metric.label[0].value);
988 }
989
990 #[test]
991 fn encode_to_vec_length_delimited() {
992 let counter: Counter = Counter::default();
993 let mut registry = Registry::default();
994 registry.register("my_counter", "My counter", counter.clone());
995 counter.inc();
996
997 let payload = encode_to_vec(®istry).unwrap();
998 let family =
999 prometheus_data_model::MetricFamily::decode_length_delimited(payload.as_slice())
1000 .unwrap();
1001
1002 assert_eq!("my_counter_total", family.name);
1003 assert_eq!(1.0, family.metric[0].counter.as_ref().unwrap().value);
1004 }
1005}