1use std::{any::Any, sync::Arc};
2
3use crate::{
4 Metric, MetricType, MetricValue,
5 encoding::EncodableMetric,
6 iterable::{FieldIter, IntoIterable, Iterable},
7};
8
9pub trait MetricsGroup:
11 Any + Iterable + IntoIterable + std::fmt::Debug + 'static + Send + Sync
12{
13 fn name(&self) -> &'static str;
15
16 fn iter(&self) -> FieldIter<'_> {
18 self.field_iter()
19 }
20}
21
22#[derive(Debug, Clone, Copy)]
24pub struct MetricItem<'a> {
25 pub(crate) name: &'static str,
26 pub(crate) help: &'static str,
27 pub(crate) metric: &'a dyn Metric,
28}
29
30impl EncodableMetric for MetricItem<'_> {
31 fn name(&self) -> &str {
32 self.name
33 }
34
35 fn help(&self) -> &str {
36 self.help
37 }
38
39 fn r#type(&self) -> MetricType {
40 self.metric.r#type()
41 }
42
43 fn value(&self) -> MetricValue {
44 self.metric.value()
45 }
46}
47
48impl<'a> MetricItem<'a> {
49 pub fn new(name: &'static str, help: &'static str, metric: &'a dyn Metric) -> Self {
51 Self { name, help, metric }
52 }
53
54 pub fn as_any(&self) -> &dyn Any {
56 self.metric.as_any()
57 }
58
59 pub fn name(&self) -> &'static str {
61 self.name
62 }
63
64 pub fn help(&self) -> &'static str {
66 self.help
67 }
68
69 pub fn r#type(&self) -> MetricType {
71 self.metric.r#type()
72 }
73
74 pub fn value(&self) -> MetricValue {
76 self.metric.value()
77 }
78}
79
80pub trait MetricsGroupSet {
82 fn name(&self) -> &'static str;
84
85 fn groups_cloned(&self) -> impl Iterator<Item = Arc<dyn MetricsGroup>>;
87
88 fn groups(&self) -> impl Iterator<Item = &dyn MetricsGroup>;
90
91 fn iter(&self) -> impl Iterator<Item = (&'static str, MetricItem<'_>)> + '_ {
95 self.groups()
96 .flat_map(|group| group.iter().map(|item| (group.name(), item)))
97 }
98}
99
100#[cfg(all(test, not(feature = "metrics")))]
103mod tests {
104 use crate::Counter;
105
106 #[test]
107 fn test() {
108 let counter = Counter::new();
109 counter.inc();
110 assert_eq!(counter.get(), 0);
111 }
112}
113
114#[cfg(all(test, feature = "metrics"))]
116mod tests {
117 use std::sync::RwLock;
118
119 use serde::{Deserialize, Serialize};
120
121 use super::*;
122 use crate::{
123 Counter, Gauge, Histogram, MetricType, MetricsGroupSet, MetricsSource, Registry,
124 encoding::{Decoder, Encoder},
125 iterable::Iterable,
126 };
127
128 #[derive(Debug, Iterable, Serialize, Deserialize)]
129 pub struct FooMetrics {
130 pub metric_a: Counter,
131 pub metric_b: Gauge,
132 }
133
134 impl Default for FooMetrics {
135 fn default() -> Self {
136 Self {
137 metric_a: Counter::new(),
138 metric_b: Gauge::new(),
139 }
140 }
141 }
142
143 impl MetricsGroup for FooMetrics {
144 fn name(&self) -> &'static str {
145 "foo"
146 }
147 }
148
149 #[derive(Debug, Default, Iterable, Serialize, Deserialize)]
150 pub struct BarMetrics {
151 pub count: Counter,
153 }
154
155 impl MetricsGroup for BarMetrics {
156 fn name(&self) -> &'static str {
157 "bar"
158 }
159 }
160
161 #[derive(Debug, Default, Serialize, Deserialize, MetricsGroupSet)]
162 #[metrics(name = "combined")]
163 struct CombinedMetrics {
164 foo: Arc<FooMetrics>,
165 bar: Arc<BarMetrics>,
166 }
167
168 #[allow(unused)]
170 #[derive(Debug, Default)]
171 struct CombinedMetricsManual {
172 foo: Arc<FooMetrics>,
173 bar: Arc<BarMetrics>,
174 }
175
176 impl MetricsGroupSet for CombinedMetricsManual {
177 fn name(&self) -> &'static str {
178 "combined"
179 }
180
181 fn groups_cloned(&self) -> impl Iterator<Item = Arc<dyn MetricsGroup>> {
182 [
183 self.foo.clone() as Arc<dyn MetricsGroup>,
184 self.bar.clone() as Arc<dyn MetricsGroup>,
185 ]
186 .into_iter()
187 }
188
189 fn groups(&self) -> impl Iterator<Item = &dyn MetricsGroup> {
190 [
191 &*self.foo as &dyn MetricsGroup,
192 &*self.bar as &dyn MetricsGroup,
193 ]
194 .into_iter()
195 }
196 }
197
198 #[test]
199 fn test_metric_help() -> Result<(), Box<dyn std::error::Error>> {
200 let metrics = FooMetrics::default();
201 let items: Vec<_> = metrics.iter().collect();
202 assert_eq!(items.len(), 2);
203 assert_eq!(items[0].name(), "metric_a");
204 assert_eq!(items[0].help(), "metric_a");
205 assert_eq!(items[0].r#type(), MetricType::Counter);
206 assert_eq!(items[1].name(), "metric_b");
207 assert_eq!(items[1].help(), "metric_b");
208 assert_eq!(items[1].r#type(), MetricType::Gauge);
209
210 Ok(())
211 }
212
213 #[test]
214 fn test_solo_registry() -> Result<(), Box<dyn std::error::Error>> {
215 let mut registry = Registry::default();
216 let metrics = Arc::new(FooMetrics::default());
217 registry.register(metrics.clone());
218
219 metrics.metric_a.inc();
220 metrics.metric_b.inc_by(2);
221 metrics.metric_b.inc_by(3);
222 assert_eq!(metrics.metric_a.get(), 1);
223 assert_eq!(metrics.metric_b.get(), 5);
224 metrics.metric_a.set(0);
225 metrics.metric_b.set(0);
226 assert_eq!(metrics.metric_a.get(), 0);
227 assert_eq!(metrics.metric_b.get(), 0);
228 metrics.metric_a.inc_by(5);
229 metrics.metric_b.inc_by(2);
230 assert_eq!(metrics.metric_a.get(), 5);
231 assert_eq!(metrics.metric_b.get(), 2);
232
233 let exp = "# HELP foo_metric_a metric_a.
234# TYPE foo_metric_a counter
235foo_metric_a_total 5
236# HELP foo_metric_b metric_b.
237# TYPE foo_metric_b gauge
238foo_metric_b 2
239# EOF
240";
241 let enc = registry.encode_openmetrics_to_string()?;
242 assert_eq!(enc, exp);
243 Ok(())
244 }
245
246 #[test]
247 fn test_metric_sets() {
248 let metrics = CombinedMetrics::default();
249 metrics.foo.metric_a.inc();
250 metrics.foo.metric_b.set(-42);
251 metrics.bar.count.inc_by(10);
252
253 let collected = metrics
255 .iter()
256 .map(|(group, metric)| (group, metric.name(), metric.help(), metric.value().to_f32()));
257 assert_eq!(
258 collected.collect::<Vec<_>>(),
259 vec![
260 ("foo", "metric_a", "metric_a", 1.0),
261 ("foo", "metric_b", "metric_b", -42.0),
262 ("bar", "count", "Bar Count", 10.0),
263 ]
264 );
265
266 let mut collected = vec![];
268 for group in metrics.groups() {
269 for metric in group.iter() {
270 if let Some(counter) = metric.as_any().downcast_ref::<Counter>() {
271 collected.push((group.name(), metric.name(), counter.value()));
272 }
273 if let Some(gauge) = metric.as_any().downcast_ref::<Gauge>() {
274 collected.push((group.name(), metric.name(), gauge.value()));
275 }
276 }
277 }
278 assert_eq!(
279 collected,
280 vec![
281 ("foo", "metric_a", MetricValue::Counter(1)),
282 ("foo", "metric_b", MetricValue::Gauge(-42)),
283 ("bar", "count", MetricValue::Counter(10)),
284 ]
285 );
286
287 let mut registry = Registry::default();
289 let sub = registry.sub_registry_with_prefix("boo");
290 sub.register_all(&metrics);
291 let exp = "# HELP boo_foo_metric_a metric_a.
292# TYPE boo_foo_metric_a counter
293boo_foo_metric_a_total 1
294# HELP boo_foo_metric_b metric_b.
295# TYPE boo_foo_metric_b gauge
296boo_foo_metric_b -42
297# HELP boo_bar_count Bar Count.
298# TYPE boo_bar_count counter
299boo_bar_count_total 10
300# EOF
301";
302 assert_eq!(registry.encode_openmetrics_to_string().unwrap(), exp);
303
304 let sub = registry.sub_registry_with_labels([("x", "y")]);
305 sub.register_all_prefixed(&metrics);
306 let exp = r#"# HELP boo_foo_metric_a metric_a.
307# TYPE boo_foo_metric_a counter
308boo_foo_metric_a_total 1
309# HELP boo_foo_metric_b metric_b.
310# TYPE boo_foo_metric_b gauge
311boo_foo_metric_b -42
312# HELP boo_bar_count Bar Count.
313# TYPE boo_bar_count counter
314boo_bar_count_total 10
315# HELP combined_foo_metric_a metric_a.
316# TYPE combined_foo_metric_a counter
317combined_foo_metric_a_total{x="y"} 1
318# HELP combined_foo_metric_b metric_b.
319# TYPE combined_foo_metric_b gauge
320combined_foo_metric_b{x="y"} -42
321# HELP combined_bar_count Bar Count.
322# TYPE combined_bar_count counter
323combined_bar_count_total{x="y"} 10
324# EOF
325"#;
326
327 assert_eq!(registry.encode_openmetrics_to_string().unwrap(), exp);
328 }
329
330 #[test]
331 fn test_derive() {
332 use crate::{MetricValue, MetricsGroup};
333
334 #[derive(Debug, MetricsGroup)]
335 #[metrics(default, name = "my-metrics")]
336 struct Metrics {
337 foo: Counter,
341 bar: Counter,
343 #[metrics(help = "Measures baz")]
345 baz: Gauge,
346 #[metrics(help = "foo")]
347 #[default(Histogram::new(vec![0.0, 0.01, 0.05, 0.1, 0.2, 0.5, 1.0]))]
348 histo: Histogram,
349 }
350
351 let metrics = Metrics::default();
352 assert_eq!(metrics.name(), "my-metrics");
353
354 metrics.foo.inc();
355 metrics.bar.inc_by(2);
356 metrics.baz.set(3);
357
358 let mut values = metrics.iter();
359 let foo = values.next().unwrap();
360 let bar = values.next().unwrap();
361 let baz = values.next().unwrap();
362 assert_eq!(foo.value(), MetricValue::Counter(1));
363 assert_eq!(foo.name(), "foo");
364 assert_eq!(foo.help(), "Counts foos");
365 assert_eq!(bar.value(), MetricValue::Counter(2));
366 assert_eq!(bar.name(), "bar");
367 assert_eq!(bar.help(), "bar");
368 assert_eq!(baz.value(), MetricValue::Gauge(3));
369 assert_eq!(baz.name(), "baz");
370 assert_eq!(baz.help(), "Measures baz");
371
372 #[derive(Debug, Default, MetricsGroup)]
373 struct FooMetrics {}
374 let metrics = FooMetrics::default();
375 assert_eq!(metrics.name(), "foo_metrics");
376 let mut values = metrics.iter();
377 assert!(values.next().is_none());
378 }
379
380 #[test]
381 fn test_serde() {
382 let metrics = CombinedMetrics::default();
383 metrics.foo.metric_a.inc();
384 metrics.foo.metric_b.set(-42);
385 metrics.bar.count.inc_by(10);
386 let encoded = postcard::to_stdvec(&metrics).unwrap();
387 let decoded: CombinedMetrics = postcard::from_bytes(&encoded).unwrap();
388 assert_eq!(decoded.foo.metric_a.get(), 1);
389 assert_eq!(decoded.foo.metric_b.get(), -42);
390 assert_eq!(decoded.bar.count.get(), 10);
391 }
392
393 #[test]
394 fn test_encode_decode() {
395 let mut registry = Registry::default();
396 let metrics = Arc::new(FooMetrics::default());
397 registry.register(metrics.clone());
398
399 metrics.metric_a.inc();
400 metrics.metric_b.set(-42);
401
402 let om_from_registry = registry.encode_openmetrics_to_string().unwrap();
403 println!("openmetrics len {}", om_from_registry.len());
404
405 let registry = Arc::new(RwLock::new(registry));
406
407 let mut encoder = Encoder::new(registry.clone());
408 let update = encoder.export_bytes().unwrap();
409 println!("first update len {}", update.len());
410
411 let mut decoder = Decoder::default();
412 decoder.import_bytes(&update).unwrap();
413
414 let om_from_decoder = decoder.encode_openmetrics_to_string().unwrap();
415 assert_eq!(om_from_decoder, om_from_registry);
416
417 metrics.metric_a.inc();
418 metrics.metric_b.set(99);
419
420 let update = encoder.export_bytes().unwrap();
421 println!("second update len {}", update.len());
422 decoder.import_bytes(&update).unwrap();
423
424 let om_from_registry = registry.encode_openmetrics_to_string().unwrap();
425 let om_from_decoder = decoder.encode_openmetrics_to_string().unwrap();
426 assert_eq!(om_from_decoder, om_from_registry);
427
428 for item in decoder.iter() {
429 assert!(item.help.is_some());
430 }
431
432 let mut encoder = Encoder::new_with_opts(
433 registry.clone(),
434 crate::encoding::EncoderOpts {
435 include_help: false,
436 },
437 );
438 let mut decoder = Decoder::default();
439 decoder.import_bytes(&update).unwrap();
440 decoder.import(encoder.export());
441 for item in decoder.iter() {
442 assert_eq!(item.help, None);
443 }
444 }
445
446 #[test]
447 fn test_histogram() {
448 use crate::Histogram;
449
450 let histogram = Histogram::new(vec![1.0, 5.0, 10.0, 50.0, 100.0, f64::INFINITY]);
451
452 histogram.observe(0.5);
453 histogram.observe(2.5);
454 histogram.observe(7.5);
455 histogram.observe(25.0);
456 histogram.observe(75.0);
457 histogram.observe(150.0);
458
459 assert_eq!(histogram.count(), 6);
460 assert_eq!(histogram.sum(), 260.5);
461
462 let buckets = histogram.buckets();
463 assert_eq!(buckets.len(), 6);
464 assert_eq!(buckets[0], (1.0, 1));
465 assert_eq!(buckets[1], (5.0, 2));
466 assert_eq!(buckets[2], (10.0, 3));
467 assert_eq!(buckets[3], (50.0, 4));
468 assert_eq!(buckets[4], (100.0, 5));
469 assert_eq!(buckets[5], (f64::INFINITY, 6));
470
471 let p50 = histogram.percentile(0.5);
472 assert_eq!(p50, 10.0);
473
474 let p99 = histogram.percentile(0.99);
475 assert_eq!(p99, 100.0);
476
477 let p100 = histogram.percentile(1.0);
478 assert_eq!(p100, f64::INFINITY);
479 }
480
481 #[test]
482 fn test_histogram_prometheus_format() {
483 use crate::Histogram;
484
485 #[derive(Debug, Iterable)]
486 pub struct HistogramMetrics {
487 pub response_time: Histogram,
488 }
489
490 impl MetricsGroup for HistogramMetrics {
491 fn name(&self) -> &'static str {
492 "http"
493 }
494 }
495
496 let metrics = HistogramMetrics {
497 response_time: Histogram::new(vec![0.1, 0.5, 1.0, 5.0, f64::INFINITY]),
498 };
499
500 metrics.response_time.observe(0.05);
501 metrics.response_time.observe(0.3);
502 metrics.response_time.observe(0.8);
503 metrics.response_time.observe(2.5);
504
505 let mut registry = Registry::default();
506 registry.register(Arc::new(metrics));
507
508 let output = registry.encode_openmetrics_to_string().unwrap();
509
510 let parsed = prometheus_parse::Scrape::parse(output.lines().map(|s| Ok(s.to_owned())))
511 .expect("Failed to parse Prometheus output");
512
513 assert_eq!(parsed.samples.len(), 3);
514
515 let histogram_sample = parsed
516 .samples
517 .iter()
518 .find(|s| s.metric == "http_response_time")
519 .expect("Expected to find http_response_time histogram");
520
521 let sum_sample = parsed
522 .samples
523 .iter()
524 .find(|s| s.metric == "http_response_time_sum")
525 .expect("Expected to find http_response_time_sum");
526
527 let count_sample = parsed
528 .samples
529 .iter()
530 .find(|s| s.metric == "http_response_time_count")
531 .expect("Expected to find http_response_time_count");
532
533 if let prometheus_parse::Value::Untyped(sum) = sum_sample.value {
534 assert_eq!(sum, 3.65);
535 } else {
536 panic!("Expected sum value");
537 }
538
539 if let prometheus_parse::Value::Untyped(count) = count_sample.value {
540 assert_eq!(count, 4.0);
541 } else {
542 panic!("Expected count value");
543 }
544
545 if let prometheus_parse::Value::Histogram(buckets) = &histogram_sample.value {
546 assert_eq!(buckets.len(), 5);
547
548 assert_eq!(buckets[0].less_than, 0.1);
549 assert_eq!(buckets[0].count, 1.0);
550
551 assert_eq!(buckets[1].less_than, 0.5);
552 assert_eq!(buckets[1].count, 2.0);
553
554 assert_eq!(buckets[2].less_than, 1.0);
555 assert_eq!(buckets[2].count, 3.0);
556
557 assert_eq!(buckets[3].less_than, 5.0);
558 assert_eq!(buckets[3].count, 4.0);
559
560 assert_eq!(buckets[4].less_than, f64::INFINITY);
561 assert_eq!(buckets[4].count, 4.0);
562 } else {
563 panic!("Expected histogram value, got {:?}", histogram_sample.value);
564 }
565 }
566}