Skip to content

Commit 16aa166

Browse files
authored
src/encoding/text.rs: Expose Encoder methods (#41)
By exposing the various `Encoder` builder methods, downstream users can implement their custom metrics using `EncodeMetric`. Showcase how to implement custom metric with example. Signed-off-by: Max Inden <mail@max-inden.de>
1 parent f7dd611 commit 16aa166

4 files changed

Lines changed: 80 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ 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.15.1] - [unreleased]
8+
9+
### Added
10+
11+
- Expose `Encoder` methods. See [PR 41].
12+
13+
[PR 41]: https://github.com/prometheus/client_rust/pull/41
14+
715
## [0.15.0] - 2022-01-16
816

917
### Changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "prometheus-client"
3-
version = "0.15.0"
3+
version = "0.15.1"
44
authors = ["Max Inden <mail@max-inden.de>"]
55
edition = "2018"
66
description = "Open Metrics client library allowing users to natively instrument applications."
@@ -24,8 +24,9 @@ async-std = { version = "1", features = ["attributes"] }
2424
criterion = "0.3"
2525
http-types = "2"
2626
pyo3 = "0.15"
27-
tide = "0.16"
2827
quickcheck = "1"
28+
rand = "0.8.4"
29+
tide = "0.16"
2930

3031
[[bench]]
3132
name = "family"

examples/custom-metric.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use prometheus_client::encoding::text::{encode, EncodeMetric, Encoder};
2+
use prometheus_client::metrics::MetricType;
3+
use prometheus_client::registry::Registry;
4+
5+
/// Showcasing encoding of custom metrics.
6+
///
7+
/// Related to the concept of "Custom Collectors" in other implementations.
8+
///
9+
/// [`MyCustomMetric`] generates and encodes a random number on each scrape.
10+
struct MyCustomMetric {}
11+
12+
impl EncodeMetric for MyCustomMetric {
13+
fn encode(&self, mut encoder: Encoder) -> Result<(), std::io::Error> {
14+
// This method is called on each Prometheus server scrape. Allowing you
15+
// to execute whatever logic is needed to generate and encode your
16+
// custom metric.
17+
//
18+
// While the `Encoder`'s builder pattern should guide you well and makes
19+
// many mistakes impossible at the type level, do keep in mind that
20+
// "with great power comes great responsibility". E.g. every CPU cycle
21+
// spend in this method delays the response send to the Prometheus
22+
// server.
23+
24+
encoder
25+
.no_suffix()?
26+
.no_bucket()?
27+
.encode_value(rand::random::<u32>())?
28+
.no_exemplar()?;
29+
30+
Ok(())
31+
}
32+
33+
fn metric_type(&self) -> prometheus_client::metrics::MetricType {
34+
MetricType::Unknown
35+
}
36+
}
37+
38+
fn main() {
39+
let mut registry = Registry::default();
40+
41+
let metric = MyCustomMetric {};
42+
registry.register(
43+
"my_custom_metric",
44+
"Custom metric returning a random number on each scrape",
45+
metric,
46+
);
47+
48+
let mut encoded = Vec::new();
49+
encode(&mut encoded, &registry).unwrap();
50+
51+
println!("Scrape output:\n{:?}", String::from_utf8(encoded).unwrap());
52+
}

src/encoding/text.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ impl Encode for () {
217217
}
218218
}
219219

220+
/// Helper type for [`EncodeMetric`], see [`EncodeMetric::encode`].
221+
///
220222
// `Encoder` does not take a trait parameter for `writer` and `labels` because
221223
// `EncodeMetric` which uses `Encoder` needs to be usable as a trait object in
222224
// order to be able to register different metric types with a `Registry`. Trait
@@ -232,6 +234,7 @@ pub struct Encoder<'a, 'b> {
232234
}
233235

234236
impl<'a, 'b> Encoder<'a, 'b> {
237+
/// Encode a metric suffix, e.g. in the case of [`Counter`] the suffic `_total`.
235238
pub fn encode_suffix(&mut self, suffix: &'static str) -> Result<BucketEncoder, std::io::Error> {
236239
self.write_name_and_unit()?;
237240

@@ -241,6 +244,7 @@ impl<'a, 'b> Encoder<'a, 'b> {
241244
self.encode_labels()
242245
}
243246

247+
/// Signal that the metric has no suffix.
244248
pub fn no_suffix(&mut self) -> Result<BucketEncoder, std::io::Error> {
245249
self.write_name_and_unit()?;
246250

@@ -285,6 +289,7 @@ impl<'a, 'b> Encoder<'a, 'b> {
285289
})
286290
}
287291

292+
/// Encode a set of labels. Used by wrapper metric types like [`Family`].
288293
pub fn with_label_set<'c, 'd>(&'c mut self, label_set: &'d dyn Encode) -> Encoder<'c, 'd> {
289294
debug_assert!(self.labels.is_none());
290295

@@ -305,7 +310,8 @@ pub struct BucketEncoder<'a> {
305310
}
306311

307312
impl<'a> BucketEncoder<'a> {
308-
fn encode_bucket(&mut self, upper_bound: f64) -> Result<ValueEncoder, std::io::Error> {
313+
/// Encode a bucket. Used for the [`Histogram`] metric type.
314+
pub fn encode_bucket(&mut self, upper_bound: f64) -> Result<ValueEncoder, std::io::Error> {
309315
if self.opened_curly_brackets {
310316
self.writer.write_all(b",")?;
311317
} else {
@@ -325,7 +331,8 @@ impl<'a> BucketEncoder<'a> {
325331
})
326332
}
327333

328-
fn no_bucket(&mut self) -> Result<ValueEncoder, std::io::Error> {
334+
/// Signal that the metric type has no bucket.
335+
pub fn no_bucket(&mut self) -> Result<ValueEncoder, std::io::Error> {
329336
if self.opened_curly_brackets {
330337
self.writer.write_all(b"}")?;
331338
}
@@ -341,7 +348,9 @@ pub struct ValueEncoder<'a> {
341348
}
342349

343350
impl<'a> ValueEncoder<'a> {
344-
fn encode_value<V: Encode>(&mut self, v: V) -> Result<ExemplarEncoder, std::io::Error> {
351+
/// Encode the metric value. E.g. in the case of [`Counter`] the
352+
/// monotonically increasing counter value.
353+
pub fn encode_value<V: Encode>(&mut self, v: V) -> Result<ExemplarEncoder, std::io::Error> {
345354
self.writer.write_all(b" ")?;
346355
v.encode(self.writer)?;
347356
Ok(ExemplarEncoder {
@@ -356,7 +365,8 @@ pub struct ExemplarEncoder<'a> {
356365
}
357366

358367
impl<'a> ExemplarEncoder<'a> {
359-
fn encode_exemplar<S: Encode, V: Encode>(
368+
/// Encode an exemplar for the given metric.
369+
pub fn encode_exemplar<S: Encode, V: Encode>(
360370
&mut self,
361371
exemplar: &Exemplar<S, V>,
362372
) -> Result<(), std::io::Error> {
@@ -368,12 +378,14 @@ impl<'a> ExemplarEncoder<'a> {
368378
Ok(())
369379
}
370380

371-
fn no_exemplar(&mut self) -> Result<(), std::io::Error> {
381+
/// Signal that the metric type has no exemplar.
382+
pub fn no_exemplar(&mut self) -> Result<(), std::io::Error> {
372383
self.writer.write_all(b"\n")?;
373384
Ok(())
374385
}
375386
}
376387

388+
/// Trait implemented by each metric type, e.g. [`Counter`], to implement its encoding.
377389
pub trait EncodeMetric {
378390
fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error>;
379391

0 commit comments

Comments
 (0)