Skip to content

Commit 9141c11

Browse files
authored
feat(derive): Introduce flatten attribute (#118)
Introduce a `#[prometheus(flatten)]` attribute which can be used when deriving `EncodeLabelSet`, allowing a nested struct to be flattened during encoding. Signed-off-by: John Howard <[email protected]> Signed-off-by: Max Inden <[email protected]>
1 parent c619ad5 commit 9141c11

File tree

5 files changed

+99
-18
lines changed

5 files changed

+99
-18
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Introduce `Collector` abstraction allowing users to provide additional metrics
1212
and their description on each scrape. See [PR 82].
1313

14+
- Introduce a `#[prometheus(flatten)]` attribute which can be used when deriving `EncodeLabelSet`, allowing
15+
a nested struct to be flattened during encoding. See [PR 118].
16+
17+
For example:
18+
19+
```rust
20+
#[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)]
21+
struct CommonLabels {
22+
a: u64,
23+
b: u64,
24+
}
25+
#[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)]
26+
struct Labels {
27+
unique: u64,
28+
#[prometheus(flatten)]
29+
common: CommonLabels,
30+
}
31+
```
32+
33+
Would be encoded as:
34+
35+
```
36+
my_metric{a="42",b="42",unique="42"} 42
37+
```
38+
1439
[PR 82]: https://github.com/prometheus/client_rust/pull/82
40+
[PR 118]: https://github.com/prometheus/client_rust/pull/118
1541

1642
## [0.19.0]
1743

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ members = ["derive-encode"]
2121
dtoa = "1.0"
2222
itoa = "1.0"
2323
parking_lot = "0.12"
24-
prometheus-client-derive-encode = { version = "0.4.0", path = "derive-encode" }
24+
prometheus-client-derive-encode = { version = "0.4.1", path = "derive-encode" }
2525
prost = { version = "0.11.0", optional = true }
2626
prost-types = { version = "0.11.0", optional = true }
2727

derive-encode/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "prometheus-client-derive-encode"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
authors = ["Max Inden <[email protected]>"]
55
edition = "2021"
66
description = "Auxiliary crate to derive Encode trait from prometheus-client."

derive-encode/src/lib.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use quote::quote;
1212
use syn::DeriveInput;
1313

1414
/// Derive `prometheus_client::encoding::EncodeLabelSet`.
15-
#[proc_macro_derive(EncodeLabelSet)]
15+
#[proc_macro_derive(EncodeLabelSet, attributes(prometheus))]
1616
pub fn derive_encode_label_set(input: TokenStream) -> TokenStream {
1717
let ast: DeriveInput = syn::parse(input).unwrap();
1818
let name = &ast.ident;
@@ -22,22 +22,40 @@ pub fn derive_encode_label_set(input: TokenStream) -> TokenStream {
2222
syn::Fields::Named(syn::FieldsNamed { named, .. }) => named
2323
.into_iter()
2424
.map(|f| {
25-
let ident = f.ident.unwrap();
26-
let ident_string = KEYWORD_IDENTIFIERS
25+
let attribute = f
26+
.attrs
2727
.iter()
28-
.find(|pair| ident == pair.1)
29-
.map(|pair| pair.0.to_string())
30-
.unwrap_or_else(|| ident.to_string());
31-
32-
quote! {
33-
let mut label_encoder = encoder.encode_label();
34-
let mut label_key_encoder = label_encoder.encode_label_key()?;
35-
EncodeLabelKey::encode(&#ident_string, &mut label_key_encoder)?;
36-
37-
let mut label_value_encoder = label_key_encoder.encode_label_value()?;
38-
EncodeLabelValue::encode(&self.#ident, &mut label_value_encoder)?;
39-
40-
label_value_encoder.finish()?;
28+
.find(|a| a.path.is_ident("prometheus"))
29+
.map(|a| a.parse_args::<syn::Ident>().unwrap().to_string());
30+
let flatten = match attribute.as_deref() {
31+
Some("flatten") => true,
32+
Some(other) => {
33+
panic!("Provided attribute '{other}', but only 'flatten' is supported")
34+
}
35+
None => false,
36+
};
37+
let ident = f.ident.unwrap();
38+
if flatten {
39+
quote! {
40+
EncodeLabelSet::encode(&self.#ident, encoder)?;
41+
}
42+
} else {
43+
let ident_string = KEYWORD_IDENTIFIERS
44+
.iter()
45+
.find(|pair| ident == pair.1)
46+
.map(|pair| pair.0.to_string())
47+
.unwrap_or_else(|| ident.to_string());
48+
49+
quote! {
50+
let mut label_encoder = encoder.encode_label();
51+
let mut label_key_encoder = label_encoder.encode_label_key()?;
52+
EncodeLabelKey::encode(&#ident_string, &mut label_key_encoder)?;
53+
54+
let mut label_value_encoder = label_key_encoder.encode_label_value()?;
55+
EncodeLabelValue::encode(&self.#ident, &mut label_value_encoder)?;
56+
57+
label_value_encoder.finish()?;
58+
}
4159
}
4260
})
4361
.collect(),

derive-encode/tests/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,40 @@ fn remap_keyword_identifiers() {
136136
+ "# EOF\n";
137137
assert_eq!(expected, buffer);
138138
}
139+
140+
#[test]
141+
fn flatten() {
142+
#[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)]
143+
struct CommonLabels {
144+
a: u64,
145+
b: u64,
146+
}
147+
#[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)]
148+
struct Labels {
149+
unique: u64,
150+
#[prometheus(flatten)]
151+
common: CommonLabels,
152+
}
153+
154+
let mut registry = Registry::default();
155+
let family = Family::<Labels, Counter>::default();
156+
registry.register("my_counter", "This is my counter", family.clone());
157+
158+
// Record a single HTTP GET request.
159+
family
160+
.get_or_create(&Labels {
161+
unique: 1,
162+
common: CommonLabels { a: 2, b: 3 },
163+
})
164+
.inc();
165+
166+
// Encode all metrics in the registry in the text format.
167+
let mut buffer = String::new();
168+
encode(&mut buffer, &registry).unwrap();
169+
170+
let expected = "# HELP my_counter This is my counter.\n".to_owned()
171+
+ "# TYPE my_counter counter\n"
172+
+ "my_counter_total{unique=\"1\",a=\"2\",b=\"3\"} 1\n"
173+
+ "# EOF\n";
174+
assert_eq!(expected, buffer);
175+
}

0 commit comments

Comments
 (0)