Skip to content

Commit bc6f1be

Browse files
committed
feat: add summary metrics type
Reference: [1] prometheus#254
1 parent adc7c4e commit bc6f1be

File tree

6 files changed

+392
-1
lines changed

6 files changed

+392
-1
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ homepage = "https://github.com/prometheus/client_rust"
1111
documentation = "https://docs.rs/prometheus-client"
1212

1313
[features]
14-
default = []
14+
default = ["summary"]
1515
protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build"]
16+
summary = ["dep:quanta", "dep:quantiles"]
1617

1718
[workspace]
1819
members = ["derive-encode"]
@@ -24,6 +25,8 @@ parking_lot = "0.12"
2425
prometheus-client-derive-encode = { version = "0.5.0", path = "derive-encode" }
2526
prost = { version = "0.12.0", optional = true }
2627
prost-types = { version = "0.12.0", optional = true }
28+
quanta = { version = "0.12.6", optional = true }
29+
quantiles = { version = "0.7.1", optional = true }
2730

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

src/encoding.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,22 @@ impl MetricEncoder<'_> {
186186
)
187187
}
188188

189+
/// Encode a summary.
190+
#[cfg(feature = "summary")]
191+
pub fn encode_summary<S: EncodeLabelSet>(
192+
&mut self,
193+
sum: f64,
194+
count: u64,
195+
quantiles: &[(f64, f64)],
196+
) -> Result<(), std::fmt::Error> {
197+
for_both_mut!(
198+
self,
199+
MetricEncoderInner,
200+
e,
201+
e.encode_summary::<S>(sum, count, quantiles)
202+
)
203+
}
204+
189205
/// Encode a metric family.
190206
pub fn encode_family<'s, S: EncodeLabelSet>(
191207
&'s mut self,

src/encoding/protobuf.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ impl From<MetricType> for openmetrics_data_model::MetricType {
5353
MetricType::Counter => openmetrics_data_model::MetricType::Counter,
5454
MetricType::Gauge => openmetrics_data_model::MetricType::Gauge,
5555
MetricType::Histogram => openmetrics_data_model::MetricType::Histogram,
56+
#[cfg(feature = "summary")]
57+
MetricType::Summary => openmetrics_data_model::MetricType::Summary,
5658
MetricType::Info => openmetrics_data_model::MetricType::Info,
5759
MetricType::Unknown => openmetrics_data_model::MetricType::Unknown,
5860
}
@@ -288,6 +290,42 @@ impl MetricEncoder<'_> {
288290

289291
Ok(())
290292
}
293+
294+
#[cfg(feature = "summary")]
295+
pub fn encode_summary<S: EncodeLabelSet>(
296+
&mut self,
297+
sum: f64,
298+
count: u64,
299+
quantiles: &[(f64, f64)],
300+
) -> Result<(), std::fmt::Error> {
301+
let quantile = quantiles
302+
.iter()
303+
.enumerate()
304+
.map(|(_, (quantile, value))| {
305+
Ok(openmetrics_data_model::summary_value::Quantile {
306+
quantile: *quantile,
307+
value: *value,
308+
})
309+
})
310+
.collect::<Result<Vec<_>, std::fmt::Error>>()?;
311+
312+
self.family.push(openmetrics_data_model::Metric {
313+
labels: self.labels.clone(),
314+
metric_points: vec![openmetrics_data_model::MetricPoint {
315+
value: Some(openmetrics_data_model::metric_point::Value::SummaryValue(
316+
openmetrics_data_model::SummaryValue {
317+
count,
318+
created: None,
319+
quantile,
320+
sum: Some(openmetrics_data_model::summary_value::Sum::DoubleValue(sum)),
321+
},
322+
)),
323+
..Default::default()
324+
}],
325+
});
326+
327+
Ok(())
328+
}
291329
}
292330

293331
impl<S: EncodeLabelSet, V: EncodeExemplarValue> TryFrom<&Exemplar<S, V>>
@@ -817,6 +855,45 @@ mod tests {
817855
}
818856
}
819857

858+
#[cfg(feature = "summary")]
859+
#[test]
860+
fn encode_summary() {
861+
use crate::metrics::summary::Summary;
862+
863+
let mut registry = Registry::default();
864+
let summary = Summary::new(std::time::Duration::from_secs(5), 10, vec![0.5, 0.9, 0.99], 0.01);
865+
registry.register("my_summary", "My summary", summary.clone());
866+
summary.observe(0.10);
867+
summary.observe(0.20);
868+
summary.observe(0.30);
869+
870+
let metric_set = encode(&registry).unwrap();
871+
872+
let family = metric_set.metric_families.first().unwrap();
873+
assert_eq!("my_summary", family.name);
874+
assert_eq!("My summary.", family.help);
875+
876+
assert_eq!(
877+
openmetrics_data_model::MetricType::Summary as i32,
878+
extract_metric_type(&metric_set)
879+
);
880+
881+
match extract_metric_point_value(&metric_set) {
882+
openmetrics_data_model::metric_point::Value::SummaryValue(value) => {
883+
assert_eq!(
884+
"Some(DoubleValue(0.6000000000000001))",
885+
format!("{:?}", value.sum),
886+
);
887+
assert_eq!(3, value.count);
888+
assert_eq!(
889+
"[Quantile { quantile: 0.5, value: 0.2 }, Quantile { quantile: 0.9, value: 0.3 }, Quantile { quantile: 0.99, value: 0.3 }]",
890+
format!("{:?}", value.quantile),
891+
);
892+
}
893+
_ => panic!("wrong value type"),
894+
}
895+
}
896+
820897
#[test]
821898
fn encode_family_counter_histogram() {
822899
let mut registry = Registry::default();

src/encoding/text.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,40 @@ impl MetricEncoder<'_> {
444444
Ok(())
445445
}
446446

447+
#[cfg(feature = "summary")]
448+
pub fn encode_summary<S: EncodeLabelSet>(
449+
&mut self,
450+
sum: f64,
451+
count: u64,
452+
quantiles: &[(f64, f64)],
453+
) -> Result<(), std::fmt::Error> {
454+
self.write_prefix_name_unit()?;
455+
self.write_suffix("sum")?;
456+
self.encode_labels::<NoLabelSet>(None)?;
457+
self.writer.write_str(" ")?;
458+
self.writer.write_str(dtoa::Buffer::new().format(sum))?;
459+
self.newline()?;
460+
461+
self.write_prefix_name_unit()?;
462+
self.write_suffix("count")?;
463+
self.encode_labels::<NoLabelSet>(None)?;
464+
self.writer.write_str(" ")?;
465+
self.writer.write_str(itoa::Buffer::new().format(count))?;
466+
self.newline()?;
467+
468+
for (_, (quantile, result)) in quantiles.iter().enumerate() {
469+
self.write_prefix_name_unit()?;
470+
self.encode_labels(Some(&[("quantile", *quantile)]))?;
471+
472+
self.writer.write_str(" ")?;
473+
self.writer.write_str(result.to_string().as_str())?;
474+
475+
self.newline()?;
476+
}
477+
478+
Ok(())
479+
}
480+
447481
/// Encode an exemplar for the given metric.
448482
fn encode_exemplar<S: EncodeLabelSet, V: EncodeExemplarValue>(
449483
&mut self,
@@ -982,6 +1016,34 @@ mod tests {
9821016
parse_with_python_client(encoded);
9831017
}
9841018

1019+
#[cfg(feature = "summary")]
1020+
#[test]
1021+
fn encode_summary() {
1022+
use crate::metrics::summary::Summary;
1023+
1024+
let mut registry = Registry::default();
1025+
let summary = Summary::new(std::time::Duration::from_secs(3), 10, vec![0.5, 0.9, 0.99], 0.0);
1026+
registry.register("my_summary", "My summary", summary.clone());
1027+
summary.observe(0.10);
1028+
summary.observe(0.20);
1029+
summary.observe(0.30);
1030+
1031+
let mut encoded = String::new();
1032+
encode(&mut encoded, &registry).unwrap();
1033+
1034+
let expected = "# HELP my_summary My summary.\n".to_owned()
1035+
+ "# TYPE my_summary summary\n"
1036+
+ "my_summary_sum 0.6000000000000001\n"
1037+
+ "my_summary_count 3\n"
1038+
+ "my_summary{quantile=\"0.5\"} 0.2\n"
1039+
+ "my_summary{quantile=\"0.9\"} 0.3\n"
1040+
+ "my_summary{quantile=\"0.99\"} 0.3\n"
1041+
+ "# EOF\n";
1042+
assert_eq!(expected, encoded);
1043+
1044+
parse_with_python_client(encoded);
1045+
}
1046+
9851047
#[test]
9861048
fn sub_registry_with_prefix_and_label() {
9871049
let top_level_metric_name = "my_top_level_metric";

src/metrics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub mod family;
66
pub mod gauge;
77
pub mod histogram;
88
pub mod info;
9+
#[cfg(feature = "summary")]
10+
pub mod summary;
911

1012
/// A metric that is aware of its Open Metrics metric type.
1113
pub trait TypedMetric {
@@ -21,6 +23,8 @@ pub enum MetricType {
2123
Gauge,
2224
Histogram,
2325
Info,
26+
#[cfg(feature = "summary")]
27+
Summary,
2428
Unknown,
2529
// Not (yet) supported metric types.
2630
//
@@ -37,6 +41,8 @@ impl MetricType {
3741
MetricType::Gauge => "gauge",
3842
MetricType::Histogram => "histogram",
3943
MetricType::Info => "info",
44+
#[cfg(feature = "summary")]
45+
MetricType::Summary => "summary",
4046
MetricType::Unknown => "unknown",
4147
}
4248
}

0 commit comments

Comments
 (0)