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