Skip to content

Commit 83dd006

Browse files
authored
encoding/: Adopt serde style encoding (#105)
Adopt encoding style similar to serde. `EncodeXXX` for a type that can be encoded (see serde's `Serialize`) and `XXXEncoder` for a supported encoding i.e. text and protobuf (see serde's `Serializer`). - Compatible with Rust's additive features. Enabling the `protobuf` feature does not change type signatures. - `EncodeMetric` is trait object safe, and thus `Registry` can use dynamic dispatch. - Implement a single encoding trait `EncodeMetric` per metric type instead of one implementation per metric AND per encoding format. - Leverage `std::fmt::Write` instead of `std::io::Write`. The OpenMetrics text format has to be unicode compliant. The OpenMetrics Protobuf format requires labels to be valid strings. Signed-off-by: Max Inden <[email protected]>
1 parent 6cd0dba commit 83dd006

File tree

23 files changed

+1562
-1238
lines changed

23 files changed

+1562
-1238
lines changed

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616

1717
### Changed
1818

19-
- Move`Encode` trait from `prometheus_client::encoding::text` to `prometheus_client::encoding`. See [PR 83].
19+
- Always use dynamic dispatch on `Registry`, i.e. remove generic type parameter `M` from `Registry`. See [PR 105].
20+
- Refactor encoding. See [PR 105].
21+
- Introducing separate traits to encode
22+
- value (e.g. `EncodeCounterValue`)
23+
- label set (`EncodeLabelSet`), derivable for structs via `prometheus-client-derive-encode`
24+
- label (`EncodeLabel`)
25+
- label key (`EncodeLabelKey`)
26+
- label value (`EncodeLabelValue`), derivable for enums via `prometheus-client-derive-encode`
27+
- Encode as UTF-8 strings, not bytes. I.e. use `std::fmt::Write` instead of `std::io::Write`.
28+
- Use signed integers for `Gauge` for compliance with OpenMetrics protobuf
29+
format. See [PR 105].
2030

2131
[PR 83]: https://github.com/prometheus/client_rust/pull/83
2232
[PR 85]: https://github.com/prometheus/client_rust/pull/85
2333
[PR 96]: https://github.com/prometheus/client_rust/pull/96
34+
[PR 105]: https://github.com/prometheus/client_rust/pull/105
2435

2536
## [0.18.1]
2637

Cargo.toml

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

1313
[features]
14-
protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build", "dep:void", "prometheus-client-derive-encode/protobuf"]
14+
default = []
15+
protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build"]
1516

1617
[workspace]
1718
members = ["derive-encode"]
@@ -23,7 +24,6 @@ parking_lot = "0.12"
2324
prometheus-client-derive-encode = { version = "0.3.0", path = "derive-encode" }
2425
prost = { version = "0.11.0", optional = true }
2526
prost-types = { version = "0.11.0", optional = true }
26-
void = { version = "1.0", optional = true }
2727

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

benches/encoding/proto.rs

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,80 @@
22
// https://github.com/tikv/rust-prometheus/blob/ab1ca7285d3463504381a5025ae1951e020d6796/benches/text_encoder.rs:write
33

44
use criterion::{black_box, criterion_group, criterion_main, Criterion};
5-
use prometheus_client::encoding::proto::{encode, EncodeMetric};
6-
use prometheus_client::encoding::Encode;
5+
use prometheus_client::encoding::protobuf;
76
use prometheus_client::metrics::counter::Counter;
87
use prometheus_client::metrics::family::Family;
98
use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
109
use prometheus_client::registry::Registry;
11-
use std::fmt::{Display, Formatter};
10+
use prometheus_client_derive_encode::{EncodeLabelSet, EncodeLabelValue};
1211

1312
pub fn proto(c: &mut Criterion) {
1413
c.bench_function("encode", |b| {
15-
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
16-
struct Labels {
14+
#[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug)]
15+
struct CounterLabels {
1716
path: String,
1817
method: Method,
1918
some_number: u64,
2019
}
2120

22-
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
21+
#[derive(Clone, Hash, PartialEq, Eq, EncodeLabelValue, Debug)]
2322
enum Method {
2423
Get,
2524
#[allow(dead_code)]
2625
Put,
2726
}
2827

29-
impl Display for Method {
30-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31-
match self {
32-
Method::Get => write!(f, "Get"),
33-
Method::Put => write!(f, "Put"),
34-
}
35-
}
28+
#[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug)]
29+
struct HistogramLabels {
30+
region: Region,
3631
}
3732

38-
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
33+
#[derive(Clone, Hash, PartialEq, Eq, EncodeLabelValue, Debug)]
3934
enum Region {
4035
Africa,
4136
#[allow(dead_code)]
4237
Asia,
4338
}
4439

45-
let mut registry = Registry::<Box<dyn EncodeMetric>>::default();
40+
let mut registry = Registry::default();
4641

4742
for i in 0..100 {
48-
let counter_family = Family::<Labels, Counter>::default();
49-
let histogram_family = Family::<Region, Histogram>::new_with_constructor(|| {
50-
Histogram::new(exponential_buckets(1.0, 2.0, 10))
51-
});
43+
let counter_family = Family::<CounterLabels, Counter>::default();
44+
let histogram_family =
45+
Family::<HistogramLabels, Histogram>::new_with_constructor(|| {
46+
Histogram::new(exponential_buckets(1.0, 2.0, 10))
47+
});
5248

5349
registry.register(
5450
format!("my_counter{}", i),
5551
"My counter",
56-
Box::new(counter_family.clone()),
52+
counter_family.clone(),
5753
);
5854
registry.register(
5955
format!("my_histogram{}", i),
6056
"My histogram",
61-
Box::new(histogram_family.clone()),
57+
histogram_family.clone(),
6258
);
6359

6460
for j in 0_u32..100 {
6561
counter_family
66-
.get_or_create(&Labels {
62+
.get_or_create(&CounterLabels {
6763
path: format!("/path/{}", i),
6864
method: Method::Get,
6965
some_number: j.into(),
7066
})
7167
.inc();
7268

7369
histogram_family
74-
.get_or_create(&Region::Africa)
70+
.get_or_create(&HistogramLabels {
71+
region: Region::Africa,
72+
})
7573
.observe(j.into());
7674
}
7775
}
7876

7977
b.iter(|| {
80-
let metric_set = encode(&registry);
78+
let metric_set = protobuf::encode(&registry).unwrap();
8179
black_box(metric_set);
8280
})
8381
});

benches/encoding/text.rs

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
11
// Benchmark inspired by https://github.com/tikv/rust-prometheus/blob/ab1ca7285d3463504381a5025ae1951e020d6796/benches/text_encoder.rs
22

33
use criterion::{black_box, criterion_group, criterion_main, Criterion};
4-
use prometheus_client::encoding::text::{encode, EncodeMetric};
5-
use prometheus_client::encoding::Encode;
4+
use prometheus_client::encoding::{self, EncodeLabelSet, EncodeLabelValue, LabelValueEncoder};
65
use prometheus_client::metrics::counter::Counter;
76
use prometheus_client::metrics::family::Family;
87
use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
98
use prometheus_client::registry::Registry;
10-
use std::io::Write;
9+
use std::fmt::Write;
1110

1211
pub fn text(c: &mut Criterion) {
1312
c.bench_function("encode", |b| {
14-
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
13+
#[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug)]
1514
struct Labels {
1615
method: Method,
1716
status: Status,
1817
some_number: u64,
1918
}
2019

21-
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
20+
#[derive(Clone, Hash, PartialEq, Eq, EncodeLabelValue, Debug)]
2221
enum Method {
2322
Get,
2423
#[allow(dead_code)]
2524
Put,
2625
}
2726

28-
#[derive(Clone, Hash, PartialEq, Eq)]
27+
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
2928
enum Status {
3029
Two,
3130
#[allow(dead_code)]
@@ -34,34 +33,19 @@ pub fn text(c: &mut Criterion) {
3433
Five,
3534
}
3635

37-
impl prometheus_client::encoding::text::Encode for Status {
38-
fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
36+
impl prometheus_client::encoding::EncodeLabelValue for Status {
37+
fn encode(&self, writer: &mut LabelValueEncoder) -> Result<(), std::fmt::Error> {
3938
let status = match self {
40-
Status::Two => b"200",
41-
Status::Four => b"400",
42-
Status::Five => b"500",
39+
Status::Two => "200",
40+
Status::Four => "400",
41+
Status::Five => "500",
4342
};
44-
writer.write_all(status)?;
43+
writer.write_str(status)?;
4544
Ok(())
4645
}
4746
}
4847

49-
#[cfg(feature = "protobuf")]
50-
impl prometheus_client::encoding::proto::EncodeLabels for Status {
51-
fn encode(&self, labels: &mut Vec<prometheus_client::encoding::proto::Label>) {
52-
let value = match self {
53-
Status::Two => "200".to_string(),
54-
Status::Four => "400".to_string(),
55-
Status::Five => "500".to_string(),
56-
};
57-
labels.push(prometheus_client::encoding::proto::Label {
58-
name: "status".to_string(),
59-
value,
60-
});
61-
}
62-
}
63-
64-
let mut registry = Registry::<Box<dyn EncodeMetric>>::default();
48+
let mut registry = Registry::default();
6549

6650
for i in 0..100 {
6751
let counter_family = Family::<Labels, Counter>::default();
@@ -72,12 +56,12 @@ pub fn text(c: &mut Criterion) {
7256
registry.register(
7357
format!("my_counter_{}", i),
7458
"My counter",
75-
Box::new(counter_family.clone()),
59+
counter_family.clone(),
7660
);
7761
registry.register(
7862
format!("my_histogram_{}", i),
7963
"My histogram",
80-
Box::new(histogram_family.clone()),
64+
histogram_family.clone(),
8165
);
8266

8367
for j in 0u32..100 {
@@ -98,10 +82,10 @@ pub fn text(c: &mut Criterion) {
9882
}
9983
}
10084

101-
let mut buffer = vec![];
85+
let mut buffer = String::new();
10286

10387
b.iter(|| {
104-
encode(&mut buffer, &registry).unwrap();
88+
encoding::text::encode(&mut buffer, &registry).unwrap();
10589
black_box(&mut buffer);
10690
})
10791
});

derive-encode/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ repository = "https://github.com/prometheus/client_rust"
99
homepage = "https://github.com/prometheus/client_rust"
1010
documentation = "https://docs.rs/prometheus-client-derive-text-encode"
1111

12-
[features]
13-
protobuf = []
14-
1512
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1613

1714
[dependencies]

0 commit comments

Comments
 (0)