1use chrono::{DateTime, TimeZone, Utc};
2#[cfg(feature = "sources-prometheus-remote-write")]
3use vector_lib::prometheus::parser::proto;
4use vector_lib::prometheus::parser::{GroupKind, MetricGroup, ParserError};
5
6use crate::event::{
7 Event,
8 metric::{Bucket, Metric, MetricKind, MetricTags, MetricValue, Quantile},
9};
10
11fn utc_timestamp(timestamp: Option<i64>, default: DateTime<Utc>) -> DateTime<Utc> {
12 timestamp
13 .and_then(|timestamp| {
14 Utc.timestamp_opt(timestamp / 1000, (timestamp % 1000) as u32 * 1000000)
15 .latest()
16 })
17 .unwrap_or(default)
18}
19
20#[cfg(any(test, feature = "sources-prometheus-scrape"))]
21pub(super) fn parse_text(packet: &str) -> Result<Vec<Event>, ParserError> {
22 vector_lib::prometheus::parser::parse_text(packet)
23 .map(|group| reparse_groups(group, vec![], false, false))
24}
25
26#[cfg(any(test, feature = "sources-prometheus-pushgateway"))]
27pub(super) fn parse_text_with_overrides(
28 packet: &str,
29 tag_overrides: impl IntoIterator<Item = (String, String)> + Clone,
30 aggregate_metrics: bool,
31) -> Result<Vec<Event>, ParserError> {
32 vector_lib::prometheus::parser::parse_text(packet)
33 .map(|group| reparse_groups(group, tag_overrides, aggregate_metrics, false))
34}
35
36#[cfg(test)]
37fn parse_text_with_nan_filtering(packet: &str) -> Result<Vec<Event>, ParserError> {
38 vector_lib::prometheus::parser::parse_text(packet)
39 .map(|group| reparse_groups(group, vec![], false, true))
40}
41
42#[cfg(feature = "sources-prometheus-remote-write")]
43pub(super) fn parse_request(
44 request: proto::WriteRequest,
45 skip_nan_values: bool,
46) -> Result<Vec<Event>, ParserError> {
47 vector_lib::prometheus::parser::parse_request(request)
48 .map(|group| reparse_groups(group, vec![], false, skip_nan_values))
49}
50
51fn reparse_groups(
52 groups: Vec<MetricGroup>,
53 tag_overrides: impl IntoIterator<Item = (String, String)> + Clone,
54 aggregate_metrics: bool,
55 skip_nan_values: bool,
56) -> Vec<Event> {
57 let mut result = Vec::new();
58 let start = Utc::now();
59
60 let metric_kind = if aggregate_metrics {
61 MetricKind::Incremental
62 } else {
63 MetricKind::Absolute
64 };
65
66 for group in groups {
67 match group.metrics {
68 GroupKind::Counter(metrics) => {
69 for (key, metric) in metrics {
70 if skip_nan_values && metric.value.is_nan() {
71 continue;
72 }
73
74 let tags = combine_tags(key.labels, tag_overrides.clone());
75
76 let counter = Metric::new(
77 group.name.clone(),
78 metric_kind,
79 MetricValue::Counter {
80 value: metric.value,
81 },
82 )
83 .with_timestamp(Some(utc_timestamp(key.timestamp, start)))
84 .with_tags(tags.as_option());
85
86 result.push(counter.into());
87 }
88 }
89 GroupKind::Gauge(metrics) | GroupKind::Untyped(metrics) => {
90 for (key, metric) in metrics {
91 if skip_nan_values && metric.value.is_nan() {
92 continue;
93 }
94
95 let tags = combine_tags(key.labels, tag_overrides.clone());
96
97 let gauge = Metric::new(
98 group.name.clone(),
99 MetricKind::Absolute,
101 MetricValue::Gauge {
102 value: metric.value,
103 },
104 )
105 .with_timestamp(Some(utc_timestamp(key.timestamp, start)))
106 .with_tags(tags.as_option());
107
108 result.push(gauge.into());
109 }
110 }
111 GroupKind::Histogram(metrics) => {
112 for (key, metric) in metrics {
113 if skip_nan_values
114 && (metric.sum.is_nan() || metric.buckets.iter().any(|b| b.bucket.is_nan()))
115 {
116 continue;
117 }
118
119 let tags = combine_tags(key.labels, tag_overrides.clone());
120
121 let mut buckets = metric.buckets;
122 buckets.sort_unstable_by(|a, b| {
123 a.bucket.total_cmp(&b.bucket).then(a.count.cmp(&b.count))
124 });
125 for i in (1..buckets.len()).rev() {
126 buckets[i].count = buckets[i].count.saturating_sub(buckets[i - 1].count);
127 }
128 let drop_last = buckets
129 .last()
130 .is_some_and(|bucket| bucket.bucket == f64::INFINITY);
131 if drop_last {
132 buckets.pop();
133 }
134
135 result.push(
136 Metric::new(
137 group.name.clone(),
138 metric_kind,
139 MetricValue::AggregatedHistogram {
140 buckets: buckets
141 .into_iter()
142 .map(|b| Bucket {
143 upper_limit: b.bucket,
144 count: b.count,
145 })
146 .collect(),
147 count: metric.count,
148 sum: metric.sum,
149 },
150 )
151 .with_timestamp(Some(utc_timestamp(key.timestamp, start)))
152 .with_tags(tags.as_option())
153 .into(),
154 );
155 }
156 }
157 GroupKind::Summary(metrics) => {
158 for (key, metric) in metrics {
159 if skip_nan_values
160 && (metric.sum.is_nan()
161 || metric
162 .quantiles
163 .iter()
164 .any(|q| q.quantile.is_nan() || q.value.is_nan()))
165 {
166 continue;
167 }
168
169 let tags = combine_tags(key.labels, tag_overrides.clone());
170
171 result.push(
172 Metric::new(
173 group.name.clone(),
174 MetricKind::Absolute,
176 MetricValue::AggregatedSummary {
177 quantiles: metric
178 .quantiles
179 .into_iter()
180 .map(|q| Quantile {
181 quantile: q.quantile,
182 value: q.value,
183 })
184 .collect(),
185 count: metric.count,
186 sum: metric.sum,
187 },
188 )
189 .with_timestamp(Some(utc_timestamp(key.timestamp, start)))
190 .with_tags(tags.as_option())
191 .into(),
192 );
193 }
194 }
195 }
196 }
197
198 result
199}
200
201fn combine_tags(
202 base_tags: impl Into<MetricTags>,
203 tag_overrides: impl IntoIterator<Item = (String, String)>,
204) -> MetricTags {
205 let mut tags = base_tags.into();
206 for (k, v) in tag_overrides.into_iter() {
207 tags.replace(k, v);
208 }
209
210 tags
211}
212
213#[cfg(test)]
214mod test {
215 use core::f64;
216 use std::sync::LazyLock;
217
218 use chrono::{TimeZone, Timelike, Utc};
219 use itertools::Itertools;
220 use similar_asserts::assert_eq;
221 use vector_lib::{assert_event_data_eq, metric_tags};
222
223 use super::*;
224 use crate::event::metric::{Metric, MetricKind, MetricValue};
225
226 static TIMESTAMP: LazyLock<DateTime<Utc>> = LazyLock::new(|| {
227 Utc.with_ymd_and_hms(2021, 2, 4, 4, 5, 6)
228 .single()
229 .and_then(|t| t.with_nanosecond(789 * 1_000_000))
230 .expect("invalid timestamp")
231 });
232
233 fn events_to_metrics(
234 events: Result<Vec<Event>, ParserError>,
235 ) -> Result<Vec<Metric>, ParserError> {
236 events.map(|events| events.into_iter().map(Event::into_metric).collect())
237 }
238
239 #[test]
240 fn adds_timestamp_if_missing() {
241 let now = Utc::now();
242 let exp = r"
243 # HELP counter Some counter
244 # TYPE count counter
245 http_requests_total 1027
246 ";
247 let result = events_to_metrics(parse_text(exp)).unwrap();
248 assert_eq!(result.len(), 1);
249 assert!(result[0].timestamp().unwrap() >= now);
250 }
251
252 #[test]
253 fn test_counter() {
254 let exp = r"
255 # HELP uptime A counter
256 # TYPE uptime counter
257 uptime 123.0 1612411506789
258 ";
259
260 assert_event_data_eq!(
261 events_to_metrics(parse_text(exp)),
262 Ok(vec![
263 Metric::new(
264 "uptime",
265 MetricKind::Absolute,
266 MetricValue::Counter { value: 123.0 },
267 )
268 .with_timestamp(Some(*TIMESTAMP))
269 ]),
270 );
271 }
272
273 #[test]
274 fn test_counter_empty() {
275 let exp = r"
276 # HELP hidden A counter
277 # TYPE hidden counter
278 ";
279
280 assert_event_data_eq!(events_to_metrics(parse_text(exp)), Ok(vec![]));
281 }
282
283 #[test]
284 fn test_counter_nan() {
285 let exp = r#"
286 # TYPE name counter
287 name{labelname="val1",basename="basevalue"} NaN
288 "#;
289
290 match events_to_metrics(parse_text(exp)).unwrap()[0].value() {
291 MetricValue::Counter { value } => {
292 assert!(value.is_nan());
293 }
294 _ => unreachable!(),
295 }
296 }
297
298 #[test]
299 fn test_counter_weird() {
300 let exp = r#"
301 # A normal comment.
302 #
303 # TYPE name counter
304 name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1612411506789
305 # HELP name two-line\n doc str\\ing
306 # HELP name2 doc str"ing 2
307 # TYPE name2 counter
308 name2{labelname="val2" ,basename = "basevalue2" } +Inf 1612411506789
309 name2{ labelname = "val1" , }-Inf 1612411506789
310 "#;
311
312 assert_event_data_eq!(
313 events_to_metrics(parse_text(exp)),
314 Ok(vec![
315 Metric::new(
316 "name",
317 MetricKind::Absolute,
318 MetricValue::Counter { value: 0.23 },
319 )
320 .with_tags(Some(metric_tags!(
321 "labelname" => "val2",
322 "basename" => "base\"v\\al\nue",
323 )))
324 .with_timestamp(Some(*TIMESTAMP)),
325 Metric::new(
326 "name2",
327 MetricKind::Absolute,
328 MetricValue::Counter {
329 value: f64::INFINITY
330 },
331 )
332 .with_tags(Some(metric_tags!(
333 "labelname" => "val2",
334 "basename" => "basevalue2",
335 )))
336 .with_timestamp(Some(*TIMESTAMP)),
337 Metric::new(
338 "name2",
339 MetricKind::Absolute,
340 MetricValue::Counter {
341 value: f64::NEG_INFINITY
342 },
343 )
344 .with_tags(Some(metric_tags!("labelname" => "val1")))
345 .with_timestamp(Some(*TIMESTAMP)),
346 ]),
347 );
348 }
349
350 #[test]
351 fn test_counter_tags_and_timestamp() {
352 let exp = r#"
353 # HELP http_requests_total The total number of HTTP requests.
354 # TYPE http_requests_total counter
355 http_requests_total{method="post",code="200"} 1027 1395066363000
356 http_requests_total{method="post",code="400"} 3 1395066363000
357 "#;
358
359 assert_event_data_eq!(
360 events_to_metrics(parse_text(exp)),
361 Ok(vec![
362 Metric::new(
363 "http_requests_total",
364 MetricKind::Absolute,
365 MetricValue::Counter { value: 1027.0 },
366 )
367 .with_timestamp(Utc.timestamp_opt(1395066363, 0).latest())
368 .with_tags(Some(metric_tags!(
369 "method" => "post",
370 "code" => "200",
371 ))),
372 Metric::new(
373 "http_requests_total",
374 MetricKind::Absolute,
375 MetricValue::Counter { value: 3.0 },
376 )
377 .with_timestamp(Utc.timestamp_opt(1395066363, 0).latest())
378 .with_tags(Some(metric_tags!(
379 "method" => "post",
380 "code" => "400"
381 )))
382 ]),
383 );
384 }
385
386 #[test]
387 fn test_gauge() {
388 let exp = r"
389 # HELP latency A gauge
390 # TYPE latency gauge
391 latency 123.0 1612411506789
392 ";
393
394 assert_event_data_eq!(
395 events_to_metrics(parse_text(exp)),
396 Ok(vec![
397 Metric::new(
398 "latency",
399 MetricKind::Absolute,
400 MetricValue::Gauge { value: 123.0 },
401 )
402 .with_timestamp(Some(*TIMESTAMP))
403 ]),
404 );
405 }
406
407 #[test]
408 fn test_gauge_minimalistic() {
409 let exp = r"
410 metric_without_timestamp_and_labels 12.47 1612411506789
411 ";
412
413 assert_event_data_eq!(
414 events_to_metrics(parse_text(exp)),
415 Ok(vec![
416 Metric::new(
417 "metric_without_timestamp_and_labels",
418 MetricKind::Absolute,
419 MetricValue::Gauge { value: 12.47 },
420 )
421 .with_timestamp(Some(*TIMESTAMP))
422 ]),
423 );
424 }
425
426 #[test]
427 fn test_gauge_empty_labels() {
428 let exp = r"
429 no_labels{} 3 1612411506789
430 ";
431
432 assert_event_data_eq!(
433 events_to_metrics(parse_text(exp)),
434 Ok(vec![
435 Metric::new(
436 "no_labels",
437 MetricKind::Absolute,
438 MetricValue::Gauge { value: 3.0 },
439 )
440 .with_timestamp(Some(*TIMESTAMP))
441 ]),
442 );
443 }
444
445 #[test]
446 fn test_gauge_minimalistic_escaped() {
447 let exp = r#"
448 msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9 1612411506789
449 "#;
450
451 assert_event_data_eq!(
452 events_to_metrics(parse_text(exp)),
453 Ok(vec![
454 Metric::new(
455 "msdos_file_access_time_seconds",
456 MetricKind::Absolute,
457 MetricValue::Gauge {
458 value: 1458255915.0
459 },
460 )
461 .with_tags(Some(metric_tags!(
462 "path" => "C:\\DIR\\FILE.TXT",
463 "error" => "Cannot find file:\n\"FILE.TXT\"",
464 )))
465 .with_timestamp(Some(*TIMESTAMP))
466 ]),
467 );
468 }
469
470 #[test]
471 fn test_tag_value_contain_bracket() {
472 let exp = r#"
473 # HELP name counter
474 # TYPE name counter
475 name{tag="}"} 0 1612411506789
476 "#;
477 assert_event_data_eq!(
478 events_to_metrics(parse_text(exp)),
479 Ok(vec![
480 Metric::new(
481 "name",
482 MetricKind::Absolute,
483 MetricValue::Counter { value: 0.0 },
484 )
485 .with_tags(Some(metric_tags! { "tag" => "}" }))
486 .with_timestamp(Some(*TIMESTAMP))
487 ]),
488 );
489 }
490
491 #[test]
492 fn test_parse_tag_value_contain_comma() {
493 let exp = r#"
494 # HELP name counter
495 # TYPE name counter
496 name{tag="a,b"} 0 1612411506789
497 "#;
498 assert_event_data_eq!(
499 events_to_metrics(parse_text(exp)),
500 Ok(vec![
501 Metric::new(
502 "name",
503 MetricKind::Absolute,
504 MetricValue::Counter { value: 0.0 },
505 )
506 .with_tags(Some(metric_tags! { "tag" => "a,b" }))
507 .with_timestamp(Some(*TIMESTAMP))
508 ]),
509 );
510 }
511
512 #[test]
513 fn test_parse_tag_escaping() {
514 let exp = r#"
515 # HELP name counter
516 # TYPE name counter
517 name{tag="\\n"} 0 1612411506789
518 "#;
519 assert_event_data_eq!(
520 events_to_metrics(parse_text(exp)),
521 Ok(vec![
522 Metric::new(
523 "name",
524 MetricKind::Absolute,
525 MetricValue::Counter { value: 0.0 },
526 )
527 .with_tags(Some(metric_tags! { "tag" => "\\n" }))
528 .with_timestamp(Some(*TIMESTAMP))
529 ]),
530 );
531 }
532
533 #[test]
534 fn test_parse_tag_dont_trim_value() {
535 let exp = r#"
536 # HELP name counter
537 # TYPE name counter
538 name{tag=" * "} 0 1612411506789
539 "#;
540 assert_event_data_eq!(
541 events_to_metrics(parse_text(exp)),
542 Ok(vec![
543 Metric::new(
544 "name",
545 MetricKind::Absolute,
546 MetricValue::Counter { value: 0.0 },
547 )
548 .with_tags(Some(metric_tags! { "tag" => " * " }))
549 .with_timestamp(Some(*TIMESTAMP))
550 ]),
551 );
552 }
553
554 #[test]
555 fn test_parse_tag_value_containing_equals() {
556 let exp = r#"
557 telemetry_scrape_size_bytes_count{registry="default",content_type="text/plain; version=0.0.4"} 1890 1612411506789
558 "#;
559
560 assert_event_data_eq!(
561 events_to_metrics(parse_text(exp)),
562 Ok(vec![
563 Metric::new(
564 "telemetry_scrape_size_bytes_count",
565 MetricKind::Absolute,
566 MetricValue::Gauge { value: 1890.0 },
567 )
568 .with_tags(Some(metric_tags!( "registry" => "default",
569 "content_type" => "text/plain; version=0.0.4" )))
570 .with_timestamp(Some(*TIMESTAMP))
571 ]),
572 );
573 }
574
575 #[test]
576 fn test_parse_tag_error_no_value() {
577 let exp = r#"
578 telemetry_scrape_size_bytes_count{registry="default",content_type} 1890 1612411506789
579 "#;
580
581 assert!(events_to_metrics(parse_text(exp)).is_err());
582 }
583
584 #[test]
585 fn test_parse_tag_error_equals_empty_value() {
586 let exp = r#"
587 telemetry_scrape_size_bytes_count{registry="default",content_type=} 1890 1612411506789
588 "#;
589
590 assert!(events_to_metrics(parse_text(exp)).is_err());
591 }
592
593 #[test]
594 fn test_gauge_weird_timestamp() {
595 let exp = r#"
596 something_weird{problem="division by zero"} +Inf -3982045000
597 "#;
598
599 assert_event_data_eq!(
600 events_to_metrics(parse_text(exp)),
601 Ok(vec![
602 Metric::new(
603 "something_weird",
604 MetricKind::Absolute,
605 MetricValue::Gauge {
606 value: f64::INFINITY
607 },
608 )
609 .with_timestamp(Utc.timestamp_opt(-3982045, 0).latest())
610 .with_tags(Some(metric_tags!("problem" => "division by zero")))
611 ]),
612 );
613 }
614
615 #[test]
616 fn test_gauge_tabs() {
617 let exp = r#"
618 # TYPE latency gauge
619 latency{env="production"} 1.0 1395066363000
620 latency{env="testing"} 2.0 1395066363000
621 "#;
622
623 assert_event_data_eq!(
624 events_to_metrics(parse_text(exp)),
625 Ok(vec![
626 Metric::new(
627 "latency",
628 MetricKind::Absolute,
629 MetricValue::Gauge { value: 1.0 },
630 )
631 .with_timestamp(Utc.timestamp_opt(1395066363, 0).latest())
632 .with_tags(Some(metric_tags!("env" => "production"))),
633 Metric::new(
634 "latency",
635 MetricKind::Absolute,
636 MetricValue::Gauge { value: 2.0 },
637 )
638 .with_timestamp(Utc.timestamp_opt(1395066363, 0).latest())
639 .with_tags(Some(metric_tags!("env" => "testing")))
640 ]),
641 );
642 }
643
644 #[test]
645 fn test_mixed() {
646 let exp = r"
647 # TYPE uptime counter
648 uptime 123.0 1612411506789
649 # TYPE temperature gauge
650 temperature -1.5 1612411506789
651 # TYPE launch_count counter
652 launch_count 10.0 1612411506789
653 ";
654
655 assert_event_data_eq!(
656 events_to_metrics(parse_text(exp)),
657 Ok(vec![
658 Metric::new(
659 "uptime",
660 MetricKind::Absolute,
661 MetricValue::Counter { value: 123.0 },
662 )
663 .with_timestamp(Some(*TIMESTAMP)),
664 Metric::new(
665 "temperature",
666 MetricKind::Absolute,
667 MetricValue::Gauge { value: -1.5 },
668 )
669 .with_timestamp(Some(*TIMESTAMP)),
670 Metric::new(
671 "launch_count",
672 MetricKind::Absolute,
673 MetricValue::Counter { value: 10.0 },
674 )
675 .with_timestamp(Some(*TIMESTAMP))
676 ]),
677 );
678 }
679
680 #[test]
681 fn test_no_value() {
682 let exp = r#"
683 # TYPE latency counter
684 latency{env="production"}
685 "#;
686
687 assert!(events_to_metrics(parse_text(exp)).is_err());
688 }
689
690 #[test]
691 fn test_no_name() {
692 let exp = r"
693 # TYPE uptime counter
694 123.0 1612411506789
695 ";
696
697 assert!(events_to_metrics(parse_text(exp)).is_err());
698 }
699
700 #[test]
701 fn test_mixed_and_loosely_typed() {
702 let exp = r"
703 # TYPE uptime counter
704 uptime 123.0 1612411506789
705 last_downtime 4.0 1612411506789
706 # TYPE temperature gauge
707 temperature -1.5 1612411506789
708 temperature_7_days_average 0.1 1612411506789
709 ";
710
711 assert_event_data_eq!(
712 events_to_metrics(parse_text(exp)),
713 Ok(vec![
714 Metric::new(
715 "uptime",
716 MetricKind::Absolute,
717 MetricValue::Counter { value: 123.0 },
718 )
719 .with_timestamp(Some(*TIMESTAMP)),
720 Metric::new(
721 "last_downtime",
722 MetricKind::Absolute,
723 MetricValue::Gauge { value: 4.0 },
724 )
725 .with_timestamp(Some(*TIMESTAMP)),
726 Metric::new(
727 "temperature",
728 MetricKind::Absolute,
729 MetricValue::Gauge { value: -1.5 },
730 )
731 .with_timestamp(Some(*TIMESTAMP)),
732 Metric::new(
733 "temperature_7_days_average",
734 MetricKind::Absolute,
735 MetricValue::Gauge { value: 0.1 },
736 )
737 .with_timestamp(Some(*TIMESTAMP))
738 ]),
739 );
740 }
741
742 #[test]
743 fn test_histogram() {
744 let exp = r#"
745 # HELP http_request_duration_seconds A histogram of the request duration.
746 # TYPE http_request_duration_seconds histogram
747 http_request_duration_seconds_bucket{le="0.05"} 24054 1612411506789
748 http_request_duration_seconds_bucket{le="0.1"} 33444 1612411506789
749 http_request_duration_seconds_bucket{le="0.2"} 100392 1612411506789
750 http_request_duration_seconds_bucket{le="0.5"} 129389 1612411506789
751 http_request_duration_seconds_bucket{le="1"} 133988 1612411506789
752 http_request_duration_seconds_bucket{le="+Inf"} 144320 1612411506789
753 http_request_duration_seconds_sum 53423 1612411506789
754 http_request_duration_seconds_count 144320 1612411506789
755 "#;
756
757 assert_event_data_eq!(
758 events_to_metrics(parse_text(exp)),
759 Ok(vec![
760 Metric::new(
761 "http_request_duration_seconds",
762 MetricKind::Absolute,
763 MetricValue::AggregatedHistogram {
764 buckets: vector_lib::buckets![
765 0.05 => 24054, 0.1 => 9390, 0.2 => 66948, 0.5 => 28997, 1.0 => 4599
766 ],
767 count: 144320,
768 sum: 53423.0,
769 },
770 )
771 .with_timestamp(Some(*TIMESTAMP))
772 ]),
773 );
774 }
775
776 #[test]
777 fn test_histogram_doesnt_panic() {
778 let mut exp = r#"
779 # HELP http_request_duration_seconds A histogram of the request duration.
780 # TYPE http_request_duration_seconds histogram
781 "#
782 .to_string();
783
784 let to_float = |v: i32| -> f64 { v as f64 };
785 exp += &(0..=15)
786 .map(to_float)
787 .chain(std::iter::once(f64::NAN))
788 .chain((16..=20).map(to_float))
789 .rev()
790 .map(|f| format!("http_request_duration_seconds_bucket{{le=\"{f}\"}} 0 1612411506789"))
791 .join("\n");
792
793 assert_event_data_eq!(
794 events_to_metrics(parse_text(&exp)),
795 Ok(vec![
796 Metric::new(
797 "http_request_duration_seconds",
798 MetricKind::Absolute,
799 MetricValue::AggregatedHistogram {
800 buckets: (0..=20)
803 .map(to_float)
804 .chain(std::iter::once(f64::NAN))
805 .map(|upper_limit| Bucket {
806 upper_limit,
807 count: 0
808 })
809 .collect(),
810 count: 0,
811 sum: 0.0,
812 },
813 )
814 .with_timestamp(Some(*TIMESTAMP))
815 ]),
816 );
817 }
818
819 #[test]
820 fn test_histogram_out_of_order() {
821 let exp = r#"
822 # HELP duration A histogram of the request duration.
823 # TYPE duration histogram
824 duration_bucket{le="+Inf"} 144320 1612411506789
825 duration_bucket{le="1"} 133988 1612411506789
826 duration_sum 53423 1612411506789
827 duration_count 144320 1612411506789
828 "#;
829
830 assert_event_data_eq!(
831 events_to_metrics(parse_text(exp)),
832 Ok(vec![
833 Metric::new(
834 "duration",
835 MetricKind::Absolute,
836 MetricValue::AggregatedHistogram {
837 buckets: vector_lib::buckets![1.0 => 133988],
838 count: 144320,
839 sum: 53423.0,
840 },
841 )
842 .with_timestamp(Some(*TIMESTAMP))
843 ]),
844 );
845 }
846
847 #[test]
848 fn test_histogram_backward_values() {
849 let exp = r#"
850 # HELP duration A histogram of the request duration.
851 # TYPE duration histogram
852 duration_bucket{le="1"} 2000 1612411506789
853 duration_bucket{le="10"} 1000 1612411506789
854 duration_bucket{le="+Inf"} 2000 1612411506789
855 duration_sum 2000 1612411506789
856 duration_count 2000 1612411506789
857 "#;
858
859 assert_event_data_eq!(
860 events_to_metrics(parse_text(exp)),
861 Ok(vec![
862 Metric::new(
863 "duration",
864 MetricKind::Absolute,
865 MetricValue::AggregatedHistogram {
866 buckets: vector_lib::buckets![1.0 => 2000, 10.0 => 0],
867 count: 2000,
868 sum: 2000.0,
869 },
870 )
871 .with_timestamp(Some(*TIMESTAMP))
872 ]),
873 );
874 }
875
876 #[test]
877 fn test_histogram_with_labels() {
878 let exp = r#"
879 # HELP gitlab_runner_job_duration_seconds Histogram of job durations
880 # TYPE gitlab_runner_job_duration_seconds histogram
881 gitlab_runner_job_duration_seconds_bucket{runner="z",le="30"} 327 1612411506789
882 gitlab_runner_job_duration_seconds_bucket{runner="z",le="60"} 474 1612411506789
883 gitlab_runner_job_duration_seconds_bucket{runner="z",le="300"} 535 1612411506789
884 gitlab_runner_job_duration_seconds_bucket{runner="z",le="600"} 536 1612411506789
885 gitlab_runner_job_duration_seconds_bucket{runner="z",le="1800"} 536 1612411506789
886 gitlab_runner_job_duration_seconds_bucket{runner="z",le="3600"} 536 1612411506789
887 gitlab_runner_job_duration_seconds_bucket{runner="z",le="7200"} 536 1612411506789
888 gitlab_runner_job_duration_seconds_bucket{runner="z",le="10800"} 536 1612411506789
889 gitlab_runner_job_duration_seconds_bucket{runner="z",le="18000"} 536 1612411506789
890 gitlab_runner_job_duration_seconds_bucket{runner="z",le="36000"} 536 1612411506789
891 gitlab_runner_job_duration_seconds_bucket{runner="z",le="+Inf"} 536 1612411506789
892 gitlab_runner_job_duration_seconds_sum{runner="z"} 19690.129384881966 1612411506789
893 gitlab_runner_job_duration_seconds_count{runner="z"} 536 1612411506789
894 gitlab_runner_job_duration_seconds_bucket{runner="x",le="30"} 1 1612411506789
895 gitlab_runner_job_duration_seconds_bucket{runner="x",le="60"} 1 1612411506789
896 gitlab_runner_job_duration_seconds_bucket{runner="x",le="300"} 1 1612411506789
897 gitlab_runner_job_duration_seconds_bucket{runner="x",le="600"} 1 1612411506789
898 gitlab_runner_job_duration_seconds_bucket{runner="x",le="1800"} 1 1612411506789
899 gitlab_runner_job_duration_seconds_bucket{runner="x",le="3600"} 1 1612411506789
900 gitlab_runner_job_duration_seconds_bucket{runner="x",le="7200"} 1 1612411506789
901 gitlab_runner_job_duration_seconds_bucket{runner="x",le="10800"} 1 1612411506789
902 gitlab_runner_job_duration_seconds_bucket{runner="x",le="18000"} 1 1612411506789
903 gitlab_runner_job_duration_seconds_bucket{runner="x",le="36000"} 1 1612411506789
904 gitlab_runner_job_duration_seconds_bucket{runner="x",le="+Inf"} 1 1612411506789
905 gitlab_runner_job_duration_seconds_sum{runner="x"} 28.975436316 1612411506789
906 gitlab_runner_job_duration_seconds_count{runner="x"} 1 1612411506789
907 gitlab_runner_job_duration_seconds_bucket{runner="y",le="30"} 285 1612411506789
908 gitlab_runner_job_duration_seconds_bucket{runner="y",le="60"} 1165 1612411506789
909 gitlab_runner_job_duration_seconds_bucket{runner="y",le="300"} 3071 1612411506789
910 gitlab_runner_job_duration_seconds_bucket{runner="y",le="600"} 3151 1612411506789
911 gitlab_runner_job_duration_seconds_bucket{runner="y",le="1800"} 3252 1612411506789
912 gitlab_runner_job_duration_seconds_bucket{runner="y",le="3600"} 3255 1612411506789
913 gitlab_runner_job_duration_seconds_bucket{runner="y",le="7200"} 3255 1612411506789
914 gitlab_runner_job_duration_seconds_bucket{runner="y",le="10800"} 3255 1612411506789
915 gitlab_runner_job_duration_seconds_bucket{runner="y",le="18000"} 3255 1612411506789
916 gitlab_runner_job_duration_seconds_bucket{runner="y",le="36000"} 3255 1612411506789
917 gitlab_runner_job_duration_seconds_bucket{runner="y",le="+Inf"} 3255 1612411506789
918 gitlab_runner_job_duration_seconds_sum{runner="y"} 381111.7498891335 1612411506789
919 gitlab_runner_job_duration_seconds_count{runner="y"} 3255 1612411506789
920 "#;
921
922 assert_event_data_eq!(
923 events_to_metrics(parse_text(exp)),
924 Ok(vec![
925 Metric::new(
926 "gitlab_runner_job_duration_seconds", MetricKind::Absolute, MetricValue::AggregatedHistogram {
927 buckets: vector_lib::buckets![
928 30.0 => 327,
929 60.0 => 147,
930 300.0 => 61,
931 600.0 => 1,
932 1800.0 => 0,
933 3600.0 => 0,
934 7200.0 => 0,
935 10800.0 => 0,
936 18000.0 => 0,
937 36000.0 => 0
938 ],
939 count: 536,
940 sum: 19690.129384881966,
941 },
942 )
943 .with_tags(Some(metric_tags!("runner" => "z")))
944 .with_timestamp(Some(*TIMESTAMP)),
945 Metric::new(
946 "gitlab_runner_job_duration_seconds", MetricKind::Absolute, MetricValue::AggregatedHistogram {
947 buckets: vector_lib::buckets![
948 30.0 => 1,
949 60.0 => 0,
950 300.0 => 0,
951 600.0 => 0,
952 1800.0 => 0,
953 3600.0 => 0,
954 7200.0 => 0,
955 10800.0 => 0,
956 18000.0 => 0,
957 36000.0 => 0
958 ],
959 count: 1,
960 sum: 28.975436316,
961 },
962 )
963 .with_tags(Some(metric_tags!("runner" => "x")))
964 .with_timestamp(Some(*TIMESTAMP)),
965 Metric::new(
966 "gitlab_runner_job_duration_seconds", MetricKind::Absolute, MetricValue::AggregatedHistogram {
967 buckets: vector_lib::buckets![
968 30.0 => 285, 60.0 => 880, 300.0 => 1906, 600.0 => 80, 1800.0 => 101, 3600.0 => 3,
969 7200.0 => 0, 10800.0 => 0, 18000.0 => 0, 36000.0 => 0
970 ],
971 count: 3255,
972 sum: 381111.7498891335,
973 },
974 )
975 .with_tags(Some(metric_tags!("runner" => "y")))
976 .with_timestamp(Some(*TIMESTAMP))
977 ]),
978 );
979 }
980
981 #[test]
982 fn test_summary() {
983 let exp = r#"
984 # HELP rpc_duration_seconds A summary of the RPC duration in seconds.
985 # TYPE rpc_duration_seconds summary
986 rpc_duration_seconds{service="a",quantile="0.01"} 3102 1612411506789
987 rpc_duration_seconds{service="a",quantile="0.05"} 3272 1612411506789
988 rpc_duration_seconds{service="a",quantile="0.5"} 4773 1612411506789
989 rpc_duration_seconds{service="a",quantile="0.9"} 9001 1612411506789
990 rpc_duration_seconds{service="a",quantile="0.99"} 76656 1612411506789
991 rpc_duration_seconds_sum{service="a"} 1.7560473e+07 1612411506789
992 rpc_duration_seconds_count{service="a"} 2693 1612411506789
993 # HELP go_gc_duration_seconds A summary of the GC invocation durations.
994 # TYPE go_gc_duration_seconds summary
995 go_gc_duration_seconds{quantile="0"} 0.009460965 1612411506789
996 go_gc_duration_seconds{quantile="0.25"} 0.009793382 1612411506789
997 go_gc_duration_seconds{quantile="0.5"} 0.009870205 1612411506789
998 go_gc_duration_seconds{quantile="0.75"} 0.01001838 1612411506789
999 go_gc_duration_seconds{quantile="1"} 0.018827136 1612411506789
1000 go_gc_duration_seconds_sum 4668.551713715 1612411506789
1001 go_gc_duration_seconds_count 602767 1612411506789
1002 "#;
1003
1004 assert_event_data_eq!(
1005 events_to_metrics(parse_text(exp)),
1006 Ok(vec![
1007 Metric::new(
1008 "rpc_duration_seconds",
1009 MetricKind::Absolute,
1010 MetricValue::AggregatedSummary {
1011 quantiles: vector_lib::quantiles![
1012 0.01 => 3102.0,
1013 0.05 => 3272.0,
1014 0.5 => 4773.0,
1015 0.9 => 9001.0,
1016 0.99 => 76656.0
1017 ],
1018 count: 2693,
1019 sum: 1.7560473e+07,
1020 },
1021 )
1022 .with_tags(Some(metric_tags!("service" => "a")))
1023 .with_timestamp(Some(*TIMESTAMP)),
1024 Metric::new(
1025 "go_gc_duration_seconds",
1026 MetricKind::Absolute,
1027 MetricValue::AggregatedSummary {
1028 quantiles: vector_lib::quantiles![
1029 0.0 => 0.009460965,
1030 0.25 => 0.009793382,
1031 0.5 => 0.009870205,
1032 0.75 => 0.01001838,
1033 1.0 => 0.018827136
1034 ],
1035 count: 602767,
1036 sum: 4668.551713715,
1037 },
1038 )
1039 .with_timestamp(Some(*TIMESTAMP)),
1040 ]),
1041 );
1042 }
1043
1044 #[test]
1046 fn test_nginx() {
1047 let exp = r#"
1048 # HELP nginx_server_bytes request/response bytes
1049 # TYPE nginx_server_bytes counter
1050 nginx_server_bytes{direction="in",host="*"} 263719
1051 nginx_server_bytes{direction="in",host="_"} 255061
1052 nginx_server_bytes{direction="in",host="nginx-vts-status"} 8658
1053 nginx_server_bytes{direction="out",host="*"} 944199
1054 nginx_server_bytes{direction="out",host="_"} 360775
1055 nginx_server_bytes{direction="out",host="nginx-vts-status"} 583424
1056 # HELP nginx_server_cache cache counter
1057 # TYPE nginx_server_cache counter
1058 nginx_server_cache{host="*",status="bypass"} 0
1059 nginx_server_cache{host="*",status="expired"} 0
1060 nginx_server_cache{host="*",status="hit"} 0
1061 nginx_server_cache{host="*",status="miss"} 0
1062 nginx_server_cache{host="*",status="revalidated"} 0
1063 nginx_server_cache{host="*",status="scarce"} 0
1064 "#;
1065
1066 let now = Utc::now();
1067 let result = events_to_metrics(parse_text(exp)).expect("Parsing failed");
1068 let result: Vec<_> = result
1070 .into_iter()
1071 .map(|metric| {
1072 assert!(metric.timestamp().expect("Missing timestamp") >= now);
1073 metric.with_timestamp(Some(*TIMESTAMP))
1074 })
1075 .collect();
1076
1077 assert_event_data_eq!(
1078 result,
1079 vec![
1080 Metric::new(
1081 "nginx_server_bytes",
1082 MetricKind::Absolute,
1083 MetricValue::Counter { value: 263719.0 },
1084 )
1085 .with_tags(Some(metric_tags! { "direction" => "in", "host" => "*" }))
1086 .with_timestamp(Some(*TIMESTAMP)),
1087 Metric::new(
1088 "nginx_server_bytes",
1089 MetricKind::Absolute,
1090 MetricValue::Counter { value: 255061.0 },
1091 )
1092 .with_tags(Some(metric_tags! { "direction" => "in", "host" => "_" }))
1093 .with_timestamp(Some(*TIMESTAMP)),
1094 Metric::new(
1095 "nginx_server_bytes",
1096 MetricKind::Absolute,
1097 MetricValue::Counter { value: 8658.0 },
1098 )
1099 .with_tags(Some(
1100 metric_tags! { "direction" => "in", "host" => "nginx-vts-status" }
1101 ))
1102 .with_timestamp(Some(*TIMESTAMP)),
1103 Metric::new(
1104 "nginx_server_bytes",
1105 MetricKind::Absolute,
1106 MetricValue::Counter { value: 944199.0 },
1107 )
1108 .with_tags(Some(metric_tags! { "direction" => "out", "host" => "*" }))
1109 .with_timestamp(Some(*TIMESTAMP)),
1110 Metric::new(
1111 "nginx_server_bytes",
1112 MetricKind::Absolute,
1113 MetricValue::Counter { value: 360775.0 },
1114 )
1115 .with_tags(Some(metric_tags! { "direction" => "out", "host" => "_" }))
1116 .with_timestamp(Some(*TIMESTAMP)),
1117 Metric::new(
1118 "nginx_server_bytes",
1119 MetricKind::Absolute,
1120 MetricValue::Counter { value: 583424.0 },
1121 )
1122 .with_tags(Some(
1123 metric_tags! { "direction" => "out", "host" => "nginx-vts-status" }
1124 ))
1125 .with_timestamp(Some(*TIMESTAMP)),
1126 Metric::new(
1127 "nginx_server_cache",
1128 MetricKind::Absolute,
1129 MetricValue::Counter { value: 0.0 },
1130 )
1131 .with_tags(Some(metric_tags! { "host" => "*", "status" => "bypass" }))
1132 .with_timestamp(Some(*TIMESTAMP)),
1133 Metric::new(
1134 "nginx_server_cache",
1135 MetricKind::Absolute,
1136 MetricValue::Counter { value: 0.0 },
1137 )
1138 .with_tags(Some(metric_tags! { "host" => "*", "status" => "expired" }))
1139 .with_timestamp(Some(*TIMESTAMP)),
1140 Metric::new(
1141 "nginx_server_cache",
1142 MetricKind::Absolute,
1143 MetricValue::Counter { value: 0.0 },
1144 )
1145 .with_tags(Some(metric_tags! { "host" => "*", "status" => "hit" }))
1146 .with_timestamp(Some(*TIMESTAMP)),
1147 Metric::new(
1148 "nginx_server_cache",
1149 MetricKind::Absolute,
1150 MetricValue::Counter { value: 0.0 },
1151 )
1152 .with_tags(Some(metric_tags! { "host" => "*", "status" => "miss" }))
1153 .with_timestamp(Some(*TIMESTAMP)),
1154 Metric::new(
1155 "nginx_server_cache",
1156 MetricKind::Absolute,
1157 MetricValue::Counter { value: 0.0 },
1158 )
1159 .with_tags(Some(
1160 metric_tags! { "host" => "*", "status" => "revalidated" }
1161 ))
1162 .with_timestamp(Some(*TIMESTAMP)),
1163 Metric::new(
1164 "nginx_server_cache",
1165 MetricKind::Absolute,
1166 MetricValue::Counter { value: 0.0 },
1167 )
1168 .with_tags(Some(metric_tags! { "host" => "*", "status" => "scarce" }))
1169 .with_timestamp(Some(*TIMESTAMP))
1170 ]
1171 );
1172 }
1173
1174 #[test]
1175 fn test_overrides_nothing_overwritten() {
1176 let exp = r#"
1177 # TYPE jobs_total counter
1178 # HELP jobs_total Total number of jobs
1179 jobs_total{type="a"} 1.0 1612411506789
1180 "#;
1181
1182 assert_event_data_eq!(
1183 events_to_metrics(parse_text_with_overrides(exp, vec![], false)),
1184 Ok(vec![
1185 Metric::new(
1186 "jobs_total",
1187 MetricKind::Absolute,
1188 MetricValue::Counter { value: 1.0 },
1189 )
1190 .with_tags(Some(metric_tags! { "type" => "a" }))
1191 .with_timestamp(Some(*TIMESTAMP))
1192 ]),
1193 );
1194 }
1195
1196 #[test]
1197 fn test_overrides_label_overwritten() {
1198 let exp = r#"
1199 # TYPE jobs_total counter
1200 # HELP jobs_total Total number of jobs
1201 jobs_total{type="a"} 1.0 1612411506789
1202 "#;
1203
1204 assert_event_data_eq!(
1205 events_to_metrics(parse_text_with_overrides(
1206 exp,
1207 vec![("type".to_owned(), "b".to_owned())],
1208 false
1209 )),
1210 Ok(vec![
1211 Metric::new(
1212 "jobs_total",
1213 MetricKind::Absolute,
1214 MetricValue::Counter { value: 1.0 },
1215 )
1216 .with_tags(Some(metric_tags! { "type" => "b" }))
1217 .with_timestamp(Some(*TIMESTAMP))
1218 ]),
1219 );
1220 }
1221
1222 #[test]
1225 fn test_overrides_last_value_preferred() {
1226 let exp = r#"
1227 # TYPE jobs_total counter
1228 # HELP jobs_total Total number of jobs
1229 jobs_total{type="a"} 1.0 1612411506789
1230 "#;
1231
1232 assert_event_data_eq!(
1233 events_to_metrics(parse_text_with_overrides(
1234 exp,
1235 vec![
1236 ("type".to_owned(), "b".to_owned()),
1237 ("type".to_owned(), "c".to_owned())
1238 ],
1239 false
1240 )),
1241 Ok(vec![
1242 Metric::new(
1243 "jobs_total",
1244 MetricKind::Absolute,
1245 MetricValue::Counter { value: 1.0 },
1246 )
1247 .with_tags(Some(metric_tags! { "type" => "c" }))
1248 .with_timestamp(Some(*TIMESTAMP))
1249 ]),
1250 );
1251 }
1252
1253 #[test]
1254 fn test_aggregation_enabled_only_aggregates_counter_and_histogram() {
1255 let exp = r#"
1256 # TYPE jobs_total counter
1257 # HELP jobs_total Total number of jobs
1258 jobs_total{type="a"} 1.0 1612411506789
1259 # TYPE jobs_current gauge
1260 # HELP jobs_current Current number of jobs
1261 jobs_current{type="a"} 5.0 1612411506789
1262 # TYPE jobs_distribution histogram
1263 # HELP jobs_distribution Distribution of jobs
1264 jobs_distribution_bucket{type="a",le="1"} 0.0 1612411506789
1265 jobs_distribution_bucket{type="a",le="2.5"} 0.0 1612411506789
1266 jobs_distribution_bucket{type="a",le="5"} 0.0 1612411506789
1267 jobs_distribution_bucket{type="a",le="10"} 1.0 1612411506789
1268 jobs_distribution_bucket{type="a",le="+Inf"} 1.0 1612411506789
1269 jobs_distribution_sum{type="a"} 8.0 1612411506789
1270 jobs_distribution_count{type="a"} 1.0 1612411506789
1271 # TYPE jobs_summary summary
1272 # HELP jobs_summary Summary of jobs
1273 jobs_summary_sum{type="a"} 8.0 1612411506789
1274 jobs_summary_count{type="a"} 1.0 1612411506789
1275 "#;
1276
1277 assert_event_data_eq!(
1278 events_to_metrics(parse_text_with_overrides(exp, vec![], true)),
1279 Ok(vec![
1280 Metric::new(
1281 "jobs_total",
1282 MetricKind::Incremental,
1283 MetricValue::Counter { value: 1.0 },
1284 )
1285 .with_tags(Some(metric_tags! { "type" => "a" }))
1286 .with_timestamp(Some(*TIMESTAMP)),
1287 Metric::new(
1288 "jobs_current",
1289 MetricKind::Absolute,
1290 MetricValue::Gauge { value: 5.0 },
1291 )
1292 .with_tags(Some(metric_tags! { "type" => "a" }))
1293 .with_timestamp(Some(*TIMESTAMP)),
1294 Metric::new(
1295 "jobs_distribution",
1296 MetricKind::Incremental,
1297 MetricValue::AggregatedHistogram {
1298 buckets: vector_lib::buckets![
1299 1.0 => 0, 2.5 => 0, 5.0 => 0, 10.0 => 1
1300 ],
1301 count: 1,
1302 sum: 8.0,
1303 },
1304 )
1305 .with_tags(Some(metric_tags! { "type" => "a" }))
1306 .with_timestamp(Some(*TIMESTAMP)),
1307 Metric::new(
1308 "jobs_summary",
1309 MetricKind::Absolute,
1310 MetricValue::AggregatedSummary {
1311 quantiles: vector_lib::quantiles![],
1312 count: 1,
1313 sum: 8.0,
1314 },
1315 )
1316 .with_tags(Some(metric_tags! { "type" => "a" }))
1317 .with_timestamp(Some(*TIMESTAMP)),
1318 ]),
1319 );
1320 }
1321
1322 #[test]
1323 fn test_aggregation_disabled_all_absolute() {
1324 let exp = r#"
1325 # TYPE jobs_total counter
1326 # HELP jobs_total Total number of jobs
1327 jobs_total{type="a"} 1.0 1612411506789
1328 # TYPE jobs_current gauge
1329 # HELP jobs_current Current number of jobs
1330 jobs_current{type="a"} 5.0 1612411506789
1331 # TYPE jobs_distribution histogram
1332 # HELP jobs_distribution Distribution of jobs
1333 jobs_distribution_bucket{type="a",le="1"} 0.0 1612411506789
1334 jobs_distribution_bucket{type="a",le="2.5"} 0.0 1612411506789
1335 jobs_distribution_bucket{type="a",le="5"} 0.0 1612411506789
1336 jobs_distribution_bucket{type="a",le="10"} 1.0 1612411506789
1337 jobs_distribution_bucket{type="a",le="+Inf"} 1.0 1612411506789
1338 jobs_distribution_sum{type="a"} 8.0 1612411506789
1339 jobs_distribution_count{type="a"} 1.0 1612411506789
1340 # TYPE jobs_summary summary
1341 # HELP jobs_summary Summary of jobs
1342 jobs_summary_sum{type="a"} 8.0 1612411506789
1343 jobs_summary_count{type="a"} 1.0 1612411506789
1344 "#;
1345
1346 assert_event_data_eq!(
1347 events_to_metrics(parse_text_with_overrides(exp, vec![], false)),
1348 Ok(vec![
1349 Metric::new(
1350 "jobs_total",
1351 MetricKind::Absolute,
1352 MetricValue::Counter { value: 1.0 },
1353 )
1354 .with_tags(Some(metric_tags! { "type" => "a" }))
1355 .with_timestamp(Some(*TIMESTAMP)),
1356 Metric::new(
1357 "jobs_current",
1358 MetricKind::Absolute,
1359 MetricValue::Gauge { value: 5.0 },
1360 )
1361 .with_tags(Some(metric_tags! { "type" => "a" }))
1362 .with_timestamp(Some(*TIMESTAMP)),
1363 Metric::new(
1364 "jobs_distribution",
1365 MetricKind::Absolute,
1366 MetricValue::AggregatedHistogram {
1367 buckets: vector_lib::buckets![
1368 1.0 => 0, 2.5 => 0, 5.0 => 0, 10.0 => 1
1369 ],
1370 count: 1,
1371 sum: 8.0,
1372 },
1373 )
1374 .with_tags(Some(metric_tags! { "type" => "a" }))
1375 .with_timestamp(Some(*TIMESTAMP)),
1376 Metric::new(
1377 "jobs_summary",
1378 MetricKind::Absolute,
1379 MetricValue::AggregatedSummary {
1380 quantiles: vector_lib::quantiles![],
1381 count: 1,
1382 sum: 8.0,
1383 },
1384 )
1385 .with_tags(Some(metric_tags! { "type" => "a" }))
1386 .with_timestamp(Some(*TIMESTAMP)),
1387 ]),
1388 );
1389 }
1390
1391 #[test]
1392 fn test_skip_nan_counter_enabled() {
1393 let exp = r#"
1394 # TYPE name counter
1395 name{labelname="val1"} NaN 1612411506789
1396 name{labelname="val2"} 123.0 1612411506789
1397 "#;
1398
1399 let result = events_to_metrics(parse_text_with_nan_filtering(exp)).unwrap();
1400 assert_eq!(result.len(), 1);
1401 assert_eq!(result[0].name(), "name");
1402 match result[0].value() {
1403 MetricValue::Counter { value } => {
1404 assert_eq!(*value, 123.0);
1405 }
1406 _ => unreachable!(),
1407 }
1408 assert_eq!(result[0].tags().unwrap().get("labelname").unwrap(), "val2");
1409 }
1410
1411 #[test]
1412 fn test_skip_nan_counter_disabled() {
1413 let exp = r#"
1414 # TYPE name counter
1415 name{labelname="val1"} NaN 1612411506789
1416 name{labelname="val2"} 123.0 1612411506789
1417 "#;
1418
1419 let result = events_to_metrics(parse_text(exp)).unwrap();
1420 assert_eq!(result.len(), 2);
1421
1422 let nan_metric = result
1424 .iter()
1425 .find(|m| m.tags().as_ref().and_then(|tags| tags.get("labelname")) == Some("val1"))
1426 .unwrap();
1427
1428 match nan_metric.value() {
1429 MetricValue::Counter { value } => {
1430 assert!(value.is_nan());
1431 }
1432 _ => unreachable!(),
1433 }
1434 }
1435
1436 #[test]
1437 fn test_skip_nan_gauge_enabled() {
1438 let exp = r#"
1439 # TYPE name gauge
1440 name{labelname="val1"} NaN 1612411506789
1441 name{labelname="val2"} 123.0 1612411506789
1442 "#;
1443
1444 let result = events_to_metrics(parse_text_with_nan_filtering(exp)).unwrap();
1445 assert_eq!(result.len(), 1);
1446 assert_eq!(result[0].name(), "name");
1447 match result[0].value() {
1448 MetricValue::Gauge { value } => {
1449 assert_eq!(*value, 123.0);
1450 }
1451 _ => unreachable!(),
1452 }
1453 }
1454
1455 #[test]
1456 fn test_skip_nan_histogram_bucket_enabled() {
1457 let exp = r#"
1458 # TYPE duration histogram
1459 duration_bucket{le="1"} 133988 1612411506789
1460 duration_bucket{le="NaN"} 144320 1612411506789
1461 duration_sum 53423 1612411506789
1462 duration_count 144320 1612411506789
1463 "#;
1464
1465 let result = events_to_metrics(parse_text_with_nan_filtering(exp)).unwrap();
1466 assert_eq!(result.len(), 0); }
1468
1469 #[test]
1470 fn test_skip_nan_histogram_sum_enabled() {
1471 let exp = r#"
1472 # TYPE duration histogram
1473 duration_bucket{le="1"} 133988 1612411506789
1474 duration_bucket{le="+Inf"} 144320 1612411506789
1475 duration_sum NaN 1612411506789
1476 duration_count 144320 1612411506789
1477 "#;
1478
1479 let result = events_to_metrics(parse_text_with_nan_filtering(exp)).unwrap();
1480 assert_eq!(result.len(), 0); }
1482
1483 #[test]
1484 fn test_skip_nan_summary_enabled() {
1485 let exp = r#"
1486 # TYPE duration summary
1487 duration{quantile="0.5"} NaN 1612411506789
1488 duration{quantile="0.99"} 76656 1612411506789
1489 duration_sum 1.7560473e+07 1612411506789
1490 duration_count 2693 1612411506789
1491 "#;
1492
1493 let result = events_to_metrics(parse_text_with_nan_filtering(exp)).unwrap();
1494 assert_eq!(result.len(), 0); }
1496
1497 #[test]
1498 fn test_skip_nan_all_valid_values() {
1499 let exp = r#"
1500 # TYPE counter_metric counter
1501 counter_metric 123.0 1612411506789
1502 # TYPE gauge_metric gauge
1503 gauge_metric 456.0 1612411506789
1504 "#;
1505
1506 let result = events_to_metrics(parse_text_with_nan_filtering(exp)).unwrap();
1507 assert_eq!(result.len(), 2); }
1509}