1use super::Statistic;
2use crate::{
3 Distribution, TimeStatistic, intern,
4 stats::{ToSnakeCase, defaults},
5};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::{fmt::Debug, time::Duration};
9
10#[macro_export]
11macro_rules! metric {
12 ($name:expr, $update:expr) => {{
13 let mut metric = $crate::Metric::new($name);
14 metric.apply_update($update);
15 metric
16 }};
17 ($scope:expr, $name:expr, $value:expr) => {{ $crate::Metric::new_scoped($name, $scope).upsert($value) }};
18 ($name:expr) => {{ $crate::Metric::new($name).upsert(1) }};
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23pub enum MetricScope {
24 #[default]
25 Generation,
26 Lifetime,
27 Step,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32pub enum Rollup {
33 #[default]
34 Sum,
35 Mean,
36 Last,
37 Min,
38 Max,
39}
40
41#[derive(Clone, PartialEq, Default)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43pub struct MetricInner {
44 pub(crate) value_statistic: Option<Statistic>,
45 pub(crate) time_statistic: Option<TimeStatistic>,
46 pub(crate) distribution: Option<Distribution>,
47}
48
49#[derive(Clone, PartialEq, Default)]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51pub struct Metric {
52 pub(super) name: &'static str,
53 pub(super) inner: MetricInner,
54 pub(super) scope: MetricScope,
55 pub(super) rollup: Rollup,
56}
57
58impl Metric {
59 pub fn new(name: &'static str) -> Self {
60 let name = intern!(name.to_snake_case());
61 let scope = defaults::default_scope(name);
62 let rollup = defaults::default_rollup(name);
63 Self {
64 name,
65 inner: MetricInner {
66 value_statistic: None,
67 time_statistic: None,
68 distribution: None,
69 },
70 scope,
71 rollup,
72 }
73 }
74
75 pub fn new_scoped(name: &'static str, scope: MetricScope) -> Self {
76 let rollup = defaults::default_rollup(name);
77 Self {
78 scope,
79 rollup,
80 ..Self::new(intern!(name.to_snake_case()))
81 }
82 }
83
84 pub fn with_rollup(mut self, rollup: Rollup) -> Self {
85 self.rollup = rollup;
86 self
87 }
88
89 pub fn scope(&self) -> MetricScope {
90 self.scope
91 }
92 pub fn rollup(&self) -> Rollup {
93 self.rollup
94 }
95
96 pub fn clear_values(&mut self) {
97 self.inner = MetricInner::default();
98 }
99
100 pub fn inner(&self) -> &MetricInner {
101 &self.inner
102 }
103
104 #[inline(always)]
105 pub fn upsert<'a>(mut self, update: impl Into<MetricUpdate<'a>>) -> Self {
106 self.apply_update(update);
107 self
108 }
109
110 pub fn update_from(&mut self, other: &Metric) {
111 if let Some(stat) = &other.inner.value_statistic {
112 let v = (stat.last_value(), stat.min(), stat.max(), stat.mean());
113 match self.rollup() {
114 Rollup::Sum => self.apply_update(stat.sum()),
115 Rollup::Mean => self.apply_update(v.3),
116 Rollup::Last => self.apply_update(v.0),
117 Rollup::Min => self.apply_update(v.1),
118 Rollup::Max => self.apply_update(v.2),
119 }
120 }
121
122 if let Some(time) = &other.inner.time_statistic {
123 let t = (
124 time.last_time(),
125 time.min(),
126 time.max(),
127 time.mean(),
128 time.sum(),
129 );
130 match self.rollup() {
131 Rollup::Sum => self.apply_update(t.4),
132 Rollup::Mean => self.apply_update(t.3),
133 Rollup::Last => self.apply_update(t.0),
134 Rollup::Min => self.apply_update(t.1),
135 Rollup::Max => self.apply_update(t.2),
136 }
137 }
138
139 if let Some(d) = &other.inner.distribution {
141 self.apply_update(d.last_sequence().as_slice());
142 }
143 }
144
145 #[inline(always)]
146 pub fn apply_update<'a>(&mut self, update: impl Into<MetricUpdate<'a>>) {
147 let update = update.into();
148 match update {
149 MetricUpdate::Float(value) => {
150 if let Some(stat) = &mut self.inner.value_statistic {
151 stat.add(value);
152 } else {
153 self.inner.value_statistic = Some(Statistic::from(value));
154 }
155 }
156 MetricUpdate::Usize(value) => {
157 if let Some(stat) = &mut self.inner.value_statistic {
158 stat.add(value as f32);
159 } else {
160 self.inner.value_statistic = Some(Statistic::from(value as f32));
161 }
162 }
163 MetricUpdate::Duration(value) => {
164 if let Some(stat) = &mut self.inner.time_statistic {
165 stat.add(value);
166 } else {
167 self.inner.time_statistic = Some(TimeStatistic::from(value));
168 }
169 }
170 MetricUpdate::Distribution(values) => {
171 if let Some(stat) = &mut self.inner.distribution {
172 stat.add(values);
173 } else {
174 self.inner.distribution = Some(Distribution::from(values));
175 }
176 }
177 MetricUpdate::FloatOperation(value, time) => {
178 if let Some(stat) = &mut self.inner.value_statistic {
179 stat.add(value);
180 } else {
181 self.inner.value_statistic = Some(Statistic::from(value));
182 }
183
184 if let Some(time_stat) = &mut self.inner.time_statistic {
185 time_stat.add(time);
186 } else {
187 self.inner.time_statistic = Some(TimeStatistic::from(time));
188 }
189 }
190 MetricUpdate::UsizeOperation(value, time) => {
191 if let Some(stat) = &mut self.inner.value_statistic {
192 stat.add(value as f32);
193 } else {
194 self.inner.value_statistic = Some(Statistic::from(value as f32));
195 }
196
197 if let Some(time_stat) = &mut self.inner.time_statistic {
198 time_stat.add(time);
199 } else {
200 self.inner.time_statistic = Some(TimeStatistic::from(time));
201 }
202 }
203 MetricUpdate::DistributionRef(values) => {
204 if let Some(stat) = &mut self.inner.distribution {
205 stat.add(values);
206 } else {
207 self.inner.distribution = Some(Distribution::from(values.as_slice()));
208 }
209 }
210 MetricUpdate::DistributionOwned(values) => {
211 if let Some(stat) = &mut self.inner.distribution {
212 stat.add(&values);
213 } else {
214 self.inner.distribution = Some(Distribution::from(values.as_slice()));
215 }
216 }
217 }
218 }
219
220 pub fn name(&self) -> &'static str {
224 self.name
225 }
226
227 pub fn last_value(&self) -> f32 {
228 self.inner
229 .value_statistic
230 .as_ref()
231 .map_or(0.0, |stat| stat.last_value())
232 }
233
234 pub fn distribution(&self) -> Option<&Distribution> {
235 self.inner.distribution.as_ref()
236 }
237
238 pub fn statistic(&self) -> Option<&Statistic> {
239 self.inner.value_statistic.as_ref()
240 }
241
242 pub fn time_statistic(&self) -> Option<&TimeStatistic> {
243 self.inner.time_statistic.as_ref()
244 }
245
246 pub fn last_time(&self) -> Duration {
247 self.time_statistic()
248 .map_or(Duration::ZERO, |stat| stat.last_time())
249 }
250
251 pub fn count(&self) -> i32 {
252 self.statistic().map(|stat| stat.count()).unwrap_or(0)
253 }
254
255 pub fn value_mean(&self) -> Option<f32> {
259 self.statistic().map(|stat| stat.mean())
260 }
261
262 pub fn value_variance(&self) -> Option<f32> {
263 self.statistic().map(|stat| stat.variance())
264 }
265
266 pub fn value_std_dev(&self) -> Option<f32> {
267 self.statistic().map(|stat| stat.std_dev())
268 }
269
270 pub fn value_skewness(&self) -> Option<f32> {
271 self.statistic().map(|stat| stat.skewness())
272 }
273
274 pub fn value_min(&self) -> Option<f32> {
275 self.statistic().map(|stat| stat.min())
276 }
277
278 pub fn value_max(&self) -> Option<f32> {
279 self.statistic().map(|stat| stat.max())
280 }
281
282 pub fn time_mean(&self) -> Option<Duration> {
286 self.time_statistic().map(|stat| stat.mean())
287 }
288
289 pub fn time_variance(&self) -> Option<Duration> {
290 self.time_statistic().map(|stat| stat.variance())
291 }
292
293 pub fn time_std_dev(&self) -> Option<Duration> {
294 self.time_statistic().map(|stat| stat.standard_deviation())
295 }
296
297 pub fn time_min(&self) -> Option<Duration> {
298 self.time_statistic().map(|stat| stat.min())
299 }
300
301 pub fn time_max(&self) -> Option<Duration> {
302 self.time_statistic().map(|stat| stat.max())
303 }
304
305 pub fn time_sum(&self) -> Option<Duration> {
306 self.time_statistic().map(|stat| stat.sum())
307 }
308
309 pub fn last_sequence(&self) -> Option<&Vec<f32>> {
313 self.distribution().map(|dist| dist.last_sequence())
314 }
315
316 pub fn distribution_mean(&self) -> Option<f32> {
317 self.distribution().map(|dist| dist.mean())
318 }
319
320 pub fn distribution_variance(&self) -> Option<f32> {
321 self.distribution().map(|dist| dist.variance())
322 }
323
324 pub fn distribution_std_dev(&self) -> Option<f32> {
325 self.distribution().map(|dist| dist.standard_deviation())
326 }
327
328 pub fn distribution_skewness(&self) -> Option<f32> {
329 self.distribution().map(|dist| dist.skewness())
330 }
331
332 pub fn distribution_kurtosis(&self) -> Option<f32> {
333 self.distribution().map(|dist| dist.kurtosis())
334 }
335
336 pub fn distribution_min(&self) -> Option<f32> {
337 self.distribution().map(|dist| dist.min())
338 }
339
340 pub fn distribution_max(&self) -> Option<f32> {
341 self.distribution().map(|dist| dist.max())
342 }
343
344 pub fn distribution_entropy(&self) -> Option<f32> {
345 self.distribution().map(|dist| dist.entropy())
346 }
347}
348
349#[derive(Debug, Clone, PartialEq)]
350pub enum MetricUpdate<'a> {
351 Float(f32),
352 Usize(usize),
353 Duration(Duration),
354 Distribution(&'a [f32]),
355 DistributionRef(&'a Vec<f32>),
356 DistributionOwned(Vec<f32>),
357 FloatOperation(f32, Duration),
358 UsizeOperation(usize, Duration),
359}
360
361impl From<f32> for MetricUpdate<'_> {
362 fn from(value: f32) -> Self {
363 MetricUpdate::Float(value)
364 }
365}
366
367impl From<usize> for MetricUpdate<'_> {
368 fn from(value: usize) -> Self {
369 MetricUpdate::Usize(value)
370 }
371}
372
373impl From<Duration> for MetricUpdate<'_> {
374 fn from(value: Duration) -> Self {
375 MetricUpdate::Duration(value)
376 }
377}
378
379impl<'a> From<&'a [f32]> for MetricUpdate<'a> {
380 fn from(value: &'a [f32]) -> Self {
381 MetricUpdate::Distribution(value)
382 }
383}
384
385impl From<(f32, Duration)> for MetricUpdate<'_> {
386 fn from(value: (f32, Duration)) -> Self {
387 MetricUpdate::FloatOperation(value.0, value.1)
388 }
389}
390
391impl From<(usize, Duration)> for MetricUpdate<'_> {
392 fn from(value: (usize, Duration)) -> Self {
393 MetricUpdate::UsizeOperation(value.0, value.1)
394 }
395}
396
397impl<'a> From<&'a Vec<f32>> for MetricUpdate<'a> {
398 fn from(value: &'a Vec<f32>) -> Self {
399 MetricUpdate::DistributionRef(value)
400 }
401}
402
403impl From<Vec<f32>> for MetricUpdate<'_> {
404 fn from(value: Vec<f32>) -> Self {
405 MetricUpdate::DistributionOwned(value)
406 }
407}
408
409impl std::fmt::Debug for Metric {
410 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411 write!(f, "Metric {{ name: {}, }}", self.name)
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418
419 #[test]
420 fn test_metric() {
421 let mut metric = Metric::new("test");
422 metric.apply_update(1.0);
423 metric.apply_update(2.0);
424 metric.apply_update(3.0);
425 metric.apply_update(4.0);
426 metric.apply_update(5.0);
427
428 assert_eq!(metric.count(), 5);
429 assert_eq!(metric.last_value(), 5.0);
430 assert_eq!(metric.value_mean().unwrap(), 3.0);
431 assert_eq!(metric.value_variance().unwrap(), 2.5);
432 assert_eq!(metric.value_std_dev().unwrap(), 1.5811388);
433 assert_eq!(metric.value_min().unwrap(), 1.0);
434 assert_eq!(metric.value_max().unwrap(), 5.0);
435 assert_eq!(metric.name(), "test");
436 }
437
438 #[test]
439 fn test_metric_labels() {
440 let mut metric = Metric::new("test");
441
442 metric.apply_update(1.0);
443 metric.apply_update(2.0);
444 metric.apply_update(3.0);
445 metric.apply_update(4.0);
446 metric.apply_update(5.0);
447
448 assert_eq!(metric.count(), 5);
449 assert_eq!(metric.last_value(), 5.0);
450 assert_eq!(metric.value_mean().unwrap(), 3.0);
451 assert_eq!(metric.value_variance().unwrap(), 2.5);
452 assert_eq!(metric.value_std_dev().unwrap(), 1.5811388);
453 assert_eq!(metric.value_min().unwrap(), 1.0);
454 assert_eq!(metric.value_max().unwrap(), 5.0);
455 }
456}