1#![cfg_attr(test, feature(test))]
17
18extern crate futures;
19extern crate hdrsample;
20#[macro_use]
21extern crate log;
22extern crate ordermap;
23#[cfg(test)]
24extern crate test;
25
26use futures::{Future, Poll};
27use hdrsample::Histogram;
28use ordermap::OrderMap;
29use std::boxed::Box;
30use std::collections::BTreeMap;
31use std::fmt;
32use std::sync::{Arc, Mutex, Weak};
33use std::sync::atomic::{AtomicUsize, Ordering};
34use std::time::Instant;
35
36pub mod prometheus;
37mod report;
38mod timing;
39
40pub use report::{Reporter, Report};
41pub use timing::Timing;
42
43type Labels = BTreeMap<&'static str, String>;
44type CounterMap = OrderMap<Key, Arc<AtomicUsize>>;
45type GaugeMap = OrderMap<Key, Arc<AtomicUsize>>;
46type StatMap = OrderMap<Key, Arc<Mutex<HistogramWithSum>>>;
47
48#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
49pub enum Prefix {
50 Root,
51 Node {
52 prefix: Arc<Prefix>,
53 value: &'static str,
54 },
55}
56
57
58pub fn new() -> (Scope, Reporter) {
65 let registry = Arc::new(Mutex::new(Registry::default()));
66
67 let scope = Scope {
68 labels: Labels::default(),
69 prefix: Arc::new(Prefix::Root),
70 registry: registry.clone(),
71 };
72
73 (scope, report::new(registry))
74}
75
76#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
78pub struct Key {
79 name: &'static str,
80 prefix: Arc<Prefix>,
81 labels: Labels,
82}
83impl Key {
84 fn new(name: &'static str, prefix: Arc<Prefix>, labels: Labels) -> Key {
85 Key {
86 name,
87 prefix,
88 labels,
89 }
90 }
91
92 pub fn name(&self) -> &'static str {
93 self.name
94 }
95 pub fn prefix(&self) -> &Arc<Prefix> {
96 &self.prefix
97 }
98 pub fn labels(&self) -> &Labels {
99 &self.labels
100 }
101}
102
103#[derive(Default)]
104pub struct Registry {
105 counters: CounterMap,
106 gauges: GaugeMap,
107 stats: StatMap,
108}
109
110#[derive(Clone)]
117pub struct Scope {
118 labels: Labels,
119 prefix: Arc<Prefix>,
120 registry: Arc<Mutex<Registry>>,
121}
122
123impl Scope {
124 pub fn labels(&self) -> &Labels {
126 &self.labels
127 }
128
129 pub fn labeled<D: fmt::Display>(mut self, k: &'static str, v: D) -> Self {
131 self.labels.insert(k, format!("{}", v));
132 self
133 }
134
135 pub fn prefixed(mut self, value: &'static str) -> Self {
137 let p = Prefix::Node {
138 prefix: self.prefix,
139 value,
140 };
141 self.prefix = Arc::new(p);
142 self
143 }
144
145 pub fn counter(&self, name: &'static str) -> Counter {
147 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
148 let mut reg = self.registry.lock().expect(
149 "failed to obtain lock on registry",
150 );
151
152 if let Some(c) = reg.counters.get(&key) {
153 return Counter(Arc::downgrade(c));
154 }
155
156 let c = Arc::new(AtomicUsize::new(0));
157 let counter = Counter(Arc::downgrade(&c));
158 reg.counters.insert(key, c);
159 counter
160 }
161
162 pub fn gauge(&self, name: &'static str) -> Gauge {
164 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
165 let mut reg = self.registry.lock().expect(
166 "failed to obtain lock on registry",
167 );
168
169 if let Some(g) = reg.gauges.get(&key) {
170 return Gauge(Arc::downgrade(g));
171 }
172
173 let g = Arc::new(AtomicUsize::new(0));
174 let gauge = Gauge(Arc::downgrade(&g));
175 reg.gauges.insert(key, g);
176 gauge
177 }
178
179 pub fn stat(&self, name: &'static str) -> Stat {
183 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
184 self.mk_stat(key, None)
185 }
186
187 pub fn timer_us(&self, name: &'static str) -> Timer {
188 Timer {
189 stat: self.stat(name),
190 unit: TimeUnit::Micros,
191 }
192 }
193
194 pub fn timer_ms(&self, name: &'static str) -> Timer {
195 Timer {
196 stat: self.stat(name),
197 unit: TimeUnit::Millis,
198 }
199 }
200
201 pub fn stat_with_bounds(&self, name: &'static str, low: u64, high: u64) -> Stat {
203 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
204 self.mk_stat(key, Some((low, high)))
205 }
206
207 fn mk_stat(&self, key: Key, bounds: Option<(u64, u64)>) -> Stat {
208 let mut reg = self.registry.lock().expect(
209 "failed to obtain lock on registry",
210 );
211
212 if let Some(h) = reg.stats.get(&key) {
213 let histo = Arc::downgrade(h);
214 return Stat { histo, bounds };
215 }
216
217 let h = Arc::new(Mutex::new(HistogramWithSum::new(bounds)));
218 let histo = Arc::downgrade(&h);
219 reg.stats.insert(key, h);
220 Stat { histo, bounds }
221 }
222}
223
224#[derive(Clone)]
226pub struct Counter(Weak<AtomicUsize>);
227impl Counter {
228 pub fn incr(&self, v: usize) {
229 if let Some(c) = self.0.upgrade() {
230 c.fetch_add(v, Ordering::AcqRel);
231 }
232 }
233}
234
235#[derive(Clone)]
237pub struct Gauge(Weak<AtomicUsize>);
238impl Gauge {
239 pub fn incr(&self, v: usize) {
240 if let Some(g) = self.0.upgrade() {
241 g.fetch_add(v, Ordering::AcqRel);
242 } else {
243 debug!("gauge dropped");
244 }
245 }
246 pub fn decr(&self, v: usize) {
247 if let Some(g) = self.0.upgrade() {
248 g.fetch_sub(v, Ordering::AcqRel);
249 } else {
250 debug!("gauge dropped");
251 }
252 }
253 pub fn set(&self, v: usize) {
254 if let Some(g) = self.0.upgrade() {
255 g.store(v, Ordering::Release);
256 } else {
257 debug!("gauge dropped");
258 }
259 }
260}
261
262const HISTOGRAM_PRECISION: u32 = 4;
264
265#[derive(Clone)]
270pub struct HistogramWithSum {
271 histogram: Histogram<usize>,
272 sum: u64,
273}
274
275impl HistogramWithSum {
276 fn new(bounds: Option<(u64, u64)>) -> Self {
278 let h = match bounds {
279 None => Histogram::<usize>::new(HISTOGRAM_PRECISION),
280 Some((l, h)) => Histogram::<usize>::new_with_bounds(l, h, HISTOGRAM_PRECISION),
281 };
282 let histogram = h.expect("failed to create histogram");
283 HistogramWithSum { histogram, sum: 0 }
284 }
285
286 fn record(&mut self, v: u64) {
288 if let Err(e) = self.histogram.record(v) {
289 error!("failed to add value to histogram: {:?}", e);
290 }
291 if v >= ::std::u64::MAX - self.sum {
292 self.sum = ::std::u64::MAX
293 } else {
294 self.sum += v;
295 }
296 }
297
298 pub fn histogram(&self) -> &Histogram<usize> {
299 &self.histogram
300 }
301 pub fn count(&self) -> u64 {
302 self.histogram.count()
303 }
304 pub fn max(&self) -> u64 {
305 self.histogram.max()
306 }
307 pub fn min(&self) -> u64 {
308 self.histogram.min()
309 }
310 pub fn sum(&self) -> u64 {
311 self.sum
312 }
313
314 pub fn clear(&mut self) {
315 self.histogram.reset();
316 self.sum = 0;
317 }
318}
319
320#[derive(Clone)]
322pub struct Stat {
323 histo: Weak<Mutex<HistogramWithSum>>,
324 bounds: Option<(u64, u64)>,
325}
326
327impl Stat {
328 pub fn add(&self, v: u64) {
329 if let Some(h) = self.histo.upgrade() {
330 let mut histo = h.lock().expect("failed to obtain lock for stat");
331 histo.record(v);
332 }
333 }
334
335 pub fn add_values(&mut self, vs: &[u64]) {
336 if let Some(h) = self.histo.upgrade() {
337 let mut histo = h.lock().expect("failed to obtain lock for stat");
338 for v in vs {
339 histo.record(*v)
340 }
341 }
342 }
343}
344
345#[derive(Clone)]
346pub struct Timer {
347 stat: Stat,
348 unit: TimeUnit,
349}
350#[derive(Copy, Clone)]
351pub enum TimeUnit {
352 Millis,
353 Micros,
354}
355impl Timer {
356 pub fn record_since(&self, t0: Instant) {
357 self.stat.add(to_u64(t0, self.unit));
358 }
359
360 pub fn time<F>(&self, fut: F) -> Timed<F>
361 where
362 F: Future + 'static,
363 {
364 let stat = self.stat.clone();
365 let unit = self.unit;
366 let f = futures::lazy(move || {
367 let t0 = Timing::start();
370 fut.then(move |v| {
371 stat.add(to_u64(t0, unit));
372 v
373 })
374 });
375 Timed(Box::new(f))
376 }
377}
378
379fn to_u64(t0: Instant, unit: TimeUnit) -> u64 {
380 match unit {
381 TimeUnit::Millis => t0.elapsed_ms(),
382 TimeUnit::Micros => t0.elapsed_us(),
383 }
384}
385
386pub struct Timed<F: Future>(Box<Future<Item = F::Item, Error = F::Error>>);
387impl<F: Future> Future for Timed<F> {
388 type Item = F::Item;
389 type Error = F::Error;
390 fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
391 self.0.poll()
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398 use test::Bencher;
399
400 static DEFAULT_METRIC_NAME: &'static str = "a_sufficiently_long_name";
401
402 #[bench]
403 fn bench_scope_clone(b: &mut Bencher) {
404 let (metrics, _) = super::new();
405 b.iter(move || { let _ = metrics.clone(); });
406 }
407
408 #[bench]
409 fn bench_scope_label(b: &mut Bencher) {
410 let (metrics, _) = super::new();
411 b.iter(move || { let _ = metrics.clone().labeled("foo", "bar"); });
412 }
413
414 #[bench]
415 fn bench_scope_clone_x1000(b: &mut Bencher) {
416 let scopes = mk_scopes(1000, "bench_scope_clone_x1000");
417 b.iter(move || for scope in &scopes {
418 let _ = scope.clone();
419 });
420 }
421
422 #[bench]
423 fn bench_scope_label_x1000(b: &mut Bencher) {
424 let scopes = mk_scopes(1000, "bench_scope_label_x1000");
425 b.iter(move || for scope in &scopes {
426 let _ = scope.clone().labeled("foo", "bar");
427 });
428 }
429
430 #[bench]
431 fn bench_counter_create(b: &mut Bencher) {
432 let (metrics, _) = super::new();
433 b.iter(move || { let _ = metrics.counter(DEFAULT_METRIC_NAME); });
434 }
435
436 #[bench]
437 fn bench_gauge_create(b: &mut Bencher) {
438 let (metrics, _) = super::new();
439 b.iter(move || { let _ = metrics.gauge(DEFAULT_METRIC_NAME); });
440 }
441
442 #[bench]
443 fn bench_stat_create(b: &mut Bencher) {
444 let (metrics, _) = super::new();
445 b.iter(move || { let _ = metrics.stat(DEFAULT_METRIC_NAME); });
446 }
447
448 #[bench]
449 fn bench_counter_create_x1000(b: &mut Bencher) {
450 let scopes = mk_scopes(1000, "bench_counter_create_x1000");
451 b.iter(move || for scope in &scopes {
452 scope.counter(DEFAULT_METRIC_NAME);
453 });
454 }
455
456 #[bench]
457 fn bench_gauge_create_x1000(b: &mut Bencher) {
458 let scopes = mk_scopes(1000, "bench_gauge_create_x1000");
459 b.iter(move || for scope in &scopes {
460 scope.gauge(DEFAULT_METRIC_NAME);
461 });
462 }
463
464 #[bench]
465 fn bench_stat_create_x1000(b: &mut Bencher) {
466 let scopes = mk_scopes(1000, "bench_stat_create_x1000");
467 b.iter(move || for scope in &scopes {
468 scope.stat(DEFAULT_METRIC_NAME);
469 });
470 }
471
472 #[bench]
473 fn bench_counter_update(b: &mut Bencher) {
474 let (metrics, _) = super::new();
475 let c = metrics.counter(DEFAULT_METRIC_NAME);
476 b.iter(move || c.incr(1));
477 }
478
479 #[bench]
480 fn bench_gauge_update(b: &mut Bencher) {
481 let (metrics, _) = super::new();
482 let g = metrics.gauge(DEFAULT_METRIC_NAME);
483 b.iter(move || g.set(1));
484 }
485
486 #[bench]
487 fn bench_stat_update(b: &mut Bencher) {
488 let (metrics, _) = super::new();
489 let s = metrics.stat(DEFAULT_METRIC_NAME);
490 b.iter(move || s.add(1));
491 }
492
493 #[bench]
494 fn bench_counter_update_x1000(b: &mut Bencher) {
495 let counters: Vec<Counter> = mk_scopes(1000, "bench_counter_update_x1000")
496 .iter()
497 .map(|s| s.counter(DEFAULT_METRIC_NAME))
498 .collect();
499 b.iter(move || for c in &counters {
500 c.incr(1)
501 });
502 }
503
504 #[bench]
505 fn bench_gauge_update_x1000(b: &mut Bencher) {
506 let gauges: Vec<Gauge> = mk_scopes(1000, "bench_gauge_update_x1000")
507 .iter()
508 .map(|s| s.gauge(DEFAULT_METRIC_NAME))
509 .collect();
510 b.iter(move || for g in &gauges {
511 g.set(1)
512 });
513 }
514
515 #[bench]
516 fn bench_stat_update_x1000(b: &mut Bencher) {
517 let stats: Vec<Stat> = mk_scopes(1000, "bench_stat_update_x1000")
518 .iter()
519 .map(|s| s.stat(DEFAULT_METRIC_NAME))
520 .collect();
521 b.iter(move || for s in &stats {
522 s.add(1)
523 });
524 }
525
526 #[bench]
527 fn bench_stat_add_x1000(b: &mut Bencher) {
528 let s = {
529 let (metrics, _) = super::new();
530 metrics.stat(DEFAULT_METRIC_NAME)
531 };
532 b.iter(move || for i in 0..1000 {
533 s.add(i)
534 });
535 }
536
537 fn mk_scopes(n: usize, name: &str) -> Vec<Scope> {
538 let (metrics, _) = super::new();
539 let metrics = metrics.prefixed("t").labeled("test_name", name).labeled(
540 "total_iterations",
541 n,
542 );
543 (0..n)
544 .map(|i| metrics.clone().labeled("iteration", format!("{}", i)))
545 .collect()
546 }
547
548 #[test]
549 fn test_report_peek() {
550 let (metrics, reporter) = super::new();
551 let metrics = metrics.labeled("joy", "painting");
552
553 let happy_accidents = metrics.counter("happy_accidents");
554 let paint_level = metrics.gauge("paint_level");
555 let mut stroke_len = metrics.stat("stroke_len");
556
557 happy_accidents.incr(1);
558 paint_level.set(2);
559 stroke_len.add_values(&[1, 2, 3]);
560
561 {
562 let report = reporter.peek();
563 {
564 let k = report
565 .counters()
566 .keys()
567 .find(|k| k.name() == "happy_accidents")
568 .expect("expected counter: happy_accidents");
569 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
570 assert_eq!(report.counters().get(&k), Some(&1));
571 }
572 {
573 let k = report
574 .gauges()
575 .keys()
576 .find(|k| k.name() == "paint_level")
577 .expect("expected gauge: paint_level");
578 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
579 assert_eq!(report.gauges().get(&k), Some(&2));
580 }
581 assert_eq!(
582 report.gauges().keys().find(|k| k.name() == "brush_width"),
583 None
584 );
585 {
586 let k = report
587 .stats()
588 .keys()
589 .find(|k| k.name() == "stroke_len")
590 .expect("expected stat: stroke_len");
591 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
592 assert!(report.stats().contains_key(&k));
593 }
594 assert_eq!(report.stats().keys().find(|k| k.name() == "tree_len"), None);
595 }
596
597 drop(paint_level);
598 let brush_width = metrics.gauge("brush_width");
599 let mut tree_len = metrics.stat("tree_len");
600
601 happy_accidents.incr(2);
602 brush_width.set(5);
603 stroke_len.add_values(&[1, 2, 3]);
604 tree_len.add_values(&[3, 4, 5]);
605
606 {
607 let report = reporter.peek();
608 {
609 let k = report
610 .counters()
611 .keys()
612 .find(|k| k.name() == "happy_accidents")
613 .expect("expected counter: happy_accidents");
614 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
615 assert_eq!(report.counters().get(&k), Some(&3));
616 }
617 {
618 let k = report
619 .gauges()
620 .keys()
621 .find(|k| k.name() == "paint_level")
622 .expect("expected gauge: paint_level");
623 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
624 assert_eq!(report.gauges().get(&k), Some(&2));
625 }
626 {
627 let k = report
628 .gauges()
629 .keys()
630 .find(|k| k.name() == "brush_width")
631 .expect("expected gauge: brush_width");
632 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
633 assert_eq!(report.gauges().get(&k), Some(&5));
634 }
635 {
636 let k = report
637 .stats()
638 .keys()
639 .find(|k| k.name() == "stroke_len")
640 .expect("expected stat: stroke_len");
641 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
642 assert!(report.stats().contains_key(&k));
643 }
644 {
645 let k = report
646 .stats()
647 .keys()
648 .find(|k| k.name() == "tree_len")
649 .expect("expected stat: tree_len");
650 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
651 assert!(report.stats().contains_key(&k));
652 }
653 }
654 }
655
656 #[test]
657 fn test_report_take() {
658 let (metrics, mut reporter) = super::new();
659 let metrics = metrics.labeled("joy", "painting");
660
661 let happy_accidents = metrics.counter("happy_accidents");
662 let paint_level = metrics.gauge("paint_level");
663 let mut stroke_len = metrics.stat("stroke_len");
664 happy_accidents.incr(1);
665 paint_level.set(2);
666 stroke_len.add_values(&[1, 2, 3]);
667 {
668 let report = reporter.take();
669 {
670 let k = report
671 .counters()
672 .keys()
673 .find(|k| k.name() == "happy_accidents")
674 .expect("expected counter: happy_accidents");
675 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
676 assert_eq!(report.counters().get(&k), Some(&1));
677 }
678 {
679 let k = report
680 .gauges()
681 .keys()
682 .find(|k| k.name() == "paint_level")
683 .expect("expected gauge");
684 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
685 assert_eq!(report.gauges().get(&k), Some(&2));
686 }
687 assert_eq!(
688 report.gauges().keys().find(|k| k.name() == "brush_width"),
689 None
690 );
691 {
692 let k = report
693 .stats()
694 .keys()
695 .find(|k| k.name() == "stroke_len")
696 .expect("expected stat");
697 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
698 assert!(report.stats().contains_key(&k));
699 }
700 assert_eq!(report.stats().keys().find(|k| k.name() == "tree_len"), None);
701 {
702 let k = report
703 .stats()
704 .keys()
705 .find(|k| k.name() == "stroke_len")
706 .expect("expected stat");
707 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
708 assert!(report.stats().contains_key(&k));
709 }
710 }
711
712 drop(paint_level);
713 drop(stroke_len);
714 {
715 let report = reporter.take();
716 {
717 let counters = report.counters();
718 let k = counters
719 .keys()
720 .find(|k| k.name() == "happy_accidents")
721 .expect("expected counter: happy_accidents");
722 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
723 assert_eq!(counters.get(&k), Some(&1));
724 }
725 {
726 let k = report
727 .gauges()
728 .keys()
729 .find(|k| k.name() == "paint_level")
730 .expect("expected gauge");
731 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
732 assert_eq!(report.gauges().get(&k), Some(&2));
733 }
734 {
735 let k = report
736 .stats()
737 .keys()
738 .find(|k| k.name() == "stroke_len")
739 .expect("expected stat");
740 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
741 assert!(report.stats().contains_key(&k));
742 }
743 }
744
745 let brush_width = metrics.gauge("brush_width");
746 let mut tree_len = metrics.stat("tree_len");
747 happy_accidents.incr(2);
748 brush_width.set(5);
749 tree_len.add_values(&[3, 4, 5]);
750 {
751 let report = reporter.take();
752 {
753 let k = report
754 .counters()
755 .keys()
756 .find(|k| k.name() == "happy_accidents")
757 .expect("expected counter: happy_accidents");
758 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
759 assert_eq!(report.counters().get(&k), Some(&3));
760 }
761 assert_eq!(
762 report.gauges().keys().find(|k| k.name() == "paint_level"),
763 None
764 );
765 {
766 let k = report
767 .gauges()
768 .keys()
769 .find(|k| k.name() == "brush_width")
770 .expect("expected gauge");
771 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
772 assert_eq!(report.gauges().get(&k), Some(&5));
773 }
774 assert_eq!(
775 report.stats().keys().find(|k| k.name() == "stroke_len"),
776 None
777 );
778 {
779 let k = report
780 .stats()
781 .keys()
782 .find(|k| k.name() == "tree_len")
783 .expect("expeced stat");
784 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
785 assert!(report.stats().contains_key(&k));
786 }
787 }
788 }
789}