Skip to content

Commit c08728c

Browse files
committed
Add Summary metric type
1 parent 69e6674 commit c08728c

File tree

5 files changed

+123
-2
lines changed

5 files changed

+123
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dtoa = "1.0"
1818
itoa = "1.0"
1919
owning_ref = "0.4"
2020
prometheus-client-derive-text-encode = { version = "0.3.0", path = "derive-text-encode" }
21+
quantiles = "0.7.1"
2122

2223
[dev-dependencies]
2324
async-std = { version = "1", features = ["attributes"] }

src/encoding/text.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ impl Encode for MetricType {
184184
MetricType::Histogram => "histogram",
185185
MetricType::Info => "info",
186186
MetricType::Unknown => "unknown",
187+
MetricType::Summary => "summary",
187188
};
188189

189190
writer.write_all(t.as_bytes())?;
@@ -603,6 +604,7 @@ where
603604
}
604605
}
605606

607+
606608
#[cfg(test)]
607609
mod tests {
608610
use super::*;

src/metrics.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod exemplar;
55
pub mod family;
66
pub mod gauge;
77
pub mod histogram;
8+
pub mod summary;
89
pub mod info;
910

1011
/// A metric that is aware of its Open Metrics metric type.
@@ -19,9 +20,9 @@ pub enum MetricType {
1920
Histogram,
2021
Info,
2122
Unknown,
23+
Summary,
2224
// Not (yet) supported metric types.
2325
//
2426
// GaugeHistogram,
2527
// StateSet,
26-
// Summary
2728
}

src/metrics/counter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ mod tests {
185185
// Map infinite, subnormal and NaN to 0.0.
186186
.map(|f| if f.is_normal() { f } else { 0.0 })
187187
.collect();
188-
let sum = fs.iter().sum();
188+
let sum: f64 = fs.iter().sum();
189189
let counter = Counter::<f64, AtomicU64>::default();
190190
for f in fs {
191191
counter.inc_by(f);

src/metrics/summary.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! Module implementing an Open Metrics histogram.
2+
//!
3+
//! See [`Summary`] for details.
4+
5+
use super::{MetricType, TypedMetric};
6+
//use owning_ref::OwningRef;
7+
//use std::iter::{self, once};
8+
use std::sync::{Arc, Mutex};
9+
10+
use quantiles::ckms::CKMS;
11+
12+
/// Open Metrics [`Summary`] to measure distributions of discrete events.
13+
pub struct Summary {
14+
target_quantile: Vec<f64>,
15+
target_error: f64,
16+
max_age_buckets: u64,
17+
max_age_seconds: u64,
18+
inner: Arc<Mutex<InnerSummary>>,
19+
}
20+
21+
impl Clone for Summary {
22+
fn clone(&self) -> Self {
23+
Summary {
24+
target_quantile: self.target_quantile.clone(),
25+
target_error: self.target_error,
26+
max_age_buckets: self.max_age_buckets,
27+
max_age_seconds: self.max_age_seconds,
28+
inner: self.inner.clone(),
29+
}
30+
}
31+
}
32+
33+
pub(crate) struct InnerSummary {
34+
sum: f64,
35+
count: u64,
36+
quantile_streams: Vec<CKMS<f64>>,
37+
// head_stream is like a cursor which carries the index
38+
// of the stream in the quantile_streams that we want to query
39+
head_stream: u64,
40+
}
41+
42+
impl Summary {
43+
pub fn new(max_age_buckets: u64, max_age_seconds: u64, target_quantile: Vec<f64>, target_error: f64) -> Self {
44+
let mut streams: Vec<CKMS<f64>> = Vec::new();
45+
for _ in 0..max_age_buckets {
46+
streams.push(CKMS::new(target_error));
47+
}
48+
49+
Summary{
50+
max_age_buckets,
51+
max_age_seconds,
52+
target_quantile,
53+
target_error,
54+
inner: Arc::new(Mutex::new(InnerSummary {
55+
sum: Default::default(),
56+
count: Default::default(),
57+
quantile_streams: streams,
58+
head_stream: 0,
59+
}))
60+
}
61+
}
62+
63+
pub fn observe(&mut self, v: f64) {
64+
let mut inner = self.inner.lock().unwrap();
65+
inner.sum += v;
66+
inner.count += 1;
67+
68+
// insert quantiles into all streams/buckets.
69+
for stream in inner.quantile_streams.iter_mut() {
70+
stream.insert(v);
71+
}
72+
}
73+
74+
pub fn get(&self) -> (f64, u64, Vec<(f64, f64)>) {
75+
let inner = self.inner.lock().unwrap();
76+
let sum = inner.sum;
77+
let count = inner.count;
78+
let head = inner.head_stream;
79+
let mut quantile_values: Vec<(f64, f64)> = Vec::new();
80+
81+
// TODO: add stream rotation
82+
for q in self.target_quantile.iter() {
83+
match inner.quantile_streams[head as usize].query(*q) {
84+
Some((_, v)) => quantile_values.push((*q, v)),
85+
None => continue, // TODO fix this
86+
};
87+
}
88+
(sum, count, quantile_values)
89+
}
90+
}
91+
92+
// TODO: should this type impl Default like Counter?
93+
94+
impl TypedMetric for Summary {
95+
const TYPE: MetricType = MetricType::Summary;
96+
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use super::*;
101+
102+
#[test]
103+
fn basic() {
104+
let mut summary = Summary::new(5, 10, vec![0.5, 0.9, 0.99], 0.01);
105+
summary.observe(5.0);
106+
summary.observe(15.0);
107+
summary.observe(25.0);
108+
109+
let (s, c, q) = summary.get();
110+
assert_eq!(45.0, s);
111+
assert_eq!(3, c);
112+
113+
for elem in q.iter() {
114+
println!("Vec<{}, {}>", elem.0, elem.1);
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)