1extern crate futures;
18extern crate hdrhistogram;
19#[macro_use]
20extern crate log;
21extern crate indexmap;
22use futures::future::FutureExt;
26use futures::task::Poll;
27use hdrhistogram::Histogram;
28use indexmap::IndexMap;
29use pin_project::pin_project;
30use std::collections::BTreeMap;
31use std::fmt;
32use std::future::Future;
33use std::pin::Pin;
34use std::sync::atomic::{AtomicUsize, Ordering};
35use std::sync::{Arc, Mutex, Weak};
36use std::time::Instant;
37
38pub mod prometheus;
39mod report;
40mod timing;
41
42pub use report::{Report, Reporter};
43pub use timing::Timing;
44
45type Labels = BTreeMap<&'static str, String>;
46type CounterMap = IndexMap<Key, Arc<AtomicUsize>>;
47type GaugeMap = IndexMap<Key, Arc<AtomicUsize>>;
48type StatMap = IndexMap<Key, Arc<Mutex<HistogramWithSum>>>;
49
50#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
51pub enum Prefix {
52 Root,
53 Node {
54 prefix: Arc<Prefix>,
55 value: &'static str,
56 },
57}
58
59pub fn new() -> (Scope, Reporter) {
66 let registry = Arc::new(Mutex::new(Registry::default()));
67
68 let scope = Scope {
69 labels: Labels::default(),
70 prefix: Arc::new(Prefix::Root),
71 registry: registry.clone(),
72 };
73
74 (scope, report::new(registry))
75}
76
77#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
79pub struct Key {
80 name: &'static str,
81 prefix: Arc<Prefix>,
82 labels: Labels,
83}
84impl Key {
85 fn new(name: &'static str, prefix: Arc<Prefix>, labels: Labels) -> Key {
86 Key {
87 name,
88 prefix,
89 labels,
90 }
91 }
92
93 pub fn name(&self) -> &'static str {
94 self.name
95 }
96 pub fn prefix(&self) -> &Arc<Prefix> {
97 &self.prefix
98 }
99 pub fn labels(&self) -> &Labels {
100 &self.labels
101 }
102}
103
104#[derive(Default)]
105pub struct Registry {
106 counters: CounterMap,
107 gauges: GaugeMap,
108 stats: StatMap,
109}
110
111#[derive(Clone)]
118pub struct Scope {
119 labels: Labels,
120 prefix: Arc<Prefix>,
121 registry: Arc<Mutex<Registry>>,
122}
123
124impl Scope {
125 pub fn labels(&self) -> &Labels {
127 &self.labels
128 }
129
130 pub fn labeled<D: fmt::Display>(mut self, k: &'static str, v: D) -> Self {
132 self.labels.insert(k, format!("{}", v));
133 self
134 }
135
136 pub fn prefixed(mut self, value: &'static str) -> Self {
138 let p = Prefix::Node {
139 prefix: self.prefix,
140 value,
141 };
142 self.prefix = Arc::new(p);
143 self
144 }
145
146 pub fn counter(&self, name: &'static str) -> Counter {
148 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
149 let mut reg = self
150 .registry
151 .lock()
152 .expect("failed to obtain lock on registry");
153
154 if let Some(c) = reg.counters.get(&key) {
155 return Counter(Arc::downgrade(c));
156 }
157
158 let c = Arc::new(AtomicUsize::new(0));
159 let counter = Counter(Arc::downgrade(&c));
160 reg.counters.insert(key, c);
161 counter
162 }
163
164 pub fn gauge(&self, name: &'static str) -> Gauge {
166 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
167 let mut reg = self
168 .registry
169 .lock()
170 .expect("failed to obtain lock on registry");
171
172 if let Some(g) = reg.gauges.get(&key) {
173 return Gauge(Arc::downgrade(g));
174 }
175
176 let g = Arc::new(AtomicUsize::new(0));
177 let gauge = Gauge(Arc::downgrade(&g));
178 reg.gauges.insert(key, g);
179 gauge
180 }
181
182 pub fn stat(&self, name: &'static str) -> Stat {
186 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
187 self.mk_stat(key, None)
188 }
189
190 pub fn timer_us(&self, name: &'static str) -> Timer {
191 Timer {
192 stat: self.stat(name),
193 unit: TimeUnit::Micros,
194 }
195 }
196
197 pub fn timer_ms(&self, name: &'static str) -> Timer {
198 Timer {
199 stat: self.stat(name),
200 unit: TimeUnit::Millis,
201 }
202 }
203
204 pub fn stat_with_bounds(&self, name: &'static str, low: u64, high: u64) -> Stat {
206 let key = Key::new(name, self.prefix.clone(), self.labels.clone());
207 self.mk_stat(key, Some((low, high)))
208 }
209
210 fn mk_stat(&self, key: Key, bounds: Option<(u64, u64)>) -> Stat {
211 let mut reg = self
212 .registry
213 .lock()
214 .expect("failed to obtain lock on registry");
215
216 if let Some(h) = reg.stats.get(&key) {
217 let histo = Arc::downgrade(h);
218 return Stat { histo, bounds };
219 }
220
221 let h = Arc::new(Mutex::new(HistogramWithSum::new(bounds)));
222 let histo = Arc::downgrade(&h);
223 reg.stats.insert(key, h);
224 Stat { histo, bounds }
225 }
226}
227
228#[derive(Clone)]
230pub struct Counter(Weak<AtomicUsize>);
231impl Counter {
232 pub fn incr(&self, v: usize) {
233 if let Some(c) = self.0.upgrade() {
234 c.fetch_add(v, Ordering::AcqRel);
235 }
236 }
237}
238
239#[derive(Clone)]
241pub struct Gauge(Weak<AtomicUsize>);
242impl Gauge {
243 pub fn incr(&self, v: usize) {
244 if let Some(g) = self.0.upgrade() {
245 g.fetch_add(v, Ordering::AcqRel);
246 } else {
247 debug!("gauge dropped");
248 }
249 }
250 pub fn decr(&self, v: usize) {
251 if let Some(g) = self.0.upgrade() {
252 g.fetch_sub(v, Ordering::AcqRel);
253 } else {
254 debug!("gauge dropped");
255 }
256 }
257 pub fn set(&self, v: usize) {
258 if let Some(g) = self.0.upgrade() {
259 g.store(v, Ordering::Release);
260 } else {
261 debug!("gauge dropped");
262 }
263 }
264}
265
266const HISTOGRAM_PRECISION: u8 = 4;
268
269#[derive(Clone)]
274pub struct HistogramWithSum {
275 histogram: Histogram<u64>,
276 sum: u64,
277}
278
279impl HistogramWithSum {
280 fn new(bounds: Option<(u64, u64)>) -> Self {
282 let h = match bounds {
283 None => Histogram::<u64>::new(HISTOGRAM_PRECISION),
284 Some((l, h)) => Histogram::<u64>::new_with_bounds(l, h, HISTOGRAM_PRECISION),
285 };
286 let histogram = h.expect("failed to create histogram");
287 HistogramWithSum { histogram, sum: 0 }
288 }
289
290 fn record(&mut self, v: u64) {
292 if let Err(e) = self.histogram.record(v) {
293 error!("failed to add value to histogram: {:?}", e);
294 }
295 if v >= ::std::u64::MAX - self.sum {
296 self.sum = ::std::u64::MAX
297 } else {
298 self.sum += v;
299 }
300 }
301
302 pub fn histogram(&self) -> &Histogram<u64> {
303 &self.histogram
304 }
305 pub fn count(&self) -> u64 {
306 self.histogram.len()
307 }
308 pub fn max(&self) -> u64 {
309 self.histogram.max()
310 }
311 pub fn min(&self) -> u64 {
312 self.histogram.min()
313 }
314 pub fn sum(&self) -> u64 {
315 self.sum
316 }
317
318 pub fn clear(&mut self) {
319 self.histogram.reset();
320 self.sum = 0;
321 }
322}
323
324#[derive(Clone)]
326pub struct Stat {
327 histo: Weak<Mutex<HistogramWithSum>>,
328 bounds: Option<(u64, u64)>,
329}
330
331impl Stat {
332 pub fn add(&self, v: u64) {
333 if let Some(h) = self.histo.upgrade() {
334 let mut histo = h.lock().expect("failed to obtain lock for stat");
335 histo.record(v);
336 }
337 }
338
339 pub fn add_values(&mut self, vs: &[u64]) {
340 if let Some(h) = self.histo.upgrade() {
341 let mut histo = h.lock().expect("failed to obtain lock for stat");
342 for v in vs {
343 histo.record(*v)
344 }
345 }
346 }
347}
348
349#[derive(Clone)]
350pub struct Timer {
351 stat: Stat,
352 unit: TimeUnit,
353}
354#[derive(Copy, Clone)]
355pub enum TimeUnit {
356 Millis,
357 Micros,
358}
359impl Timer {
360 pub fn record_since(&self, t0: Instant) {
361 self.stat.add(to_u64(t0, self.unit));
362 }
363
364 pub fn time<F: Future>(&self, fut: F) -> Timed<impl Future<Output = F::Output>> {
365 let stat = self.stat.clone();
366 let unit = self.unit;
367 let f = futures::future::lazy(|_| {
368 Timing::start()
371 })
372 .then(move |t0| {
373 fut.map(move |v| {
374 stat.add(to_u64(t0, unit));
375 v
376 })
377 });
378 Timed(f)
379 }
380}
381
382fn to_u64(t0: Instant, unit: TimeUnit) -> u64 {
383 match unit {
384 TimeUnit::Millis => t0.elapsed_ms(),
385 TimeUnit::Micros => t0.elapsed_us(),
386 }
387}
388
389#[pin_project]
390pub struct Timed<F>(#[pin] F);
391impl<F: Future> Future for Timed<F> {
392 type Output = F::Output;
393 fn poll(self: Pin<&mut Self>, cx: &mut futures::task::Context) -> Poll<Self::Output> {
394 self.project().0.poll(cx)
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 #[test]
401 fn test_report_peek() {
402 let (metrics, reporter) = super::new();
403 let metrics = metrics.labeled("joy", "painting");
404
405 let happy_accidents = metrics.counter("happy_accidents");
406 let paint_level = metrics.gauge("paint_level");
407 let mut stroke_len = metrics.stat("stroke_len");
408
409 happy_accidents.incr(1);
410 paint_level.set(2);
411 stroke_len.add_values(&[1, 2, 3]);
412
413 {
414 let report = reporter.peek();
415 {
416 let k = report
417 .counters()
418 .keys()
419 .find(|k| k.name() == "happy_accidents")
420 .expect("expected counter: happy_accidents");
421 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
422 assert_eq!(report.counters().get(k), Some(&1));
423 }
424 {
425 let k = report
426 .gauges()
427 .keys()
428 .find(|k| k.name() == "paint_level")
429 .expect("expected gauge: paint_level");
430 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
431 assert_eq!(report.gauges().get(k), Some(&2));
432 }
433 assert_eq!(
434 report.gauges().keys().find(|k| k.name() == "brush_width"),
435 None
436 );
437 {
438 let k = report
439 .stats()
440 .keys()
441 .find(|k| k.name() == "stroke_len")
442 .expect("expected stat: stroke_len");
443 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
444 assert!(report.stats().contains_key(k));
445 }
446 assert_eq!(report.stats().keys().find(|k| k.name() == "tree_len"), None);
447 }
448
449 drop(paint_level);
450 let brush_width = metrics.gauge("brush_width");
451 let mut tree_len = metrics.stat("tree_len");
452
453 happy_accidents.incr(2);
454 brush_width.set(5);
455 stroke_len.add_values(&[1, 2, 3]);
456 tree_len.add_values(&[3, 4, 5]);
457
458 {
459 let report = reporter.peek();
460 {
461 let k = report
462 .counters()
463 .keys()
464 .find(|k| k.name() == "happy_accidents")
465 .expect("expected counter: happy_accidents");
466 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
467 assert_eq!(report.counters().get(k), Some(&3));
468 }
469 {
470 let k = report
471 .gauges()
472 .keys()
473 .find(|k| k.name() == "paint_level")
474 .expect("expected gauge: paint_level");
475 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
476 assert_eq!(report.gauges().get(k), Some(&2));
477 }
478 {
479 let k = report
480 .gauges()
481 .keys()
482 .find(|k| k.name() == "brush_width")
483 .expect("expected gauge: brush_width");
484 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
485 assert_eq!(report.gauges().get(k), Some(&5));
486 }
487 {
488 let k = report
489 .stats()
490 .keys()
491 .find(|k| k.name() == "stroke_len")
492 .expect("expected stat: stroke_len");
493 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
494 assert!(report.stats().contains_key(k));
495 }
496 {
497 let k = report
498 .stats()
499 .keys()
500 .find(|k| k.name() == "tree_len")
501 .expect("expected stat: tree_len");
502 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
503 assert!(report.stats().contains_key(k));
504 }
505 }
506 }
507
508 #[test]
509 fn test_report_take() {
510 let (metrics, mut reporter) = super::new();
511 let metrics = metrics.labeled("joy", "painting");
512
513 let happy_accidents = metrics.counter("happy_accidents");
514 let paint_level = metrics.gauge("paint_level");
515 let mut stroke_len = metrics.stat("stroke_len");
516 happy_accidents.incr(1);
517 paint_level.set(2);
518 stroke_len.add_values(&[1, 2, 3]);
519 {
520 let report = reporter.take();
521 {
522 let k = report
523 .counters()
524 .keys()
525 .find(|k| k.name() == "happy_accidents")
526 .expect("expected counter: happy_accidents");
527 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
528 assert_eq!(report.counters().get(k), Some(&1));
529 }
530 {
531 let k = report
532 .gauges()
533 .keys()
534 .find(|k| k.name() == "paint_level")
535 .expect("expected gauge");
536 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
537 assert_eq!(report.gauges().get(k), Some(&2));
538 }
539 assert_eq!(
540 report.gauges().keys().find(|k| k.name() == "brush_width"),
541 None
542 );
543 {
544 let k = report
545 .stats()
546 .keys()
547 .find(|k| k.name() == "stroke_len")
548 .expect("expected stat");
549 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
550 assert!(report.stats().contains_key(k));
551 }
552 assert_eq!(report.stats().keys().find(|k| k.name() == "tree_len"), None);
553 {
554 let k = report
555 .stats()
556 .keys()
557 .find(|k| k.name() == "stroke_len")
558 .expect("expected stat");
559 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
560 assert!(report.stats().contains_key(k));
561 }
562 }
563
564 drop(paint_level);
565 drop(stroke_len);
566 {
567 let report = reporter.take();
568 {
569 let counters = report.counters();
570 let k = counters
571 .keys()
572 .find(|k| k.name() == "happy_accidents")
573 .expect("expected counter: happy_accidents");
574 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
575 assert_eq!(counters.get(k), Some(&1));
576 }
577 {
578 let k = report
579 .gauges()
580 .keys()
581 .find(|k| k.name() == "paint_level")
582 .expect("expected gauge");
583 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
584 assert_eq!(report.gauges().get(k), Some(&2));
585 }
586 {
587 let k = report
588 .stats()
589 .keys()
590 .find(|k| k.name() == "stroke_len")
591 .expect("expected stat");
592 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
593 assert!(report.stats().contains_key(k));
594 }
595 }
596
597 let brush_width = metrics.gauge("brush_width");
598 let mut tree_len = metrics.stat("tree_len");
599 happy_accidents.incr(2);
600 brush_width.set(5);
601 tree_len.add_values(&[3, 4, 5]);
602 {
603 let report = reporter.take();
604 {
605 let k = report
606 .counters()
607 .keys()
608 .find(|k| k.name() == "happy_accidents")
609 .expect("expected counter: happy_accidents");
610 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
611 assert_eq!(report.counters().get(k), Some(&3));
612 }
613 assert_eq!(
614 report.gauges().keys().find(|k| k.name() == "paint_level"),
615 None
616 );
617 {
618 let k = report
619 .gauges()
620 .keys()
621 .find(|k| k.name() == "brush_width")
622 .expect("expected gauge");
623 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
624 assert_eq!(report.gauges().get(k), Some(&5));
625 }
626 assert_eq!(
627 report.stats().keys().find(|k| k.name() == "stroke_len"),
628 None
629 );
630 {
631 let k = report
632 .stats()
633 .keys()
634 .find(|k| k.name() == "tree_len")
635 .expect("expeced stat");
636 assert_eq!(k.labels.get("joy"), Some(&"painting".to_string()));
637 assert!(report.stats().contains_key(k));
638 }
639 }
640 }
641}