Skip to content
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 #83

Merged
merged 75 commits into from
Sep 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
b78dbaf
Rough implementation to encode Counter
ackintosh Feb 24, 2022
e2e96f9
src/encoding/proto: Construct Registry with proto::EncodeMetric
mxinden Feb 28, 2022
abb66df
src/encoding/proto: Implement encoding for labels
ackintosh Mar 14, 2022
33b527f
src/encoding/proto: Implement encoding for CounterWithExemplar
ackintosh Mar 14, 2022
a3e6afc
Move comment
ackintosh Mar 19, 2022
c3bb963
src/encoding/proto: Implement encoding for Gauge
ackintosh Mar 19, 2022
d29e2d3
src/encoding/proto: Implement encoding for Histogram
ackintosh Mar 20, 2022
18ca645
src/encoding/proto: Tweak function name according to encoding::text
ackintosh Mar 20, 2022
9c97af5
src/encoding/proto: Implement encoding for HistogramWithExemplars
ackintosh Mar 20, 2022
ca45f64
src/encoding/proto: Move function for readability
ackintosh Mar 20, 2022
d76e7a0
src/encoding/proto: Implement encoding for Info
ackintosh Mar 20, 2022
4b31a21
src/encoding/proto: Add tests for Counter
ackintosh Mar 24, 2022
571c707
src/encoding/proto: Add assertions for MetricType
ackintosh Mar 24, 2022
84a0745
src/encoding/proto: DRY-ed tests with `extract_metric_point_value`
ackintosh Mar 24, 2022
90d8b00
src/encoding/proto: Assert name and help
ackintosh Mar 24, 2022
03fc057
src/encoding/proto: Refactor: use associated constants
ackintosh Mar 24, 2022
745160c
src/encoding/proto: Add assertions for counter.exemplar
ackintosh Mar 24, 2022
4b839c9
src/encoding/proto: Add assertion for Unit
ackintosh Mar 24, 2022
202d457
src/encoding/proto: Refactoring: extract a method `Unit::as_str()`
ackintosh Mar 24, 2022
2e7e35a
src/encoding/proto: Remove superfluous comments
ackintosh Mar 24, 2022
bd90dc3
src/encoding/proto: Add tests for Family
ackintosh Mar 24, 2022
c1b38a2
src/encoding/proto: Remove redundant test
ackintosh Mar 25, 2022
92b4493
src/encoding/proto: Add doc
ackintosh Mar 26, 2022
3cfe085
Introduce `protobuf` feature
ackintosh Jul 27, 2022
ee6344a
src/encoding/proto: Refactoring for loop
ackintosh Jul 27, 2022
cc0df24
Implement proc macro `EncodeProtobuf`
ackintosh Aug 8, 2022
7040f2a
Add impl EncodeGaugeValue for u64
ackintosh Aug 10, 2022
6cf6e09
Try Associated Type
ackintosh Jul 31, 2022
887beff
Fix life parameter issue
ackintosh Aug 3, 2022
49a3dfb
Make EncodeLabel::encode return iterator
ackintosh Aug 3, 2022
3b8dc35
src/encoding/proto: Refactor EncodeLabel and EncodeMetric
mxinden Aug 8, 2022
f07148b
Fix assertions because of changes of the order of result
ackintosh Aug 12, 2022
0583a66
Remove unnecessary lifetime parameter
ackintosh Aug 12, 2022
88fe41e
Rename EncodeLabel -> EncodeLabels
ackintosh Aug 12, 2022
e5a37a3
Using `Void` to indicate the impossible case
ackintosh Aug 12, 2022
7d2c6a0
Changed proc macro accordingly since `encode` returns iterator
ackintosh Aug 12, 2022
2e55b46
Rename the proc macro since the name was a bit redundant
ackintosh Aug 12, 2022
83a0b89
Add a test for Family (counter and histogram)
ackintosh Aug 13, 2022
00dcdd1
Add benchmark code for proto encoding
ackintosh Aug 13, 2022
f20ae6b
Add a test for Family, Counter and Histogram
ackintosh Aug 13, 2022
50ca0a9
Remove `Box` to reduce allocation count
ackintosh Aug 15, 2022
6afc9b7
Use std::iter::Once on enums as enum returns single element
ackintosh Aug 15, 2022
289241c
encoding/proto: Pass metric and label vec
mxinden Aug 19, 2022
dd69b89
derive-proto-encode: Update according to the changes in encoding/proto
ackintosh Aug 23, 2022
2f8d1a3
Cargo.toml: Make `void` optional
ackintosh Aug 23, 2022
547e1b4
benches: Update according to the changes in encoding/proto
ackintosh Aug 23, 2022
f8c621a
encoding/proto: same implementation to slice
ackintosh Aug 23, 2022
2d169ad
Bump the crate version and add a changelog entry
ackintosh Aug 27, 2022
4b7c75f
Add --all-features
ackintosh Aug 27, 2022
48745c4
Remove unnecessary generic parameter
ackintosh Aug 30, 2022
f367ebf
Merge derive-proto-encode into derive-text-encode
ackintosh Sep 5, 2022
870cdae
`protobuf` feature requires Display
ackintosh Sep 5, 2022
26ec648
Rename `prometheus-client-derive-text-encode` to `prometheus-client-d…
ackintosh Sep 5, 2022
2fb5f1f
derive-encode: Bump up patch version because of adding protobuf feature
ackintosh Sep 5, 2022
1d70145
Merge branch 'master' into protobuf
ackintosh Sep 5, 2022
15dc7a7
Fix missing docs
ackintosh Sep 7, 2022
bb08122
Fix clippy warnings: field assignment outside of initializer for an i…
ackintosh Sep 7, 2022
766c650
Fix clippy warnings: an implementation of `From` is preferred
ackintosh Sep 7, 2022
e99575b
Fix redundant clone
ackintosh Sep 7, 2022
a6c797b
Fix clippy warnings: manual implementation of `Option::map`
ackintosh Sep 7, 2022
cc9f739
Fix clippy warnings: explicit lifetimes given in parameter types wher…
ackintosh Sep 7, 2022
d106aa3
Fix clippy warnings: called `map(..).flatten()` on `Option`
ackintosh Sep 7, 2022
77c9c22
Allow some lint warnings on openmetrics.rs which is an auto-generated…
ackintosh Sep 7, 2022
ec56224
Remove unnecessary cfg
ackintosh Sep 7, 2022
d992bad
Move`Encode` trait from `prometheus_client::encoding::text` to `prome…
ackintosh Sep 7, 2022
8fce756
Bump up the minor version as the unreleased contains breaking changes
ackintosh Sep 7, 2022
639cb0a
Update a link to PR
ackintosh Sep 7, 2022
ca0f583
misc fixes
divagant-martian Sep 8, 2022
8baf256
Remove a `Display` requirement
ackintosh Sep 10, 2022
3a2190d
Remove unnecessary Display implementation
ackintosh Sep 10, 2022
396512f
Disambiguate `Encode`
ackintosh Sep 10, 2022
20fd62e
Fix `cannot find derive macro Encode in this scope` in docs
ackintosh Sep 10, 2022
8ac25a5
Fix clippy warnings
ackintosh Sep 10, 2022
f5b61f1
Don't use AtomicI64 on unsupported platforms
ackintosh Sep 10, 2022
69f85ba
Merge pull request #9 from ackintosh/remove-display
ackintosh Sep 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: test
args: --benches
args: --benches --all-features

test:
name: Test Suite
Expand Down Expand Up @@ -73,7 +73,7 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: test
args: --all
args: --all --all-features

fmt:
name: Rustfmt
Expand Down Expand Up @@ -135,7 +135,7 @@ jobs:
RUSTDOCFLAGS: "--deny broken_intra_doc_links"
with:
command: doc
args: --verbose --workspace --no-deps --document-private-items
args: --verbose --workspace --no-deps --document-private-items --all-features

cross-compile:
name: Cross compile
Expand All @@ -159,4 +159,4 @@ jobs:
with:
use-cross: true
command: build
args: --release --target=${{ matrix.target }}
args: --release --target=${{ matrix.target }} --all-features
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.19.0] - unreleased

### Added
- Added support for the OpenMetrics protobuf format. See [PR 83].

### Changed

- Move`Encode` trait from `prometheus_client::encoding::text` to `prometheus_client::encoding`. See [PR 83].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏


[PR 83]: https://github.com/prometheus/client_rust/pull/83

## [0.18.0]

### Changed
Expand Down
22 changes: 19 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "prometheus-client"
version = "0.18.0"
version = "0.19.0"
authors = ["Max Inden <mail@max-inden.de>"]
edition = "2021"
description = "Open Metrics client library allowing users to natively instrument applications."
Expand All @@ -10,14 +10,20 @@ 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:void", "prometheus-client-derive-encode/protobuf"]

[workspace]
members = ["derive-text-encode"]
members = ["derive-encode"]

[dependencies]
dtoa = "1.0"
itoa = "1.0"
parking_lot = "0.12"
prometheus-client-derive-text-encode = { version = "0.3.0", path = "derive-text-encode" }
prometheus-client-derive-encode = { version = "0.3.0", path = "derive-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"] }
Expand All @@ -29,6 +35,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
Expand All @@ -37,3 +46,10 @@ harness = false
name = "text"
path = "benches/encoding/text.rs"
harness = false
required-features = []

[[bench]]
name = "proto"
path = "benches/encoding/proto.rs"
harness = false
required-features = ["protobuf"]
87 changes: 87 additions & 0 deletions benches/encoding/proto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Benchmark inspired by
// https://github.com/tikv/rust-prometheus/blob/ab1ca7285d3463504381a5025ae1951e020d6796/benches/text_encoder.rs:write

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use prometheus_client::encoding::proto::{encode, EncodeMetric};
use prometheus_client::encoding::Encode;
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, Encode)]
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(&registry);
black_box(metric_set);
})
});
}

criterion_group!(benches, proto);
criterion_main!(benches);
20 changes: 18 additions & 2 deletions benches/encoding/text.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// 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::text::{encode, Encode, EncodeMetric};
use prometheus_client::encoding::text::{encode, EncodeMetric};
use prometheus_client::encoding::Encode;
use prometheus_client::metrics::counter::Counter;
use prometheus_client::metrics::family::Family;
use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
Expand Down Expand Up @@ -33,7 +34,7 @@ pub fn text(c: &mut Criterion) {
Five,
}

impl Encode for Status {
impl prometheus_client::encoding::text::Encode for Status {
fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
let status = match self {
Status::Two => b"200",
Expand All @@ -45,6 +46,21 @@ pub fn text(c: &mut Criterion) {
}
}

#[cfg(feature = "protobuf")]
impl prometheus_client::encoding::proto::EncodeLabels for Status {
fn encode(&self, labels: &mut Vec<prometheus_client::encoding::proto::Label>) {
let value = match self {
Status::Two => "200".to_string(),
Status::Four => "400".to_string(),
Status::Five => "500".to_string(),
};
labels.push(prometheus_client::encoding::proto::Label {
name: "status".to_string(),
value,
});
}
}

let mut registry = Registry::<Box<dyn EncodeMetric>>::default();

for i in 0..100 {
Expand Down
11 changes: 11 additions & 0 deletions build.rs
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(())
}
11 changes: 7 additions & 4 deletions derive-text-encode/Cargo.toml → derive-encode/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
[package]
name = "prometheus-client-derive-text-encode"
version = "0.3.0"
name = "prometheus-client-derive-encode"
version = "0.3.1"
authors = ["Max Inden <mail@max-inden.de>"]
edition = "2021"
description = "Auxiliary crate to derive text Encode trait from prometheus-client."
description = "Auxiliary crate to derive Encode 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-text-encode"

[features]
protobuf = []

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
Expand All @@ -17,7 +20,7 @@ quote = "1"
syn = "1"

[dev-dependencies]
prometheus-client = { path = "../" }
prometheus-client = { path = "../", features = ["protobuf"] }

[lib]
proc-macro = true
87 changes: 86 additions & 1 deletion derive-text-encode/src/lib.rs → derive-encode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;

let body = match ast.data {
let body = match ast.clone().data {
syn::Data::Struct(s) => match s.fields {
syn::Fields::Named(syn::FieldsNamed { named, .. }) => named
.into_iter()
Expand Down Expand Up @@ -70,9 +70,94 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
}
}
};

#[cfg(feature = "protobuf")]
let gen = {
let protobuf = derive_protobuf_encode(ast);
quote! {
#gen

#protobuf
}
};

gen.into()
}

#[cfg(feature = "protobuf")]
fn derive_protobuf_encode(ast: DeriveInput) -> TokenStream2 {
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 = {
let mut labels = vec![];
self.#ident.encode(&mut labels);
debug_assert_eq!(1, labels.len(), "Labels encoded from {} should have only one label.", #ident_string);
labels.pop().expect("should have an element")
};
// Override the label name with the field name of this struct.
label.name = #ident_string.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>) {
#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."),
}
}

// Copied from https://github.com/djc/askama (MIT and APACHE licensed) and
// modified.
static KEYWORD_IDENTIFIERS: [(&str, &str); 48] = [
Expand Down
Loading