Skip to content

Commit 82ab3c4

Browse files
authored
fix(encoding): do not encode descriptor of empty family (#279)
Signed-off-by: Jean-Baptiste Skutnik <jskutnik@ddn.com>
1 parent 6bb0b10 commit 82ab3c4

File tree

5 files changed

+55
-1
lines changed

5 files changed

+55
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
fewer `as` casts in their implementation. This caught an issue where
1313
`EncodeGaugeValue` would not error when encoding some `u64`s that don't fit
1414
in a `i64`. See [PR 281].
15+
- Filter out empty metric families, to match the go client. See [PR 279].
1516

17+
[PR 279]: https://github.com/prometheus/client_rust/pull/279
1618
[PR 281]: https://github.com/prometheus/client_rust/pull/281
1719

1820
## [0.24.0]

src/encoding.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ pub trait EncodeMetric {
5050
// One can not use [`TypedMetric`] directly, as associated constants are not
5151
// object safe and thus can not be used with dynamic dispatching.
5252
fn metric_type(&self) -> MetricType;
53+
54+
/// Check if the metric is empty.
55+
///
56+
/// An empty metric is a metric that has no data to encode, and thus should not have any
57+
/// descriptor in the final output.
58+
///
59+
/// By default, this returns `false`, ensuring the metric and its description is always
60+
/// encoded.
61+
fn is_empty(&self) -> bool {
62+
false
63+
}
5364
}
5465

5566
impl EncodeMetric for Box<dyn EncodeMetric> {

src/encoding/text.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,4 +1278,41 @@ def parse(input):
12781278
.unwrap();
12791279
})
12801280
}
1281+
1282+
#[test]
1283+
fn encode_omit_empty() {
1284+
let mut registry = Registry::default();
1285+
let counter1: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1286+
let counter2: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1287+
let counter3: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1288+
1289+
registry.register("counter1", "First counter", counter1.clone());
1290+
registry.register("counter2", "Second counter", counter2.clone());
1291+
registry.register("counter3", "Third counter", counter3.clone());
1292+
1293+
counter1.get_or_create(&vec![("label", "value")]).inc();
1294+
1295+
let mut encoded = String::new();
1296+
encode(&mut encoded, &registry).unwrap();
1297+
1298+
let expected = "# HELP counter1 First counter.\n".to_owned()
1299+
+ "# TYPE counter1 counter\n"
1300+
+ "counter1_total{label=\"value\"} 1\n"
1301+
+ "# EOF\n";
1302+
assert_eq!(expected, encoded);
1303+
1304+
counter2.get_or_create(&vec![("label", "value")]).inc();
1305+
1306+
let mut encoded = String::new();
1307+
encode(&mut encoded, &registry).unwrap();
1308+
1309+
let expected = "# HELP counter1 First counter.\n".to_owned()
1310+
+ "# TYPE counter1 counter\n"
1311+
+ "counter1_total{label=\"value\"} 1\n"
1312+
+ "# HELP counter2 Second counter.\n"
1313+
+ "# TYPE counter2 counter\n"
1314+
+ "counter2_total{label=\"value\"} 1\n"
1315+
+ "# EOF\n";
1316+
assert_eq!(expected, encoded);
1317+
}
12811318
}

src/metrics/family.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ where
377377
fn metric_type(&self) -> MetricType {
378378
M::TYPE
379379
}
380+
381+
fn is_empty(&self) -> bool {
382+
self.metrics.read().is_empty()
383+
}
380384
}
381385

382386
#[cfg(test)]

src/registry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ impl Registry {
287287
}
288288

289289
pub(crate) fn encode(&self, encoder: &mut DescriptorEncoder) -> Result<(), std::fmt::Error> {
290-
for (descriptor, metric) in self.metrics.iter() {
290+
for (descriptor, metric) in self.metrics.iter().filter(|(_, m)| !m.is_empty()) {
291291
let mut descriptor_encoder =
292292
encoder.with_prefix_and_labels(self.prefix.as_ref(), &self.labels);
293293
let metric_encoder = descriptor_encoder.encode_descriptor(

0 commit comments

Comments
 (0)