Skip to content

Commit

Permalink
src/registry: Use dynamic dispatch and remove type parameter M
Browse files Browse the repository at this point in the history
Instead of being generic over the metric type on `Registry`, use dynamic
dispatch.

On the upside this significantly simplifies type signatures exposed to the user.
On the downside this requires users to always use dynamic dispatch and requires
all metrics to implement auxiliary trait `Debug` and auto traits `Send` and
`Sync`.
  • Loading branch information
mxinden committed Sep 29, 2022
1 parent ffc2ab6 commit c1bb0e2
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 83 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Always use dynamic dispatch on Registry, i.e. remove generic type parameter `M` from `Registry`.

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

[PR 83]: https://github.com/prometheus/client_rust/pull/83
Expand Down
12 changes: 5 additions & 7 deletions examples/actix-web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use prometheus_client::metrics::counter::Counter;
use prometheus_client::metrics::family::Family;
use prometheus_client::registry::Registry;

#[derive(Clone, Hash, PartialEq, Eq, Encode)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
pub enum Method {
Get,
Post,
}

#[derive(Clone, Hash, PartialEq, Eq, Encode)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
pub struct MethodLabels {
pub method: Method,
}
Expand Down Expand Up @@ -55,11 +55,9 @@ async fn main() -> std::io::Result<()> {
let mut state = AppState {
registry: Registry::default(),
};
state.registry.register(
"requests",
"Count of requests",
Box::new(metrics.requests.clone()),
);
state
.registry
.register("requests", "Count of requests", metrics.requests.clone());
let state = web::Data::new(Mutex::new(state));

HttpServer::new(move || {
Expand Down
1 change: 1 addition & 0 deletions examples/custom-metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use prometheus_client::registry::Registry;
/// Related to the concept of "Custom Collectors" in other implementations.
///
/// [`MyCustomMetric`] generates and encodes a random number on each scrape.
#[derive(Debug)]
struct MyCustomMetric {}

impl EncodeMetric for MyCustomMetric {
Expand Down
6 changes: 3 additions & 3 deletions examples/tide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,21 @@ async fn main() -> std::result::Result<(), std::io::Error> {
Ok(())
}

#[derive(Clone, Hash, PartialEq, Eq, Encode)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
struct Labels {
method: Method,
path: String,
}

#[derive(Clone, Hash, PartialEq, Eq, Encode)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
enum Method {
Get,
Put,
}

#[derive(Clone)]
struct State {
registry: Arc<Registry<Family<Labels, Counter>>>,
registry: Arc<Registry>,
}

#[derive(Default)]
Expand Down
12 changes: 4 additions & 8 deletions src/encoding/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub use prometheus_client_derive_encode::*;

/// Encode the metrics registered with the provided [`Registry`] into MetricSet
/// using the OpenMetrics protobuf format.
pub fn encode<M>(registry: &Registry<M>) -> openmetrics_data_model::MetricSet
pub fn encode<M>(registry: &Registry) -> openmetrics_data_model::MetricSet
where
M: EncodeMetric,
{
Expand All @@ -57,7 +57,8 @@ where
let mut family = openmetrics_data_model::MetricFamily {
name: desc.name().to_string(),
r#type: {
let metric_type: openmetrics_data_model::MetricType = metric.metric_type().into();
let metric_type: openmetrics_data_model::MetricType =
EncodeMetric::metric_type(metric.as_ref()).into();
metric_type as i32
},
unit: if let Some(unit) = desc.unit() {
Expand All @@ -71,7 +72,7 @@ where

let mut labels = vec![];
desc.labels().encode(&mut labels);
metric.encode(labels, &mut family.metrics);
EncodeMetric::encode(metric.as_ref(), labels, &mut family.metrics);

metric_set.metric_families.push(family);
}
Expand Down Expand Up @@ -118,11 +119,6 @@ impl EncodeMetric for Box<dyn EncodeMetric> {
}
}

/// Trait combining [`EncodeMetric`] and [`Send`].
pub trait SendEncodeMetric: EncodeMetric + Send {}

impl<T: EncodeMetric + Send> SendEncodeMetric for T {}

/// Trait to implement its label encoding in the OpenMetrics protobuf format.
pub trait EncodeLabels {
/// Encode the given instance into Labels in the OpenMetrics protobuf
Expand Down
22 changes: 3 additions & 19 deletions src/encoding/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@ use std::ops::Deref;

/// Encode the metrics registered with the provided [`Registry`] into the
/// provided [`Write`]r using the OpenMetrics text format.
pub fn encode<W, M>(writer: &mut W, registry: &Registry<M>) -> Result<(), std::io::Error>
pub fn encode<W>(writer: &mut W, registry: &Registry) -> Result<(), std::io::Error>
where
W: Write,
M: EncodeMetric,
{
for (desc, metric) in registry.iter() {
writer.write_all(b"# HELP ")?;
Expand All @@ -63,7 +62,7 @@ where
unit.encode(writer)?;
}
writer.write_all(b" ")?;
metric.metric_type().encode(writer)?;
EncodeMetric::metric_type(metric.as_ref()).encode(writer)?;
writer.write_all(b"\n")?;

if let Some(unit) = desc.unit() {
Expand All @@ -84,7 +83,7 @@ where
labels: None,
};

metric.encode(encoder)?;
EncodeMetric::encode(metric.as_ref(), encoder)?;
}

writer.write_all(b"# EOF\n")?;
Expand Down Expand Up @@ -402,21 +401,6 @@ impl EncodeMetric for Box<dyn EncodeMetric> {
}
}

/// Trait combining [`EncodeMetric`], [`Send`] and [`Sync`].
pub trait SendSyncEncodeMetric: EncodeMetric + Send + Sync {}

impl<T: EncodeMetric + Send + Sync> SendSyncEncodeMetric for T {}

impl EncodeMetric for Box<dyn SendSyncEncodeMetric> {
fn encode(&self, encoder: Encoder) -> Result<(), std::io::Error> {
self.deref().encode(encoder)
}

fn metric_type(&self) -> MetricType {
self.deref().metric_type()
}
}

/////////////////////////////////////////////////////////////////////////////////
// Counter

Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
//! //
//! // You could as well use `(String, String)` to represent a label set,
//! // instead of the custom type below.
//! #[derive(Clone, Hash, PartialEq, Eq, Encode)]
//! #[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
//! struct Labels {
//! // Use your own enum types to represent label values.
//! method: Method,
//! // Or just a plain string.
//! path: String,
//! };
//!
//! #[derive(Clone, Hash, PartialEq, Eq, Encode)]
//! #[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
//! enum Method {
//! GET,
//! PUT,
Expand All @@ -54,7 +54,7 @@
//! "http_requests",
//! // And the metric help text.
//! "Number of HTTP requests received",
//! Box::new(http_requests.clone()),
//! http_requests.clone(),
//! );
//!
//! // Somewhere in your business logic record a single HTTP GET request.
Expand Down
13 changes: 10 additions & 3 deletions src/metrics/family.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ use std::sync::Arc;
/// # use std::io::Write;
/// #
/// # let mut registry = Registry::default();
/// #[derive(Clone, Hash, PartialEq, Eq, Encode)]
/// #[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
/// struct Labels {
/// method: Method,
/// };
///
/// #[derive(Clone, Hash, PartialEq, Eq, Encode)]
/// #[derive(Clone, Debug, Hash, PartialEq, Eq, Encode)]
/// enum Method {
/// GET,
/// PUT,
Expand All @@ -97,7 +97,6 @@ use std::sync::Arc;
/// # assert_eq!(expected, String::from_utf8(buffer).unwrap());
/// ```
// TODO: Consider exposing hash algorithm.
#[derive(Debug)]
pub struct Family<S, M, C = fn() -> M> {
metrics: Arc<RwLock<HashMap<S, M>>>,
/// Function that when called constructs a new metric.
Expand All @@ -111,6 +110,14 @@ pub struct Family<S, M, C = fn() -> M> {
constructor: C,
}

impl<S: std::fmt::Debug, M: std::fmt::Debug, C> std::fmt::Debug for Family<S, M, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Family")
.field("metrics", &self.metrics)
.finish()
}
}

/// A constructor for creating new metrics in a [`Family`] when calling
/// [`Family::get_or_create`]. Such constructor is provided via
/// [`Family::new_with_constructor`].
Expand Down
Loading

0 comments on commit c1bb0e2

Please sign in to comment.