Skip to content

Commit ffc2ab6

Browse files
ackintoshmxindendivagant-martian
authoredSep 11, 2022
encoding/: Add Protobuf support (prometheus#83)
Add support for the OpenMetrics Protobuf encoding. Co-authored-by: Max Inden <[email protected]> Co-authored-by: Diva M <[email protected]>
1 parent 16993b6 commit ffc2ab6

File tree

20 files changed

+1624
-102
lines changed

20 files changed

+1624
-102
lines changed
 

‎.github/workflows/rust.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- uses: actions-rs/cargo@v1
4040
with:
4141
command: test
42-
args: --benches
42+
args: --benches --all-features
4343

4444
test:
4545
name: Test Suite
@@ -73,7 +73,7 @@ jobs:
7373
- uses: actions-rs/cargo@v1
7474
with:
7575
command: test
76-
args: --all
76+
args: --all --all-features
7777

7878
fmt:
7979
name: Rustfmt
@@ -135,7 +135,7 @@ jobs:
135135
RUSTDOCFLAGS: "--deny broken_intra_doc_links"
136136
with:
137137
command: doc
138-
args: --verbose --workspace --no-deps --document-private-items
138+
args: --verbose --workspace --no-deps --document-private-items --all-features
139139

140140
cross-compile:
141141
name: Cross compile
@@ -159,4 +159,4 @@ jobs:
159159
with:
160160
use-cross: true
161161
command: build
162-
args: --release --target=${{ matrix.target }}
162+
args: --release --target=${{ matrix.target }} --all-features

‎CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.19.0] - unreleased
8+
9+
### Added
10+
- Added support for the OpenMetrics protobuf format. See [PR 83].
11+
12+
### Changed
13+
14+
- Move`Encode` trait from `prometheus_client::encoding::text` to `prometheus_client::encoding`. See [PR 83].
15+
16+
[PR 83]: https://github.com/prometheus/client_rust/pull/83
17+
718
## [0.18.0]
819

920
### Changed

‎Cargo.toml

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "prometheus-client"
3-
version = "0.18.0"
3+
version = "0.19.0"
44
authors = ["Max Inden <mail@max-inden.de>"]
55
edition = "2021"
66
description = "Open Metrics client library allowing users to natively instrument applications."
@@ -10,14 +10,20 @@ repository = "https://github.com/prometheus/client_rust"
1010
homepage = "https://github.com/prometheus/client_rust"
1111
documentation = "https://docs.rs/prometheus-client"
1212

13+
[features]
14+
protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build", "dep:void", "prometheus-client-derive-encode/protobuf"]
15+
1316
[workspace]
14-
members = ["derive-text-encode"]
17+
members = ["derive-encode"]
1518

1619
[dependencies]
1720
dtoa = "1.0"
1821
itoa = "1.0"
1922
parking_lot = "0.12"
20-
prometheus-client-derive-text-encode = { version = "0.3.0", path = "derive-text-encode" }
23+
prometheus-client-derive-encode = { version = "0.3.0", path = "derive-encode" }
24+
prost = { version = "0.9.0", optional = true }
25+
prost-types = { version = "0.9.0", optional = true }
26+
void = { version = "1.0", optional = true }
2127

2228
[dev-dependencies]
2329
async-std = { version = "1", features = ["attributes"] }
@@ -29,6 +35,9 @@ rand = "0.8.4"
2935
tide = "0.16"
3036
actix-web = "4"
3137

38+
[build-dependencies]
39+
prost-build = { version = "0.9.0", optional = true }
40+
3241
[[bench]]
3342
name = "family"
3443
harness = false
@@ -37,3 +46,10 @@ harness = false
3746
name = "text"
3847
path = "benches/encoding/text.rs"
3948
harness = false
49+
required-features = []
50+
51+
[[bench]]
52+
name = "proto"
53+
path = "benches/encoding/proto.rs"
54+
harness = false
55+
required-features = ["protobuf"]

‎benches/encoding/proto.rs

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Benchmark inspired by
2+
// https://github.com/tikv/rust-prometheus/blob/ab1ca7285d3463504381a5025ae1951e020d6796/benches/text_encoder.rs:write
3+
4+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
5+
use prometheus_client::encoding::proto::{encode, EncodeMetric};
6+
use prometheus_client::encoding::Encode;
7+
use prometheus_client::metrics::counter::Counter;
8+
use prometheus_client::metrics::family::Family;
9+
use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
10+
use prometheus_client::registry::Registry;
11+
use std::fmt::{Display, Formatter};
12+
13+
pub fn proto(c: &mut Criterion) {
14+
c.bench_function("encode", |b| {
15+
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
16+
struct Labels {
17+
path: String,
18+
method: Method,
19+
some_number: u64,
20+
}
21+
22+
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
23+
enum Method {
24+
Get,
25+
#[allow(dead_code)]
26+
Put,
27+
}
28+
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+
}
36+
}
37+
38+
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
39+
enum Region {
40+
Africa,
41+
#[allow(dead_code)]
42+
Asia,
43+
}
44+
45+
let mut registry = Registry::<Box<dyn EncodeMetric>>::default();
46+
47+
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+
});
52+
53+
registry.register(
54+
format!("my_counter{}", i),
55+
"My counter",
56+
Box::new(counter_family.clone()),
57+
);
58+
registry.register(
59+
format!("my_histogram{}", i),
60+
"My histogram",
61+
Box::new(histogram_family.clone()),
62+
);
63+
64+
for j in 0_u32..100 {
65+
counter_family
66+
.get_or_create(&Labels {
67+
path: format!("/path/{}", i),
68+
method: Method::Get,
69+
some_number: j.into(),
70+
})
71+
.inc();
72+
73+
histogram_family
74+
.get_or_create(&Region::Africa)
75+
.observe(j.into());
76+
}
77+
}
78+
79+
b.iter(|| {
80+
let metric_set = encode(&registry);
81+
black_box(metric_set);
82+
})
83+
});
84+
}
85+
86+
criterion_group!(benches, proto);
87+
criterion_main!(benches);

‎benches/encoding/text.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
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, Encode, EncodeMetric};
4+
use prometheus_client::encoding::text::{encode, EncodeMetric};
5+
use prometheus_client::encoding::Encode;
56
use prometheus_client::metrics::counter::Counter;
67
use prometheus_client::metrics::family::Family;
78
use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
@@ -33,7 +34,7 @@ pub fn text(c: &mut Criterion) {
3334
Five,
3435
}
3536

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

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+
4864
let mut registry = Registry::<Box<dyn EncodeMetric>>::default();
4965

5066
for i in 0..100 {

‎build.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use std::io::Result;
2+
3+
fn main() -> Result<()> {
4+
#[cfg(feature = "protobuf")]
5+
prost_build::compile_protos(
6+
&["src/encoding/proto/openmetrics_data_model.proto"],
7+
&["src/encoding/proto/"],
8+
)?;
9+
10+
Ok(())
11+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
[package]
2-
name = "prometheus-client-derive-text-encode"
3-
version = "0.3.0"
2+
name = "prometheus-client-derive-encode"
3+
version = "0.3.1"
44
authors = ["Max Inden <mail@max-inden.de>"]
55
edition = "2021"
6-
description = "Auxiliary crate to derive text Encode trait from prometheus-client."
6+
description = "Auxiliary crate to derive Encode trait from prometheus-client."
77
license = "Apache-2.0 OR MIT"
88
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+
1215
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1316

1417
[dependencies]
@@ -17,7 +20,7 @@ quote = "1"
1720
syn = "1"
1821

1922
[dev-dependencies]
20-
prometheus-client = { path = "../" }
23+
prometheus-client = { path = "../", features = ["protobuf"] }
2124

2225
[lib]
2326
proc-macro = true

‎derive-text-encode/src/lib.rs ‎derive-encode/src/lib.rs

+86-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
1010
let ast: DeriveInput = syn::parse(input).unwrap();
1111
let name = &ast.ident;
1212

13-
let body = match ast.data {
13+
let body = match ast.clone().data {
1414
syn::Data::Struct(s) => match s.fields {
1515
syn::Fields::Named(syn::FieldsNamed { named, .. }) => named
1616
.into_iter()
@@ -70,9 +70,94 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
7070
}
7171
}
7272
};
73+
74+
#[cfg(feature = "protobuf")]
75+
let gen = {
76+
let protobuf = derive_protobuf_encode(ast);
77+
quote! {
78+
#gen
79+
80+
#protobuf
81+
}
82+
};
83+
7384
gen.into()
7485
}
7586

87+
#[cfg(feature = "protobuf")]
88+
fn derive_protobuf_encode(ast: DeriveInput) -> TokenStream2 {
89+
let name = &ast.ident;
90+
91+
match ast.data {
92+
syn::Data::Struct(s) => match s.fields {
93+
syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
94+
let push_labels: TokenStream2 = named
95+
.into_iter()
96+
.map(|f| {
97+
let ident = f.ident.unwrap();
98+
let ident_string = KEYWORD_IDENTIFIERS
99+
.iter()
100+
.find(|pair| ident == pair.1)
101+
.map(|pair| pair.0.to_string())
102+
.unwrap_or_else(|| ident.to_string());
103+
104+
quote! {
105+
let mut label = {
106+
let mut labels = vec![];
107+
self.#ident.encode(&mut labels);
108+
debug_assert_eq!(1, labels.len(), "Labels encoded from {} should have only one label.", #ident_string);
109+
labels.pop().expect("should have an element")
110+
};
111+
// Override the label name with the field name of this struct.
112+
label.name = #ident_string.to_string();
113+
labels.push(label);
114+
}
115+
})
116+
.collect();
117+
118+
quote! {
119+
impl prometheus_client::encoding::proto::EncodeLabels for #name {
120+
fn encode(&self, labels: &mut Vec<prometheus_client::encoding::proto::Label>) {
121+
#push_labels
122+
}
123+
}
124+
}
125+
}
126+
syn::Fields::Unnamed(_) => {
127+
panic!("Can not derive Encode for struct with unnamed fields.")
128+
}
129+
syn::Fields::Unit => panic!("Can not derive Encode for struct with unit field."),
130+
},
131+
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
132+
let match_arms: TokenStream2 = variants
133+
.into_iter()
134+
.map(|v| {
135+
let ident = v.ident;
136+
quote! {
137+
#name::#ident => {
138+
let mut label = prometheus_client::encoding::proto::Label::default();
139+
label.name = stringify!(#name).to_string();
140+
label.value = stringify!(#ident).to_string();
141+
labels.push(label);
142+
}
143+
}
144+
})
145+
.collect();
146+
147+
quote! {
148+
impl prometheus_client::encoding::proto::EncodeLabels for #name {
149+
fn encode(&self, labels: &mut Vec<prometheus_client::encoding::proto::Label>) {
150+
match self {
151+
#match_arms
152+
};
153+
}
154+
}
155+
}
156+
}
157+
syn::Data::Union(_) => panic!("Can not derive Encode for union."),
158+
}
159+
}
160+
76161
// Copied from https://github.com/djc/askama (MIT and APACHE licensed) and
77162
// modified.
78163
static KEYWORD_IDENTIFIERS: [(&str, &str); 48] = [

‎derive-encode/tests/lib.rs

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use prometheus_client::encoding::text::encode;
2+
use prometheus_client::encoding::Encode;
3+
use prometheus_client::metrics::counter::Counter;
4+
use prometheus_client::metrics::family::Family;
5+
use prometheus_client::registry::Registry;
6+
7+
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
8+
struct Labels {
9+
method: Method,
10+
path: String,
11+
}
12+
13+
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
14+
enum Method {
15+
Get,
16+
#[allow(dead_code)]
17+
Put,
18+
}
19+
20+
#[test]
21+
fn basic_flow() {
22+
let mut registry = Registry::default();
23+
24+
let family = Family::<Labels, Counter>::default();
25+
registry.register("my_counter", "This is my counter", family.clone());
26+
27+
// Record a single HTTP GET request.
28+
family
29+
.get_or_create(&Labels {
30+
method: Method::Get,
31+
path: "/metrics".to_string(),
32+
})
33+
.inc();
34+
35+
// Encode all metrics in the registry in the text format.
36+
let mut buffer = vec![];
37+
encode(&mut buffer, &registry).unwrap();
38+
39+
let expected = "# HELP my_counter This is my counter.\n".to_owned()
40+
+ "# TYPE my_counter counter\n"
41+
+ "my_counter_total{method=\"Get\",path=\"/metrics\"} 1\n"
42+
+ "# EOF\n";
43+
assert_eq!(expected, String::from_utf8(buffer).unwrap());
44+
}
45+
46+
#[cfg(feature = "protobuf")]
47+
mod protobuf {
48+
use crate::{Labels, Method};
49+
use prometheus_client::encoding::proto::encode;
50+
use prometheus_client::metrics::counter::Counter;
51+
use prometheus_client::metrics::family::Family;
52+
use prometheus_client::registry::Registry;
53+
54+
#[test]
55+
fn structs() {
56+
let mut registry = Registry::default();
57+
let family = Family::<Labels, Counter>::default();
58+
registry.register("my_counter", "This is my counter", family.clone());
59+
60+
// Record a single HTTP GET request.
61+
family
62+
.get_or_create(&Labels {
63+
method: Method::Get,
64+
path: "/metrics".to_string(),
65+
})
66+
.inc();
67+
68+
// Encode all metrics in the registry in the OpenMetrics protobuf format.
69+
let mut metric_set = encode(&registry);
70+
let mut family: prometheus_client::encoding::proto::MetricFamily =
71+
metric_set.metric_families.pop().unwrap();
72+
let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap();
73+
74+
let method = &metric.labels[0];
75+
assert_eq!("method", method.name);
76+
assert_eq!("Get", method.value);
77+
78+
let path = &metric.labels[1];
79+
assert_eq!("path", path.name);
80+
assert_eq!("/metrics", path.value);
81+
}
82+
83+
#[test]
84+
fn enums() {
85+
let mut registry = Registry::default();
86+
let family = Family::<Method, Counter>::default();
87+
registry.register("my_counter", "This is my counter", family.clone());
88+
89+
// Record a single HTTP GET request.
90+
family.get_or_create(&Method::Get).inc();
91+
92+
// Encode all metrics in the registry in the OpenMetrics protobuf format.
93+
let mut metric_set = encode(&registry);
94+
let mut family: prometheus_client::encoding::proto::MetricFamily =
95+
metric_set.metric_families.pop().unwrap();
96+
let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap();
97+
98+
let label = &metric.labels[0];
99+
assert_eq!("Method", label.name);
100+
assert_eq!("Get", label.value);
101+
}
102+
}
103+
104+
#[test]
105+
fn remap_keyword_identifiers() {
106+
#[derive(Encode, Hash, Clone, Eq, PartialEq)]
107+
struct Labels {
108+
// `r#type` is problematic as `r#` is not a valid OpenMetrics label name
109+
// but one needs to use keyword identifier syntax (aka. raw identifiers)
110+
// as `type` is a keyword.
111+
//
112+
// Test makes sure `r#type` is replaced by `type` in the OpenMetrics
113+
// output.
114+
r#type: u64,
115+
}
116+
117+
let labels = Labels { r#type: 42 };
118+
119+
let mut buffer = vec![];
120+
121+
{
122+
use prometheus_client::encoding::text::Encode;
123+
labels.encode(&mut buffer).unwrap();
124+
}
125+
126+
assert_eq!(
127+
"type=\"42\"".to_string(),
128+
String::from_utf8(buffer).unwrap()
129+
);
130+
}

‎derive-text-encode/tests/lib.rs

-67
This file was deleted.

‎examples/actix-web.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::sync::Mutex;
22

33
use actix_web::{web, App, HttpResponse, HttpServer, Responder, Result};
4-
use prometheus_client::encoding::text::{encode, Encode};
4+
use prometheus_client::encoding::text::encode;
5+
use prometheus_client::encoding::Encode;
56
use prometheus_client::metrics::counter::Counter;
67
use prometheus_client::metrics::family::Family;
78
use prometheus_client::registry::Registry;

‎examples/tide.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use prometheus_client::encoding::text::{encode, Encode};
1+
use prometheus_client::encoding::text::encode;
2+
use prometheus_client::encoding::Encode;
23
use prometheus_client::metrics::counter::Counter;
34
use prometheus_client::metrics::family::Family;
45
use prometheus_client::registry::Registry;

‎src/encoding.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
//! Exposition format implementations.
22
3+
pub use prometheus_client_derive_encode::*;
4+
5+
#[cfg(feature = "protobuf")]
6+
pub mod proto;
37
pub mod text;

‎src/encoding/proto.rs

+980
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
syntax = "proto3";
2+
3+
// The OpenMetrics protobuf schema which defines the protobuf wire format.
4+
// Ensure to interpret "required" as semantically required for a valid message.
5+
// All string fields MUST be UTF-8 encoded strings.
6+
package openmetrics;
7+
8+
import "google/protobuf/timestamp.proto";
9+
10+
// The top-level container type that is encoded and sent over the wire.
11+
message MetricSet {
12+
// Each MetricFamily has one or more MetricPoints for a single Metric.
13+
repeated MetricFamily metric_families = 1;
14+
}
15+
16+
// One or more Metrics for a single MetricFamily, where each Metric
17+
// has one or more MetricPoints.
18+
message MetricFamily {
19+
// Required.
20+
string name = 1;
21+
22+
// Optional.
23+
MetricType type = 2;
24+
25+
// Optional.
26+
string unit = 3;
27+
28+
// Optional.
29+
string help = 4;
30+
31+
// Optional.
32+
repeated Metric metrics = 5;
33+
}
34+
35+
// The type of a Metric.
36+
enum MetricType {
37+
// Unknown must use unknown MetricPoint values.
38+
UNKNOWN = 0;
39+
// Gauge must use gauge MetricPoint values.
40+
GAUGE = 1;
41+
// Counter must use counter MetricPoint values.
42+
COUNTER = 2;
43+
// State set must use state set MetricPoint values.
44+
STATE_SET = 3;
45+
// Info must use info MetricPoint values.
46+
INFO = 4;
47+
// Histogram must use histogram value MetricPoint values.
48+
HISTOGRAM = 5;
49+
// Gauge histogram must use histogram value MetricPoint values.
50+
GAUGE_HISTOGRAM = 6;
51+
// Summary quantiles must use summary value MetricPoint values.
52+
SUMMARY = 7;
53+
}
54+
55+
// A single metric with a unique set of labels within a metric family.
56+
message Metric {
57+
// Optional.
58+
repeated Label labels = 1;
59+
60+
// Optional.
61+
repeated MetricPoint metric_points = 2;
62+
}
63+
64+
// A name-value pair. These are used in multiple places: identifying
65+
// timeseries, value of INFO metrics, and exemplars in Histograms.
66+
message Label {
67+
// Required.
68+
string name = 1;
69+
70+
// Required.
71+
string value = 2;
72+
}
73+
74+
// A MetricPoint in a Metric.
75+
message MetricPoint {
76+
// Required.
77+
oneof value {
78+
UnknownValue unknown_value = 1;
79+
GaugeValue gauge_value = 2;
80+
CounterValue counter_value = 3;
81+
HistogramValue histogram_value = 4;
82+
StateSetValue state_set_value = 5;
83+
InfoValue info_value = 6;
84+
SummaryValue summary_value = 7;
85+
}
86+
87+
// Optional.
88+
google.protobuf.Timestamp timestamp = 8;
89+
}
90+
91+
// Value for UNKNOWN MetricPoint.
92+
message UnknownValue {
93+
// Required.
94+
oneof value {
95+
double double_value = 1;
96+
int64 int_value = 2;
97+
}
98+
}
99+
100+
// Value for GAUGE MetricPoint.
101+
message GaugeValue {
102+
// Required.
103+
oneof value {
104+
double double_value = 1;
105+
int64 int_value = 2;
106+
}
107+
}
108+
109+
// Value for COUNTER MetricPoint.
110+
message CounterValue {
111+
// Required.
112+
oneof total {
113+
double double_value = 1;
114+
uint64 int_value = 2;
115+
}
116+
117+
// The time values began being collected for this counter.
118+
// Optional.
119+
google.protobuf.Timestamp created = 3;
120+
121+
// Optional.
122+
Exemplar exemplar = 4;
123+
}
124+
125+
// Value for HISTOGRAM or GAUGE_HISTOGRAM MetricPoint.
126+
message HistogramValue {
127+
// Optional.
128+
oneof sum {
129+
double double_value = 1;
130+
int64 int_value = 2;
131+
}
132+
133+
// Optional.
134+
uint64 count = 3;
135+
136+
// The time values began being collected for this histogram.
137+
// Optional.
138+
google.protobuf.Timestamp created = 4;
139+
140+
// Optional.
141+
repeated Bucket buckets = 5;
142+
143+
// Bucket is the number of values for a bucket in the histogram
144+
// with an optional exemplar.
145+
message Bucket {
146+
// Required.
147+
uint64 count = 1;
148+
149+
// Optional.
150+
double upper_bound = 2;
151+
152+
// Optional.
153+
Exemplar exemplar = 3;
154+
}
155+
}
156+
157+
message Exemplar {
158+
// Required.
159+
double value = 1;
160+
161+
// Optional.
162+
google.protobuf.Timestamp timestamp = 2;
163+
164+
// Labels are additional information about the exemplar value (e.g. trace id).
165+
// Optional.
166+
repeated Label label = 3;
167+
}
168+
169+
// Value for STATE_SET MetricPoint.
170+
message StateSetValue {
171+
// Optional.
172+
repeated State states = 1;
173+
174+
message State {
175+
// Required.
176+
bool enabled = 1;
177+
178+
// Required.
179+
string name = 2;
180+
}
181+
}
182+
183+
// Value for INFO MetricPoint.
184+
message InfoValue {
185+
// Optional.
186+
repeated Label info = 1;
187+
}
188+
189+
// Value for SUMMARY MetricPoint.
190+
message SummaryValue {
191+
// Optional.
192+
oneof sum {
193+
double double_value = 1;
194+
int64 int_value = 2;
195+
}
196+
197+
// Optional.
198+
uint64 count = 3;
199+
200+
// The time sum and count values began being collected for this summary.
201+
// Optional.
202+
google.protobuf.Timestamp created = 4;
203+
204+
// Optional.
205+
repeated Quantile quantile = 5;
206+
207+
message Quantile {
208+
// Required.
209+
double quantile = 1;
210+
211+
// Required.
212+
double value = 2;
213+
}
214+
}

‎src/encoding/text.rs

+1-16
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ use std::collections::HashMap;
3838
use std::io::Write;
3939
use std::ops::Deref;
4040

41-
pub use prometheus_client_derive_text_encode::*;
42-
4341
/// Encode the metrics registered with the provided [`Registry`] into the
4442
/// provided [`Write`]r using the OpenMetrics text format.
4543
pub fn encode<W, M>(writer: &mut W, registry: &Registry<M>) -> Result<(), std::io::Error>
@@ -197,20 +195,7 @@ impl Encode for MetricType {
197195

198196
impl Encode for Unit {
199197
fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> {
200-
let u = match self {
201-
Unit::Amperes => "amperes",
202-
Unit::Bytes => "bytes",
203-
Unit::Celsius => "celsius",
204-
Unit::Grams => "grams",
205-
Unit::Joules => "joules",
206-
Unit::Meters => "meters",
207-
Unit::Ratios => "ratios",
208-
Unit::Seconds => "seconds",
209-
Unit::Volts => "volts",
210-
Unit::Other(other) => other.as_str(),
211-
};
212-
213-
writer.write_all(u.as_bytes())?;
198+
writer.write_all(self.as_str().as_bytes())?;
214199
Ok(())
215200
}
216201
}

‎src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//! # Examples
1414
//!
1515
//! ```
16-
//! use prometheus_client::encoding::text::Encode;
16+
//! use prometheus_client::encoding::Encode;
1717
//! use prometheus_client::encoding::text::encode;
1818
//! use prometheus_client::metrics::counter::{Atomic, Counter};
1919
//! use prometheus_client::metrics::family::Family;

‎src/metrics/family.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use std::sync::Arc;
5757
/// [`Encode`](crate::encoding::text::Encode) implementation.
5858
///
5959
/// ```
60-
/// # use prometheus_client::encoding::text::Encode;
60+
/// # use prometheus_client::encoding::Encode;
6161
/// # use prometheus_client::encoding::text::encode;
6262
/// # use prometheus_client::metrics::counter::{Atomic, Counter};
6363
/// # use prometheus_client::metrics::family::Family;

‎src/metrics/gauge.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use super::{MetricType, TypedMetric};
66
use std::marker::PhantomData;
77
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
8-
use std::sync::atomic::AtomicU64;
8+
use std::sync::atomic::{AtomicI64, AtomicU64};
99
use std::sync::atomic::{AtomicU32, Ordering};
1010
use std::sync::Arc;
1111

@@ -234,6 +234,33 @@ impl Atomic<f64> for AtomicU64 {
234234
}
235235
}
236236

237+
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
238+
impl Atomic<i64> for AtomicI64 {
239+
fn inc(&self) -> i64 {
240+
self.inc_by(1)
241+
}
242+
243+
fn inc_by(&self, v: i64) -> i64 {
244+
self.fetch_add(v, Ordering::Relaxed)
245+
}
246+
247+
fn dec(&self) -> i64 {
248+
self.dec_by(1)
249+
}
250+
251+
fn dec_by(&self, v: i64) -> i64 {
252+
self.fetch_sub(v, Ordering::Relaxed)
253+
}
254+
255+
fn set(&self, v: i64) -> i64 {
256+
self.swap(v, Ordering::Relaxed)
257+
}
258+
259+
fn get(&self) -> i64 {
260+
self.load(Ordering::Relaxed)
261+
}
262+
}
263+
237264
impl<N, A> TypedMetric for Gauge<N, A> {
238265
const TYPE: MetricType = MetricType::Gauge;
239266
}

‎src/registry.rs

+18
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,24 @@ pub enum Unit {
347347
Other(String),
348348
}
349349

350+
impl Unit {
351+
/// Returns the given Unit's str representation.
352+
pub fn as_str(&self) -> &str {
353+
match self {
354+
Unit::Amperes => "amperes",
355+
Unit::Bytes => "bytes",
356+
Unit::Celsius => "celsius",
357+
Unit::Grams => "grams",
358+
Unit::Joules => "joules",
359+
Unit::Meters => "meters",
360+
Unit::Ratios => "ratios",
361+
Unit::Seconds => "seconds",
362+
Unit::Volts => "volts",
363+
Unit::Other(other) => other.as_str(),
364+
}
365+
}
366+
}
367+
350368
#[cfg(test)]
351369
mod tests {
352370
use super::*;

0 commit comments

Comments
 (0)
Please sign in to comment.