witchcraft_metrics/
timer.rs

1// Copyright 2019 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use crate::{Clock, ExponentiallyDecayingReservoir, Meter, Reservoir, Snapshot};
15use std::sync::Arc;
16use std::time::{Duration, Instant};
17
18/// A metric tracking the duration and rate of events.
19///
20/// The timer's default reservoir implementation (used by its [`Default`] implementation) is the
21/// [`ExponentiallyDecayingReservoir`].
22pub struct Timer {
23    meter: Meter,
24    reservoir: Box<dyn Reservoir>,
25    clock: Arc<dyn Clock>,
26}
27
28impl Default for Timer {
29    #[inline]
30    fn default() -> Timer {
31        Timer::new(ExponentiallyDecayingReservoir::new())
32    }
33}
34
35impl Timer {
36    /// Creates a new timer.
37    pub fn new<R>(reservoir: R) -> Timer
38    where
39        R: Reservoir,
40    {
41        Timer {
42            meter: Meter::new(),
43            reservoir: Box::new(reservoir),
44            clock: crate::SYSTEM_CLOCK.clone(),
45        }
46    }
47
48    /// Creates a new timer using the provided [`Clock`] as its time source.
49    pub fn new_with<R>(reservoir: R, clock: Arc<dyn Clock>) -> Self
50    where
51        R: Reservoir,
52    {
53        Timer {
54            meter: Meter::new_with(clock.clone()),
55            reservoir: Box::new(reservoir),
56            clock,
57        }
58    }
59
60    /// Adds a new timed event to the metric.
61    #[inline]
62    pub fn update(&self, duration: Duration) {
63        self.meter.mark(1);
64        let nanos = duration.as_nanos() as i64;
65        self.reservoir.update(nanos);
66    }
67
68    /// Returns a guard type which reports the time elapsed since its creation when it drops.
69    #[inline]
70    pub fn time(&self) -> Time<'_> {
71        Time {
72            timer: self,
73            start: self.clock.now(),
74        }
75    }
76
77    /// Returns the number of events reported to the metric.
78    #[inline]
79    pub fn count(&self) -> i64 {
80        self.meter.count()
81    }
82
83    /// Returns the one minute rolling average rate of the occurrence of events measured in events per second.
84    #[inline]
85    pub fn one_minute_rate(&self) -> f64 {
86        self.meter.one_minute_rate()
87    }
88
89    /// Returns the five minute rolling average rate of the occurrence of events measured in events per second.
90    #[inline]
91    pub fn five_minute_rate(&self) -> f64 {
92        self.meter.five_minute_rate()
93    }
94
95    /// Returns the fifteen minute rolling average rate of the occurrence of events measured in events per second.
96    #[inline]
97    pub fn fifteen_minute_rate(&self) -> f64 {
98        self.meter.fifteen_minute_rate()
99    }
100
101    /// Returns the mean rate of the occurrence of events since the creation of the timer measured in events per second.
102    #[inline]
103    pub fn mean_rate(&self) -> f64 {
104        self.meter.mean_rate()
105    }
106
107    /// Returns a snapshot of the statistical distribution of durations of events, measured in nanoseconds.
108    #[inline]
109    pub fn snapshot(&self) -> Box<dyn Snapshot> {
110        self.reservoir.snapshot()
111    }
112}
113
114/// A guard type which reports the time elapsed since its creation to a timer when it drops.
115pub struct Time<'a> {
116    timer: &'a Timer,
117    start: Instant,
118}
119
120impl Drop for Time<'_> {
121    #[inline]
122    fn drop(&mut self) {
123        self.timer.update(self.timer.clock.now() - self.start);
124    }
125}
126
127#[cfg(test)]
128mod test {
129    use crate::Timer;
130    use std::thread;
131    use std::time::Duration;
132
133    #[test]
134    #[allow(clippy::float_cmp)]
135    fn basic() {
136        let timer = Timer::default();
137
138        for _ in 0..15 {
139            timer.update(Duration::from_nanos(0));
140        }
141
142        for _ in 0..5 {
143            timer.update(Duration::from_nanos(5));
144        }
145
146        assert_eq!(timer.count(), 20);
147        assert!(timer.mean_rate() > 0.);
148        assert_eq!(timer.snapshot().value(0.8), 5.)
149    }
150
151    #[test]
152    fn time() {
153        let timer = Timer::default();
154
155        let guard = timer.time();
156        thread::sleep(Duration::from_millis(10));
157        drop(guard);
158
159        assert_eq!(timer.count(), 1);
160        assert!(timer.snapshot().max() >= 10_000_000);
161    }
162}