-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Protobuf support #47
Protobuf support #47
Changes from all commits
6aafa61
638d84e
132b7f4
1ae51af
4885fbe
f6c4689
55f673b
fed1866
c80672d
ef88152
463dce9
1c9d81c
0537cdf
3dcbc45
6fd0693
8d89d6e
2077ed7
229a4b0
243e98a
1ecf2b6
2edf381
58ef300
24a56ab
e2a7310
46e8e63
0855a0a
f318b65
223b256
56da14d
c92e42a
2d8e80d
b3dec21
d82860d
7873e28
286e5b4
6354f92
3dd63fb
5c5c508
fea4725
626e695
aebc4bc
0f0f70e
496c8ab
42d1534
20a2b4f
c048128
87143eb
fa909c2
46309f2
63a127d
fa07f15
977ca7f
05dab82
b3d49ce
d55296d
ff7ada0
42f00e3
6d4f6f6
1c965d4
9e83678
cbd97ab
de0f38f
3cff4d6
3548aa4
5af3aff
9649407
680483e
15ef4e0
182d7b0
038fc2a
c68c4ba
59624c5
9c41272
2d28710
250db46
33c940a
2d98197
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "prometheus-client" | ||
version = "0.18.0" | ||
version = "0.18.1" | ||
authors = ["Max Inden <mail@max-inden.de>"] | ||
edition = "2021" | ||
description = "Open Metrics client library allowing users to natively instrument applications." | ||
|
@@ -10,14 +10,21 @@ repository = "https://github.com/prometheus/client_rust" | |
homepage = "https://github.com/prometheus/client_rust" | ||
documentation = "https://docs.rs/prometheus-client" | ||
|
||
[features] | ||
protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build", "dep:prometheus-client-derive-proto-encode", "dep:void"] | ||
|
||
[workspace] | ||
members = ["derive-text-encode"] | ||
members = ["derive-text-encode", "derive-proto-encode"] | ||
|
||
[dependencies] | ||
dtoa = "1.0" | ||
itoa = "1.0" | ||
parking_lot = "0.12" | ||
prometheus-client-derive-proto-encode = { version = "0.1.0", path = "derive-proto-encode", optional = true } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exploring the thought on whether we could use the same derive macro for both text and protobuf. I guess we can not feature flag the generated protobuf code via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is cool if we use the same derive macro, I'll do some experiments. 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I investigated but yeah, I couldn't find a way to do that. |
||
prometheus-client-derive-text-encode = { version = "0.3.0", path = "derive-text-encode" } | ||
prost = { version = "0.9.0", optional = true } | ||
prost-types = { version = "0.9.0", optional = true } | ||
void = { version = "1.0", optional = true } | ||
|
||
[dev-dependencies] | ||
async-std = { version = "1", features = ["attributes"] } | ||
|
@@ -29,6 +36,9 @@ rand = "0.8.4" | |
tide = "0.16" | ||
actix-web = "4" | ||
|
||
[build-dependencies] | ||
prost-build = { version = "0.9.0", optional = true } | ||
|
||
[[bench]] | ||
name = "family" | ||
harness = false | ||
|
@@ -37,3 +47,8 @@ harness = false | |
name = "text" | ||
path = "benches/encoding/text.rs" | ||
harness = false | ||
|
||
[[bench]] | ||
name = "proto" | ||
path = "benches/encoding/proto.rs" | ||
harness = false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Benchmark inspired by https://github.com/tikv/rust-prometheus/blob/ab1ca7285d3463504381a5025ae1951e020d6796/benches/text_encoder.rs | ||
|
||
use criterion::{black_box, criterion_group, criterion_main, Criterion}; | ||
use prometheus_client::encoding::proto::{encode, Encode, EncodeMetric}; | ||
use prometheus_client::metrics::counter::Counter; | ||
use prometheus_client::metrics::family::Family; | ||
use prometheus_client::metrics::histogram::{exponential_buckets, Histogram}; | ||
use prometheus_client::registry::Registry; | ||
use std::fmt::{Display, Formatter}; | ||
|
||
pub fn proto(c: &mut Criterion) { | ||
c.bench_function("encode", |b| { | ||
#[derive(Clone, Hash, PartialEq, Eq, Encode)] | ||
struct Labels { | ||
path: String, | ||
method: Method, | ||
some_number: u64, | ||
} | ||
|
||
#[derive(Clone, Hash, PartialEq, Eq)] | ||
enum Method { | ||
Get, | ||
#[allow(dead_code)] | ||
Put, | ||
} | ||
|
||
impl Display for Method { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Method::Get => write!(f, "Get"), | ||
Method::Put => write!(f, "Put"), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Hash, PartialEq, Eq, Encode)] | ||
enum Region { | ||
Africa, | ||
#[allow(dead_code)] | ||
Asia, | ||
} | ||
|
||
let mut registry = Registry::<Box<dyn EncodeMetric>>::default(); | ||
|
||
for i in 0..100 { | ||
let counter_family = Family::<Labels, Counter>::default(); | ||
let histogram_family = Family::<Region, Histogram>::new_with_constructor(|| { | ||
Histogram::new(exponential_buckets(1.0, 2.0, 10)) | ||
}); | ||
|
||
registry.register( | ||
format!("my_counter{}", i), | ||
"My counter", | ||
Box::new(counter_family.clone()), | ||
); | ||
registry.register( | ||
format!("my_histogram{}", i), | ||
"My histogram", | ||
Box::new(histogram_family.clone()), | ||
); | ||
|
||
for j in 0_u32..100 { | ||
counter_family | ||
.get_or_create(&Labels { | ||
path: format!("/path/{}", i), | ||
method: Method::Get, | ||
some_number: j.into(), | ||
}) | ||
.inc(); | ||
|
||
histogram_family | ||
.get_or_create(&Region::Africa) | ||
.observe(j.into()); | ||
} | ||
} | ||
|
||
b.iter(|| { | ||
let metric_set = encode(®istry); | ||
black_box(metric_set); | ||
}) | ||
}); | ||
} | ||
|
||
criterion_group!(benches, proto); | ||
criterion_main!(benches); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use std::io::Result; | ||
|
||
fn main() -> Result<()> { | ||
#[cfg(feature = "protobuf")] | ||
prost_build::compile_protos( | ||
&["src/encoding/proto/openmetrics_data_model.proto"], | ||
&["src/encoding/proto/"], | ||
)?; | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "prometheus-client-derive-proto-encode" | ||
version = "0.1.0" | ||
authors = ["Akihito Nakano <sora.akatsuki@gmail.com>"] | ||
edition = "2021" | ||
description = "Auxiliary crate to derive protobuf EncodeLabel trait from prometheus-client." | ||
license = "Apache-2.0 OR MIT" | ||
repository = "https://github.com/prometheus/client_rust" | ||
homepage = "https://github.com/prometheus/client_rust" | ||
documentation = "https://docs.rs/prometheus-client-derive-proto-encode" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
proc-macro2 = "1" | ||
quote = "1" | ||
syn = "1" | ||
|
||
[dev-dependencies] | ||
prometheus-client = { path = "../", features = ["protobuf"] } | ||
|
||
[lib] | ||
proc-macro = true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
use proc_macro::TokenStream; | ||
use proc_macro2::TokenStream as TokenStream2; | ||
use quote::quote; | ||
use syn::DeriveInput; | ||
|
||
#[proc_macro_derive(Encode)] | ||
pub fn derive_encode(input: TokenStream) -> TokenStream { | ||
let ast: DeriveInput = syn::parse(input).unwrap(); | ||
let name = &ast.ident; | ||
|
||
match ast.data { | ||
syn::Data::Struct(s) => match s.fields { | ||
syn::Fields::Named(syn::FieldsNamed { named, .. }) => { | ||
let push_labels: TokenStream2 = named | ||
.into_iter() | ||
.map(|f| { | ||
let ident = f.ident.unwrap(); | ||
let ident_string = KEYWORD_IDENTIFIERS | ||
.iter() | ||
.find(|pair| ident == pair.1) | ||
.map(|pair| pair.0.to_string()) | ||
.unwrap_or_else(|| ident.to_string()); | ||
|
||
quote! { | ||
let mut label = prometheus_client::encoding::proto::Label::default(); | ||
label.name = #ident_string.to_string(); | ||
label.value = format!("{}", self.#ident); | ||
labels.push(label); | ||
} | ||
}) | ||
.collect(); | ||
|
||
quote! { | ||
impl prometheus_client::encoding::proto::EncodeLabels for #name { | ||
fn encode(&self, labels: &mut Vec<prometheus_client::encoding::proto::Label>) { | ||
#push_labels | ||
} | ||
} | ||
} | ||
} | ||
syn::Fields::Unnamed(_) => { | ||
panic!("Can not derive Encode for struct with unnamed fields.") | ||
} | ||
syn::Fields::Unit => panic!("Can not derive Encode for struct with unit field."), | ||
}, | ||
syn::Data::Enum(syn::DataEnum { variants, .. }) => { | ||
let match_arms: TokenStream2 = variants | ||
.into_iter() | ||
.map(|v| { | ||
let ident = v.ident; | ||
quote! { | ||
#name::#ident => { | ||
let mut label = prometheus_client::encoding::proto::Label::default(); | ||
label.name = stringify!(#name).to_string(); | ||
label.value = stringify!(#ident).to_string(); | ||
labels.push(label); | ||
} | ||
} | ||
}) | ||
.collect(); | ||
|
||
quote! { | ||
impl prometheus_client::encoding::proto::EncodeLabels for #name { | ||
fn encode(&self, labels: &mut Vec<prometheus_client::encoding::proto::Label>) { | ||
match self { | ||
#match_arms | ||
}; | ||
} | ||
} | ||
} | ||
} | ||
syn::Data::Union(_) => panic!("Can not derive Encode for union."), | ||
}.into() | ||
} | ||
|
||
// Copied from https://github.com/djc/askama (MIT and APACHE licensed) and | ||
// modified. | ||
static KEYWORD_IDENTIFIERS: [(&str, &str); 48] = [ | ||
("as", "r#as"), | ||
("break", "r#break"), | ||
("const", "r#const"), | ||
("continue", "r#continue"), | ||
("crate", "r#crate"), | ||
("else", "r#else"), | ||
("enum", "r#enum"), | ||
("extern", "r#extern"), | ||
("false", "r#false"), | ||
("fn", "r#fn"), | ||
("for", "r#for"), | ||
("if", "r#if"), | ||
("impl", "r#impl"), | ||
("in", "r#in"), | ||
("let", "r#let"), | ||
("loop", "r#loop"), | ||
("match", "r#match"), | ||
("mod", "r#mod"), | ||
("move", "r#move"), | ||
("mut", "r#mut"), | ||
("pub", "r#pub"), | ||
("ref", "r#ref"), | ||
("return", "r#return"), | ||
("static", "r#static"), | ||
("struct", "r#struct"), | ||
("trait", "r#trait"), | ||
("true", "r#true"), | ||
("type", "r#type"), | ||
("unsafe", "r#unsafe"), | ||
("use", "r#use"), | ||
("where", "r#where"), | ||
("while", "r#while"), | ||
("async", "r#async"), | ||
("await", "r#await"), | ||
("dyn", "r#dyn"), | ||
("abstract", "r#abstract"), | ||
("become", "r#become"), | ||
("box", "r#box"), | ||
("do", "r#do"), | ||
("final", "r#final"), | ||
("macro", "r#macro"), | ||
("override", "r#override"), | ||
("priv", "r#priv"), | ||
("typeof", "r#typeof"), | ||
("unsized", "r#unsized"), | ||
("virtual", "r#virtual"), | ||
("yield", "r#yield"), | ||
("try", "r#try"), | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think CI is currently not running with
--all-features
. Can you add it with this pull request to make sure your feature is being tested?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added 33c940a