radiate_core/stats/
metrics.rs1use super::Statistic;
2use crate::{
3 TimeStatistic,
4 stats::{Tag, TagKind, defaults},
5};
6use radiate_utils::{ToSnakeCase, cache_string, intern, intern_snake_case};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{sync::Arc, time::Duration};
10
11#[macro_export]
12macro_rules! metric {
13 ($name:expr, $update:expr) => {{
14 let mut metric = $crate::Metric::new($name);
15 metric.apply_update($update);
16 metric
17 }};
18 ($name:expr) => {{ $crate::Metric::new($name).upsert(1) }};
19}
20
21#[derive(Clone, PartialEq, Default)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23pub struct MetricInner {
24 pub(crate) value_statistic: Option<Statistic>,
25 pub(crate) time_statistic: Option<TimeStatistic>,
26}
27
28#[derive(Clone, PartialEq, Default)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30pub struct Metric {
31 pub(super) name: Arc<String>,
32 pub(super) inner: MetricInner,
33 pub(super) tags: Tag,
34}
35
36impl Metric {
37 pub fn new(name: &'static str) -> Self {
38 let name = cache_string!(intern_snake_case!(name));
39 let tags = defaults::default_tags(&name);
40
41 Self {
42 name,
43 inner: MetricInner {
44 value_statistic: None,
45 time_statistic: None,
46 },
47 tags,
48 }
49 }
50
51 #[inline(always)]
52 pub fn tags(&self) -> Tag {
53 self.tags
54 }
55
56 #[inline(always)]
57 pub fn with_tag(mut self, tag: TagKind) -> Self {
58 self.add_tag(tag);
59 self
60 }
61
62 #[inline(always)]
63 pub fn with_tags<T>(&mut self, tags: T)
64 where
65 T: Into<Tag>,
66 {
67 self.tags = tags.into();
68 }
69
70 #[inline(always)]
71 pub fn add_tag(&mut self, tag: TagKind) {
72 self.tags.insert(tag);
73 }
74
75 pub fn contains_tag(&self, tag: &TagKind) -> bool {
76 self.tags.has(*tag)
77 }
78
79 pub fn tags_iter(&self) -> impl Iterator<Item = TagKind> {
80 self.tags.iter()
81 }
82
83 pub fn clear_values(&mut self) {
84 self.inner = MetricInner::default();
85 }
86
87 #[inline(always)]
88 pub fn upsert<'a>(mut self, update: impl Into<MetricUpdate<'a>>) -> Self {
89 self.apply_update(update);
90 self
91 }
92
93 #[inline(always)]
94 pub fn update_from(&mut self, other: Metric) {
95 if let Some(stat) = other.inner.value_statistic {
96 if stat.count() as f32 == stat.sum() && !other.tags.has(TagKind::Distribution) {
99 self.apply_update(stat.sum());
100 } else {
101 self.apply_update(stat);
102 }
103 }
104
105 if let Some(time) = other.inner.time_statistic {
106 self.apply_update(time);
107 }
108
109 self.tags = self.tags.union(other.tags);
110 }
111
112 #[inline(always)]
113 pub fn apply_update<'a>(&mut self, update: impl Into<MetricUpdate<'a>>) {
114 let update = update.into();
115 match update {
116 MetricUpdate::Float(value) => {
117 self.update_statistic(value);
118 }
119 MetricUpdate::Usize(value) => {
120 self.update_statistic(value as f32);
121 }
122 MetricUpdate::Duration(value) => {
123 self.update_time_statistic(value);
124 }
125 MetricUpdate::FloatOperation(value, time) => {
126 self.update_statistic(value);
127 self.update_time_statistic(time);
128 }
129 MetricUpdate::UsizeOperation(value, time) => {
130 self.update_statistic(value as f32);
131 self.update_time_statistic(time);
132 }
133 MetricUpdate::UsizeDistribution(values) => {
134 self.update_statistic_from_iter(values.iter().map(|v| *v as f32));
135 }
136 MetricUpdate::Distribution(values) => {
137 self.update_statistic_from_iter(values.iter().cloned());
138 }
139 MetricUpdate::Statistic(stat) => {
140 if let Some(existing_stat) = &mut self.inner.value_statistic {
141 existing_stat.merge(&stat);
142 } else {
143 self.new_statistic(stat);
144 }
145 }
146 MetricUpdate::TimeStatistic(time_stat) => {
147 if let Some(existing_time_stat) = &mut self.inner.time_statistic {
148 existing_time_stat.merge(&time_stat);
149 } else {
150 self.new_time_statistic(time_stat);
151 }
152 }
153 }
154 }
155
156 pub fn new_statistic(&mut self, value: impl Into<Statistic>) {
157 self.inner.value_statistic = Some(value.into());
158 self.add_tag(TagKind::Statistic);
159 }
160
161 pub fn new_time_statistic(&mut self, value: impl Into<TimeStatistic>) {
162 self.inner.time_statistic = Some(value.into());
163 self.add_tag(TagKind::Time);
164 }
165
166 fn update_statistic(&mut self, value: f32) {
167 if let Some(stat) = &mut self.inner.value_statistic {
168 stat.add(value);
169 } else {
170 self.new_statistic(value);
171 }
172 }
173
174 fn update_time_statistic(&mut self, value: Duration) {
175 if let Some(stat) = &mut self.inner.time_statistic {
176 stat.add(value);
177 } else {
178 self.new_time_statistic(value);
179 }
180 }
181
182 fn update_statistic_from_iter<I>(&mut self, values: I)
183 where
184 I: IntoIterator<Item = f32>,
185 {
186 if let Some(stat) = &mut self.inner.value_statistic {
187 for value in values {
188 stat.add(value);
189 }
190 } else {
191 let mut new_stat = Statistic::default();
192 for value in values {
193 new_stat.add(value);
194 }
195
196 self.new_statistic(new_stat);
197 self.add_tag(TagKind::Distribution);
198 }
199 }
200
201 pub fn name(&self) -> &str {
205 &self.name
206 }
207
208 pub fn last_value(&self) -> f32 {
209 self.inner
210 .value_statistic
211 .as_ref()
212 .map_or(0.0, |stat| stat.last_value())
213 }
214
215 pub fn statistic(&self) -> Option<&Statistic> {
216 self.inner.value_statistic.as_ref()
217 }
218
219 pub fn time_statistic(&self) -> Option<&TimeStatistic> {
220 self.inner.time_statistic.as_ref()
221 }
222
223 pub fn last_time(&self) -> Duration {
224 self.time_statistic()
225 .map_or(Duration::ZERO, |stat| stat.last_time())
226 }
227
228 pub fn count(&self) -> i32 {
229 if let Some(stat) = &self.inner.value_statistic {
230 return stat.count();
231 } else if let Some(stat) = &self.inner.time_statistic {
232 return stat.count();
233 }
234
235 0
237 }
238
239 pub fn value_mean(&self) -> Option<f32> {
243 self.statistic().map(|stat| stat.mean())
244 }
245
246 pub fn value_variance(&self) -> Option<f32> {
247 self.statistic().map(|stat| stat.variance())
248 }
249
250 pub fn value_std_dev(&self) -> Option<f32> {
251 self.statistic().map(|stat| stat.std_dev())
252 }
253
254 pub fn value_skewness(&self) -> Option<f32> {
255 self.statistic().map(|stat| stat.skewness())
256 }
257
258 pub fn value_min(&self) -> Option<f32> {
259 self.statistic().map(|stat| stat.min())
260 }
261
262 pub fn value_max(&self) -> Option<f32> {
263 self.statistic().map(|stat| stat.max())
264 }
265
266 pub fn value_sum(&self) -> Option<f32> {
267 self.statistic().map(|stat| stat.sum())
268 }
269
270 pub fn value_count(&self) -> Option<i32> {
271 self.statistic().map(|stat| stat.count())
272 }
273
274 pub fn time_mean(&self) -> Option<Duration> {
278 self.time_statistic().map(|stat| stat.mean())
279 }
280
281 pub fn time_variance(&self) -> Option<Duration> {
282 self.time_statistic().map(|stat| stat.variance())
283 }
284
285 pub fn time_std_dev(&self) -> Option<Duration> {
286 self.time_statistic().map(|stat| stat.standard_deviation())
287 }
288
289 pub fn time_min(&self) -> Option<Duration> {
290 self.time_statistic().map(|stat| stat.min())
291 }
292
293 pub fn time_max(&self) -> Option<Duration> {
294 self.time_statistic().map(|stat| stat.max())
295 }
296
297 pub fn time_sum(&self) -> Option<Duration> {
298 self.time_statistic().map(|stat| stat.sum())
299 }
300}
301
302#[derive(Clone, PartialEq)]
303pub enum MetricUpdate<'a> {
304 Float(f32),
305 Usize(usize),
306 Duration(Duration),
307 FloatOperation(f32, Duration),
308 UsizeOperation(usize, Duration),
309 Distribution(&'a [f32]),
310 UsizeDistribution(&'a [usize]),
311 Statistic(Statistic),
312 TimeStatistic(TimeStatistic),
313}
314
315impl From<f32> for MetricUpdate<'_> {
316 fn from(value: f32) -> Self {
317 MetricUpdate::Float(value)
318 }
319}
320
321impl From<usize> for MetricUpdate<'_> {
322 fn from(value: usize) -> Self {
323 MetricUpdate::Usize(value)
324 }
325}
326
327impl From<Duration> for MetricUpdate<'_> {
328 fn from(value: Duration) -> Self {
329 MetricUpdate::Duration(value)
330 }
331}
332
333impl<'a> From<&'a [f32]> for MetricUpdate<'a> {
334 fn from(value: &'a [f32]) -> Self {
335 MetricUpdate::Distribution(value)
336 }
337}
338
339impl From<(f32, Duration)> for MetricUpdate<'_> {
340 fn from(value: (f32, Duration)) -> Self {
341 MetricUpdate::FloatOperation(value.0, value.1)
342 }
343}
344
345impl From<(usize, Duration)> for MetricUpdate<'_> {
346 fn from(value: (usize, Duration)) -> Self {
347 MetricUpdate::UsizeOperation(value.0, value.1)
348 }
349}
350
351impl<'a> From<&'a Vec<f32>> for MetricUpdate<'a> {
352 fn from(value: &'a Vec<f32>) -> Self {
353 MetricUpdate::Distribution(value)
354 }
355}
356
357impl<'a> From<&'a Vec<usize>> for MetricUpdate<'a> {
358 fn from(value: &'a Vec<usize>) -> Self {
359 MetricUpdate::UsizeDistribution(value)
360 }
361}
362
363impl From<Statistic> for MetricUpdate<'_> {
364 fn from(value: Statistic) -> Self {
365 MetricUpdate::Statistic(value)
366 }
367}
368
369impl From<TimeStatistic> for MetricUpdate<'_> {
370 fn from(value: TimeStatistic) -> Self {
371 MetricUpdate::TimeStatistic(value)
372 }
373}
374
375impl std::fmt::Debug for Metric {
376 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377 write!(f, "Metric {{ name: {}, }}", self.name)
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn test_metric() {
387 let mut metric = Metric::new("test");
388 metric.apply_update(1.0);
389 metric.apply_update(2.0);
390 metric.apply_update(3.0);
391 metric.apply_update(4.0);
392 metric.apply_update(5.0);
393
394 assert_eq!(metric.count(), 5);
395 assert_eq!(metric.last_value(), 5.0);
396 assert_eq!(metric.value_mean().unwrap(), 3.0);
397 assert_eq!(metric.value_variance().unwrap(), 2.5);
398 assert_eq!(metric.value_std_dev().unwrap(), 1.5811388);
399 assert_eq!(metric.value_min().unwrap(), 1.0);
400 assert_eq!(metric.value_max().unwrap(), 5.0);
401 assert_eq!(metric.name(), "test");
402 }
403
404 #[test]
405 fn test_metric_labels() {
406 let mut metric = Metric::new("test");
407
408 metric.apply_update(1.0);
409 metric.apply_update(2.0);
410 metric.apply_update(3.0);
411 metric.apply_update(4.0);
412 metric.apply_update(5.0);
413
414 assert_eq!(metric.count(), 5);
415 assert_eq!(metric.last_value(), 5.0);
416 assert_eq!(metric.value_mean().unwrap(), 3.0);
417 assert_eq!(metric.value_variance().unwrap(), 2.5);
418 assert_eq!(metric.value_std_dev().unwrap(), 1.5811388);
419 assert_eq!(metric.value_min().unwrap(), 1.0);
420 assert_eq!(metric.value_max().unwrap(), 5.0);
421 }
422}