From b78dbaf00bb53b6dc6ffe9402c8393589f2530ad Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Feb 2022 23:15:29 +0900 Subject: [PATCH 01/73] Rough implementation to encode Counter Compile error: error[E0277]: the trait bound `Box: proto::EncodeMetric` is not satisfied --> src/encoding/proto.rs:151:33 | 151 | println!("{:?}", encode(®istry)); | ------ ^^^^^^^^^ the trait `proto::EncodeMetric` is not implemented for `Box` | | | required by a bound introduced by this call Signed-off-by: ackintosh --- Cargo.toml | 5 + build.rs | 6 + src/encoding.rs | 1 + src/encoding/proto.rs | 156 +++++++++++++ .../proto/openmetrics_data_model.proto | 214 ++++++++++++++++++ 5 files changed, 382 insertions(+) create mode 100644 build.rs create mode 100644 src/encoding/proto.rs create mode 100644 src/encoding/proto/openmetrics_data_model.proto diff --git a/Cargo.toml b/Cargo.toml index 647b61e1..7b2d4264 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ dtoa = "1.0" itoa = "1.0" parking_lot = "0.12" prometheus-client-derive-text-encode = { version = "0.3.0", path = "derive-text-encode" } +prost = "0.9.0" +prost-types = "0.9.0" [dev-dependencies] async-std = { version = "1", features = ["attributes"] } @@ -29,6 +31,9 @@ rand = "0.8.4" tide = "0.16" actix-web = "4" +[build-dependencies] +prost-build = "0.9.0" + [[bench]] name = "family" harness = false diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..44c54667 --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +use std::io::Result; + +fn main() -> Result<()> { + prost_build::compile_protos(&["src/encoding/proto/openmetrics_data_model.proto"], &["src/encoding/proto/"])?; + Ok(()) +} diff --git a/src/encoding.rs b/src/encoding.rs index 07149d48..084e5100 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,3 +1,4 @@ //! Exposition format implementations. +pub mod proto; pub mod text; diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs new file mode 100644 index 00000000..ee0a945d --- /dev/null +++ b/src/encoding/proto.rs @@ -0,0 +1,156 @@ +// Include the `openmetrics_data_model` module, which is generated from `proto/openmetrics_data_model.proto`. +pub mod openmetrics_data_model { + include!(concat!(env!("OUT_DIR"), "/openmetrics.rs")); +} + +use std::ops::Deref; +use crate::registry::{Registry, Unit}; +use crate::metrics::counter::Counter; +use crate::metrics::MetricType; + +pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet +where + M: EncodeMetric, +{ + // MetricSet + let mut metric_set = openmetrics_data_model::MetricSet::default(); + + for (desc, metric) in registry.iter() { + // MetricFamily + let mut family = openmetrics_data_model::MetricFamily::default(); + // MetricFamily.name + family.name = desc.name().to_string(); + // MetricFamily.type + family.r#type = { + let metric_type: openmetrics_data_model::MetricType = metric.metric_type().into(); + metric_type as i32 + }; + // MetricFamily.unit + if let Some(unit) = desc.unit() { + family.unit = match unit { + Unit::Amperes => "amperes", + Unit::Bytes => "bytes", + Unit::Celsius => "celsius", + Unit::Grams => "grams", + Unit::Joules => "joules", + Unit::Meters => "meters", + Unit::Ratios => "ratios", + Unit::Seconds => "seconds", + Unit::Volts => "volts", + Unit::Other(other) => other.as_str(), + }.to_string(); + } + // MetricFamily.help + family.help = desc.help().to_string(); + // Metric + let mut m = openmetrics_data_model::Metric::default(); + // Metric.labels + for l in desc.labels() { + let mut label = openmetrics_data_model::Label::default(); + label.name = l.0.to_string(); + label.value = l.1.to_string(); + m.labels.push(label); + } + // Metric.metric_points + m.metric_points.push(metric.encode()); + + family.metrics.push(m); + metric_set.metric_families.push(family); + } + + metric_set +} + +impl From for openmetrics_data_model::MetricType { + fn from(m: MetricType) -> Self { + match m { + MetricType::Counter => openmetrics_data_model::MetricType::Counter, + MetricType::Gauge => openmetrics_data_model::MetricType::Gauge, + MetricType::Histogram => openmetrics_data_model::MetricType::Histogram, + MetricType::Info => openmetrics_data_model::MetricType::Info, + MetricType::Unknown => openmetrics_data_model::MetricType::Unknown, + } + } +} + +/// Trait implemented by each metric type, e.g. [`Counter`], to implement its encoding. +pub trait EncodeMetric { + fn encode(&self) -> openmetrics_data_model::MetricPoint; + + fn metric_type(&self) -> MetricType; +} + +impl EncodeMetric for Box { + fn encode(&self) -> openmetrics_data_model::MetricPoint { + self.deref().encode() + } + + fn metric_type(&self) -> MetricType { + self.deref().metric_type() + } +} + +pub trait SendEncodeMetric: EncodeMetric + Send {} + +impl SendEncodeMetric for T {} + +impl EncodeMetric for Box { + fn encode(&self) -> openmetrics_data_model::MetricPoint { + self.deref().encode() + } + + fn metric_type(&self) -> MetricType { + self.deref().metric_type() + } +} + +///////////////////////////////////////////////////////////////////////////////// +// Counter + +impl EncodeMetric for Counter { + fn encode(&self) -> openmetrics_data_model::MetricPoint { + let metric_point = openmetrics_data_model::MetricPoint::default(); + // TODO: value + metric_point + } + + fn metric_type(&self) -> MetricType { + MetricType::Counter + } +} + +#[cfg(test)] +mod tests { + use std::borrow::Cow; + use crate::metrics::counter::Counter; + use crate::metrics::family::Family; + use crate::registry::Unit; + use super::*; + + #[test] + fn test_encode() { + let mut registry = ::default(); + + let counter: Counter = Counter::default(); + registry.register_with_unit("my_counter", "My counter", Unit::Seconds, Box::new(counter.clone())); + counter.inc(); + + let family = Family::, Counter>::default(); + let sub_registry = registry.sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value"))); + sub_registry.register("my_counter_family", "My counter family", Box::new(family.clone())); + family + .get_or_create(&vec![ + ("method".to_string(), "GET".to_string()), + ("status".to_string(), "200".to_string()), + ]) + .inc(); + family + .get_or_create(&vec![ + ("method".to_string(), "POST".to_string()), + ("status".to_string(), "503".to_string()), + ]) + .inc(); + + println!("{:?}", encode(®istry)); + } +} diff --git a/src/encoding/proto/openmetrics_data_model.proto b/src/encoding/proto/openmetrics_data_model.proto new file mode 100644 index 00000000..a95942d9 --- /dev/null +++ b/src/encoding/proto/openmetrics_data_model.proto @@ -0,0 +1,214 @@ +syntax = "proto3"; + +// The OpenMetrics protobuf schema which defines the protobuf wire format. +// Ensure to interpret "required" as semantically required for a valid message. +// All string fields MUST be UTF-8 encoded strings. +package openmetrics; + +import "google/protobuf/timestamp.proto"; + +// The top-level container type that is encoded and sent over the wire. +message MetricSet { + // Each MetricFamily has one or more MetricPoints for a single Metric. + repeated MetricFamily metric_families = 1; +} + +// One or more Metrics for a single MetricFamily, where each Metric +// has one or more MetricPoints. +message MetricFamily { + // Required. + string name = 1; + + // Optional. + MetricType type = 2; + + // Optional. + string unit = 3; + + // Optional. + string help = 4; + + // Optional. + repeated Metric metrics = 5; +} + +// The type of a Metric. +enum MetricType { + // Unknown must use unknown MetricPoint values. + UNKNOWN = 0; + // Gauge must use gauge MetricPoint values. + GAUGE = 1; + // Counter must use counter MetricPoint values. + COUNTER = 2; + // State set must use state set MetricPoint values. + STATE_SET = 3; + // Info must use info MetricPoint values. + INFO = 4; + // Histogram must use histogram value MetricPoint values. + HISTOGRAM = 5; + // Gauge histogram must use histogram value MetricPoint values. + GAUGE_HISTOGRAM = 6; + // Summary quantiles must use summary value MetricPoint values. + SUMMARY = 7; +} + +// A single metric with a unique set of labels within a metric family. +message Metric { + // Optional. + repeated Label labels = 1; + + // Optional. + repeated MetricPoint metric_points = 2; +} + +// A name-value pair. These are used in multiple places: identifying +// timeseries, value of INFO metrics, and exemplars in Histograms. +message Label { + // Required. + string name = 1; + + // Required. + string value = 2; +} + +// A MetricPoint in a Metric. +message MetricPoint { + // Required. + oneof value { + UnknownValue unknown_value = 1; + GaugeValue gauge_value = 2; + CounterValue counter_value = 3; + HistogramValue histogram_value = 4; + StateSetValue state_set_value = 5; + InfoValue info_value = 6; + SummaryValue summary_value = 7; + } + + // Optional. + google.protobuf.Timestamp timestamp = 8; +} + +// Value for UNKNOWN MetricPoint. +message UnknownValue { + // Required. + oneof value { + double double_value = 1; + int64 int_value = 2; + } +} + +// Value for GAUGE MetricPoint. +message GaugeValue { + // Required. + oneof value { + double double_value = 1; + int64 int_value = 2; + } +} + +// Value for COUNTER MetricPoint. +message CounterValue { + // Required. + oneof total { + double double_value = 1; + uint64 int_value = 2; + } + + // The time values began being collected for this counter. + // Optional. + google.protobuf.Timestamp created = 3; + + // Optional. + Exemplar exemplar = 4; +} + +// Value for HISTOGRAM or GAUGE_HISTOGRAM MetricPoint. +message HistogramValue { + // Optional. + oneof sum { + double double_value = 1; + int64 int_value = 2; + } + + // Optional. + uint64 count = 3; + + // The time values began being collected for this histogram. + // Optional. + google.protobuf.Timestamp created = 4; + + // Optional. + repeated Bucket buckets = 5; + + // Bucket is the number of values for a bucket in the histogram + // with an optional exemplar. + message Bucket { + // Required. + uint64 count = 1; + + // Optional. + double upper_bound = 2; + + // Optional. + Exemplar exemplar = 3; + } +} + +message Exemplar { + // Required. + double value = 1; + + // Optional. + google.protobuf.Timestamp timestamp = 2; + + // Labels are additional information about the exemplar value (e.g. trace id). + // Optional. + repeated Label label = 3; +} + +// Value for STATE_SET MetricPoint. +message StateSetValue { + // Optional. + repeated State states = 1; + + message State { + // Required. + bool enabled = 1; + + // Required. + string name = 2; + } +} + +// Value for INFO MetricPoint. +message InfoValue { + // Optional. + repeated Label info = 1; +} + +// Value for SUMMARY MetricPoint. +message SummaryValue { + // Optional. + oneof sum { + double double_value = 1; + int64 int_value = 2; + } + + // Optional. + uint64 count = 3; + + // The time sum and count values began being collected for this summary. + // Optional. + google.protobuf.Timestamp created = 4; + + // Optional. + repeated Quantile quantile = 5; + + message Quantile { + // Required. + double quantile = 1; + + // Required. + double value = 2; + } +} From e2e96f9a1622a1f099008e099cbd41d64809de4e Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 28 Feb 2022 18:33:46 +0100 Subject: [PATCH 02/73] src/encoding/proto: Construct Registry with proto::EncodeMetric Signed-off-by: ackintosh --- src/encoding/proto.rs | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index ee0a945d..f1e79030 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -3,10 +3,11 @@ pub mod openmetrics_data_model { include!(concat!(env!("OUT_DIR"), "/openmetrics.rs")); } -use std::ops::Deref; -use crate::registry::{Registry, Unit}; use crate::metrics::counter::Counter; +use crate::metrics::family::Family; use crate::metrics::MetricType; +use crate::registry::{Registry, Unit}; +use std::ops::Deref; pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet where @@ -38,7 +39,8 @@ where Unit::Seconds => "seconds", Unit::Volts => "volts", Unit::Other(other) => other.as_str(), - }.to_string(); + } + .to_string(); } // MetricFamily.help family.help = desc.help().to_string(); @@ -119,25 +121,45 @@ impl EncodeMetric for Counter { } } +impl EncodeMetric for Family { + fn encode(&self) -> openmetrics_data_model::MetricPoint { + todo!() + } + + fn metric_type(&self) -> MetricType { + todo!() + } +} + #[cfg(test)] mod tests { - use std::borrow::Cow; + use super::*; use crate::metrics::counter::Counter; use crate::metrics::family::Family; use crate::registry::Unit; - use super::*; + use std::borrow::Cow; #[test] fn test_encode() { - let mut registry = ::default(); + let mut registry: Registry> = Registry::default(); let counter: Counter = Counter::default(); - registry.register_with_unit("my_counter", "My counter", Unit::Seconds, Box::new(counter.clone())); + registry.register_with_unit( + "my_counter", + "My counter", + Unit::Seconds, + Box::new(counter.clone()), + ); counter.inc(); let family = Family::, Counter>::default(); - let sub_registry = registry.sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value"))); - sub_registry.register("my_counter_family", "My counter family", Box::new(family.clone())); + let sub_registry = + registry.sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value"))); + sub_registry.register( + "my_counter_family", + "My counter family", + Box::new(family.clone()), + ); family .get_or_create(&vec![ ("method".to_string(), "GET".to_string()), From abb66dfe54d2bc804e61c81be9b2b1ee6c433f4b Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Mon, 14 Mar 2022 23:30:09 +0900 Subject: [PATCH 03/73] src/encoding/proto: Implement encoding for labels Signed-off-by: ackintosh --- build.rs | 5 +- src/encoding/proto.rs | 127 ++++++++++++++++++++++++++++++++---------- 2 files changed, 103 insertions(+), 29 deletions(-) diff --git a/build.rs b/build.rs index 44c54667..36a14ae6 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,9 @@ use std::io::Result; fn main() -> Result<()> { - prost_build::compile_protos(&["src/encoding/proto/openmetrics_data_model.proto"], &["src/encoding/proto/"])?; + prost_build::compile_protos( + &["src/encoding/proto/openmetrics_data_model.proto"], + &["src/encoding/proto/"], + )?; Ok(()) } diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index f1e79030..685477a3 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -4,8 +4,8 @@ pub mod openmetrics_data_model { } use crate::metrics::counter::Counter; -use crate::metrics::family::Family; -use crate::metrics::MetricType; +use crate::metrics::family::{Family, MetricConstructor}; +use crate::metrics::{MetricType, TypedMetric}; use crate::registry::{Registry, Unit}; use std::ops::Deref; @@ -44,19 +44,9 @@ where } // MetricFamily.help family.help = desc.help().to_string(); - // Metric - let mut m = openmetrics_data_model::Metric::default(); - // Metric.labels - for l in desc.labels() { - let mut label = openmetrics_data_model::Label::default(); - label.name = l.0.to_string(); - label.value = l.1.to_string(); - m.labels.push(label); - } - // Metric.metric_points - m.metric_points.push(metric.encode()); - - family.metrics.push(m); + println!("family.help: {}", family.help); + // MetricFamily.Metric + family.metrics = metric.encode(desc.labels().encode()); metric_set.metric_families.push(family); } @@ -77,14 +67,20 @@ impl From for openmetrics_data_model::MetricType { /// Trait implemented by each metric type, e.g. [`Counter`], to implement its encoding. pub trait EncodeMetric { - fn encode(&self) -> openmetrics_data_model::MetricPoint; + fn encode( + &self, + labels: Vec, + ) -> Vec; fn metric_type(&self) -> MetricType; } impl EncodeMetric for Box { - fn encode(&self) -> openmetrics_data_model::MetricPoint { - self.deref().encode() + fn encode( + &self, + labels: Vec, + ) -> Vec { + self.deref().encode(labels) } fn metric_type(&self) -> MetricType { @@ -97,8 +93,11 @@ pub trait SendEncodeMetric: EncodeMetric + Send {} impl SendEncodeMetric for T {} impl EncodeMetric for Box { - fn encode(&self) -> openmetrics_data_model::MetricPoint { - self.deref().encode() + fn encode( + &self, + labels: Vec, + ) -> Vec { + self.deref().encode(labels) } fn metric_type(&self) -> MetricType { @@ -106,14 +105,66 @@ impl EncodeMetric for Box { } } +pub trait EncodeLabel { + fn encode(&self) -> Vec; +} + +impl EncodeLabel for (K, V) { + fn encode(&self) -> Vec { + let mut label = openmetrics_data_model::Label::default(); + label.name = self.0.to_string(); + label.value = self.1.to_string(); + vec![label] + } +} + +impl EncodeLabel for Vec { + fn encode(&self) -> Vec { + let mut label = vec![]; + for t in self { + label.append(&mut t.encode()); + } + label + } +} + +impl EncodeLabel for &[T] { + fn encode(&self) -> Vec { + let mut label = vec![]; + for t in self.iter() { + label.append(&mut t.encode()); + } + label + } +} + ///////////////////////////////////////////////////////////////////////////////// // Counter impl EncodeMetric for Counter { - fn encode(&self) -> openmetrics_data_model::MetricPoint { - let metric_point = openmetrics_data_model::MetricPoint::default(); - // TODO: value - metric_point + fn encode( + &self, + labels: Vec, + ) -> Vec { + let mut metric = openmetrics_data_model::Metric::default(); + metric.labels = labels; + + metric.metric_points = { + let mut metric_point = openmetrics_data_model::MetricPoint::default(); + metric_point.value = { + let mut counter_value = openmetrics_data_model::CounterValue::default(); + counter_value.total = Some(openmetrics_data_model::counter_value::Total::IntValue( + self.get(), + )); + Some(openmetrics_data_model::metric_point::Value::CounterValue( + counter_value, + )) + }; + + vec![metric_point] + }; + + vec![metric] } fn metric_type(&self) -> MetricType { @@ -121,13 +172,33 @@ impl EncodeMetric for Counter { } } -impl EncodeMetric for Family { - fn encode(&self) -> openmetrics_data_model::MetricPoint { - todo!() +///////////////////////////////////////////////////////////////////////////////// +// Family + +impl EncodeMetric for Family +where + S: Clone + std::hash::Hash + Eq + EncodeLabel, + M: EncodeMetric + TypedMetric, + C: MetricConstructor, +{ + fn encode( + &self, + labels: Vec, + ) -> Vec { + let mut metrics = vec![]; + + let guard = self.read(); + for (label_set, metric) in guard.iter() { + let mut label = label_set.encode(); + label.append(&mut labels.clone()); + metrics.extend(metric.encode(label)); + } + + metrics } fn metric_type(&self) -> MetricType { - todo!() + M::TYPE } } From 33b527fcf81c2b3cd2842d9327fe221b284201ff Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Tue, 15 Mar 2022 01:41:20 +0900 Subject: [PATCH 04/73] src/encoding/proto: Implement encoding for CounterWithExemplar Signed-off-by: ackintosh --- src/encoding/proto.rs | 126 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 16 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 685477a3..c81dd1aa 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -4,8 +4,9 @@ pub mod openmetrics_data_model { } use crate::metrics::counter::Counter; +use crate::metrics::exemplar::{CounterWithExemplar, Exemplar}; use crate::metrics::family::{Family, MetricConstructor}; -use crate::metrics::{MetricType, TypedMetric}; +use crate::metrics::{counter, MetricType, TypedMetric}; use crate::registry::{Registry, Unit}; use std::ops::Deref; @@ -138,32 +139,67 @@ impl EncodeLabel for &[T] { } } +pub trait EncodeCounterValue { + fn encode(&self) -> openmetrics_data_model::counter_value::Total; +} + +impl EncodeCounterValue for u64 { + fn encode(&self) -> openmetrics_data_model::counter_value::Total { + openmetrics_data_model::counter_value::Total::IntValue(*self) + } +} + +impl EncodeCounterValue for f64 { + fn encode(&self) -> openmetrics_data_model::counter_value::Total { + openmetrics_data_model::counter_value::Total::DoubleValue(*self) + } +} + ///////////////////////////////////////////////////////////////////////////////// // Counter -impl EncodeMetric for Counter { +impl EncodeMetric for Counter +where + N: EncodeCounterValue, + A: counter::Atomic, +{ fn encode( &self, labels: Vec, ) -> Vec { - let mut metric = openmetrics_data_model::Metric::default(); + let mut metric = encode_counter(self.get(), None); metric.labels = labels; - metric.metric_points = { - let mut metric_point = openmetrics_data_model::MetricPoint::default(); - metric_point.value = { - let mut counter_value = openmetrics_data_model::CounterValue::default(); - counter_value.total = Some(openmetrics_data_model::counter_value::Total::IntValue( - self.get(), - )); - Some(openmetrics_data_model::metric_point::Value::CounterValue( - counter_value, - )) - }; - - vec![metric_point] + vec![metric] + } + + fn metric_type(&self) -> MetricType { + MetricType::Counter + } +} + +impl EncodeMetric for CounterWithExemplar +where + S: EncodeLabel, + N: Clone + EncodeCounterValue, + A: counter::Atomic, + f64: From, +{ + fn encode( + &self, + labels: Vec, + ) -> Vec { + let (value, exemplar) = self.get(); + + let exemplar_proto = if let Some(e) = exemplar.as_ref() { + Some(encode_exemplar(e)) + } else { + None }; + let mut metric = encode_counter(value.clone(), exemplar_proto); + metric.labels = labels; + vec![metric] } @@ -172,6 +208,46 @@ impl EncodeMetric for Counter { } } +fn encode_counter( + value: N, + exemplar: Option, +) -> openmetrics_data_model::Metric +where + N: EncodeCounterValue, +{ + let mut metric = openmetrics_data_model::Metric::default(); + + metric.metric_points = { + let mut metric_point = openmetrics_data_model::MetricPoint::default(); + metric_point.value = { + let mut counter_value = openmetrics_data_model::CounterValue::default(); + counter_value.total = Some(value.encode()); + counter_value.exemplar = exemplar; + + Some(openmetrics_data_model::metric_point::Value::CounterValue( + counter_value, + )) + }; + + vec![metric_point] + }; + + metric +} + +fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar +where + N: Clone, + S: EncodeLabel, + f64: From, // required because Exemplar.value is defined as `double` in protobuf +{ + let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); + exemplar_proto.value = exemplar.value.clone().into(); + exemplar_proto.label = exemplar.label_set.encode(); + + exemplar_proto +} + ///////////////////////////////////////////////////////////////////////////////// // Family @@ -206,6 +282,7 @@ where mod tests { use super::*; use crate::metrics::counter::Counter; + use crate::metrics::exemplar::CounterWithExemplar; use crate::metrics::family::Family; use crate::registry::Unit; use std::borrow::Cow; @@ -246,4 +323,21 @@ mod tests { println!("{:?}", encode(®istry)); } + + #[test] + fn encode_counter_with_exemplar() { + let mut registry = Registry::default(); + + let counter_with_exemplar: CounterWithExemplar<(String, f64), f64> = + CounterWithExemplar::default(); + registry.register_with_unit( + "my_counter_with_exemplar", + "My counter with exemplar", + Unit::Seconds, + counter_with_exemplar.clone(), + ); + + counter_with_exemplar.inc_by(1.0, Some(("user_id".to_string(), 42.0))); + println!("{:?}", encode(®istry)); + } } From a3e6afcefc94829936cc030208f4a90ad3e5826b Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Sat, 19 Mar 2022 22:39:08 +0900 Subject: [PATCH 05/73] Move comment Signed-off-by: ackintosh --- src/encoding/proto.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index c81dd1aa..23877ece 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -139,6 +139,9 @@ impl EncodeLabel for &[T] { } } +///////////////////////////////////////////////////////////////////////////////// +// Counter + pub trait EncodeCounterValue { fn encode(&self) -> openmetrics_data_model::counter_value::Total; } @@ -155,9 +158,6 @@ impl EncodeCounterValue for f64 { } } -///////////////////////////////////////////////////////////////////////////////// -// Counter - impl EncodeMetric for Counter where N: EncodeCounterValue, From c3bb963283a1b924ccf3c5a09370a9f5cd8989e6 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Sat, 19 Mar 2022 23:09:04 +0900 Subject: [PATCH 06/73] src/encoding/proto: Implement encoding for Gauge Signed-off-by: ackintosh --- src/encoding/proto.rs | 94 ++++++++++++++++++++++++++++++++++++++++++- src/metrics/gauge.rs | 28 ++++++++++++- 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 23877ece..84f5fb3e 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -6,7 +6,8 @@ pub mod openmetrics_data_model { use crate::metrics::counter::Counter; use crate::metrics::exemplar::{CounterWithExemplar, Exemplar}; use crate::metrics::family::{Family, MetricConstructor}; -use crate::metrics::{counter, MetricType, TypedMetric}; +use crate::metrics::gauge::Gauge; +use crate::metrics::{counter, gauge, MetricType, TypedMetric}; use crate::registry::{Registry, Unit}; use std::ops::Deref; @@ -45,7 +46,6 @@ where } // MetricFamily.help family.help = desc.help().to_string(); - println!("family.help: {}", family.help); // MetricFamily.Metric family.metrics = metric.encode(desc.labels().encode()); metric_set.metric_families.push(family); @@ -248,6 +248,60 @@ where exemplar_proto } +///////////////////////////////////////////////////////////////////////////////// +// Gauge + +pub trait EncodeGaugeValue { + fn encode(&self) -> openmetrics_data_model::gauge_value::Value; +} + +// GaugeValue.int_value is defined as `int64` in protobuf +impl EncodeGaugeValue for i64 { + fn encode(&self) -> openmetrics_data_model::gauge_value::Value { + openmetrics_data_model::gauge_value::Value::IntValue(*self) + } +} + +impl EncodeGaugeValue for f64 { + fn encode(&self) -> openmetrics_data_model::gauge_value::Value { + openmetrics_data_model::gauge_value::Value::DoubleValue(*self) + } +} + +impl EncodeMetric for Gauge +where + N: EncodeGaugeValue, + A: gauge::Atomic, +{ + fn encode( + &self, + labels: Vec, + ) -> Vec { + let mut metric = openmetrics_data_model::Metric::default(); + + metric.metric_points = { + let mut metric_point = openmetrics_data_model::MetricPoint::default(); + metric_point.value = { + let mut gauge_value = openmetrics_data_model::GaugeValue::default(); + gauge_value.value = Some(self.get().encode()); + + Some(openmetrics_data_model::metric_point::Value::GaugeValue( + gauge_value, + )) + }; + + vec![metric_point] + }; + + metric.labels = labels; + vec![metric] + } + + fn metric_type(&self) -> MetricType { + MetricType::Gauge + } +} + ///////////////////////////////////////////////////////////////////////////////// // Family @@ -284,8 +338,10 @@ mod tests { use crate::metrics::counter::Counter; use crate::metrics::exemplar::CounterWithExemplar; use crate::metrics::family::Family; + use crate::metrics::gauge::Gauge; use crate::registry::Unit; use std::borrow::Cow; + use std::sync::atomic::AtomicI64; #[test] fn test_encode() { @@ -340,4 +396,38 @@ mod tests { counter_with_exemplar.inc_by(1.0, Some(("user_id".to_string(), 42.0))); println!("{:?}", encode(®istry)); } + + #[test] + fn encode_gauge() { + let mut registry = Registry::default(); + let gauge = Gauge::::default(); + registry.register("my_gauge", "My gauge", gauge.clone()); + gauge.inc(); + + let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_gauge", family.name); + assert_eq!( + openmetrics_data_model::MetricType::Gauge as i32, + family.r#type + ); + assert_eq!("My gauge.", family.help); + + let metric = family.metrics.first().unwrap(); + let gauge_value = openmetrics_data_model::metric_point::Value::GaugeValue({ + let mut v = openmetrics_data_model::GaugeValue::default(); + v.value = Some(openmetrics_data_model::gauge_value::Value::IntValue(1)); + v + }); + assert_eq!( + &gauge_value, + metric + .metric_points + .first() + .unwrap() + .value + .as_ref() + .unwrap() + ); + } } diff --git a/src/metrics/gauge.rs b/src/metrics/gauge.rs index a8e41140..1e940411 100644 --- a/src/metrics/gauge.rs +++ b/src/metrics/gauge.rs @@ -6,7 +6,7 @@ use super::{MetricType, TypedMetric}; use std::marker::PhantomData; #[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))] use std::sync::atomic::AtomicU64; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{AtomicI64, AtomicU32, Ordering}; use std::sync::Arc; /// Open Metrics [`Gauge`] to record current measurements. @@ -226,6 +226,32 @@ impl Atomic for AtomicU64 { } } +impl Atomic for AtomicI64 { + fn inc(&self) -> i64 { + self.inc_by(1) + } + + fn inc_by(&self, v: i64) -> i64 { + self.fetch_add(v, Ordering::Relaxed) + } + + fn dec(&self) -> i64 { + self.dec_by(1) + } + + fn dec_by(&self, v: i64) -> i64 { + self.fetch_sub(v, Ordering::Relaxed) + } + + fn set(&self, v: i64) -> i64 { + self.swap(v, Ordering::Relaxed) + } + + fn get(&self) -> i64 { + self.load(Ordering::Relaxed) + } +} + impl TypedMetric for Gauge { const TYPE: MetricType = MetricType::Gauge; } From d29e2d3a5cd99decbb22b2879cfac61e6a29a998 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Sun, 20 Mar 2022 16:22:58 +0900 Subject: [PATCH 07/73] src/encoding/proto: Implement encoding for Histogram Signed-off-by: ackintosh --- src/encoding/proto.rs | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 84f5fb3e..d3bd2f45 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -7,6 +7,7 @@ use crate::metrics::counter::Counter; use crate::metrics::exemplar::{CounterWithExemplar, Exemplar}; use crate::metrics::family::{Family, MetricConstructor}; use crate::metrics::gauge::Gauge; +use crate::metrics::histogram::Histogram; use crate::metrics::{counter, gauge, MetricType, TypedMetric}; use crate::registry::{Registry, Unit}; use std::ops::Deref; @@ -332,6 +333,51 @@ where } } +///////////////////////////////////////////////////////////////////////////////// +// Histogram + +impl EncodeMetric for Histogram { + fn encode( + &self, + labels: Vec, + ) -> Vec { + let mut metric = openmetrics_data_model::Metric::default(); + + metric.metric_points = { + let mut metric_point = openmetrics_data_model::MetricPoint::default(); + metric_point.value = { + let mut histogram_value = openmetrics_data_model::HistogramValue::default(); + let (sum, count, buckets) = self.get(); + histogram_value.sum = Some( + openmetrics_data_model::histogram_value::Sum::DoubleValue(sum), + ); + histogram_value.count = count; + + let mut cummulative = 0; + for (_i, (upper_bound, count)) in buckets.iter().enumerate() { + cummulative += count; + let mut bucket = openmetrics_data_model::histogram_value::Bucket::default(); + bucket.count = cummulative; + bucket.upper_bound = *upper_bound; + histogram_value.buckets.push(bucket); + } + Some(openmetrics_data_model::metric_point::Value::HistogramValue( + histogram_value, + )) + }; + + vec![metric_point] + }; + + metric.labels = labels; + vec![metric] + } + + fn metric_type(&self) -> MetricType { + MetricType::Histogram + } +} + #[cfg(test)] mod tests { use super::*; @@ -339,6 +385,7 @@ mod tests { use crate::metrics::exemplar::CounterWithExemplar; use crate::metrics::family::Family; use crate::metrics::gauge::Gauge; + use crate::metrics::histogram::{exponential_buckets, Histogram}; use crate::registry::Unit; use std::borrow::Cow; use std::sync::atomic::AtomicI64; @@ -430,4 +477,41 @@ mod tests { .unwrap() ); } + + #[test] + fn encode_histogram() { + let mut registry = Registry::default(); + let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10)); + registry.register("my_histogram", "My histogram", histogram.clone()); + histogram.observe(1.0); + + let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!( + openmetrics_data_model::MetricType::Histogram as i32, + family.r#type + ); + + let metric = family.metrics.first().unwrap(); + let metric_point_value = metric + .metric_points + .first() + .unwrap() + .value + .as_ref() + .unwrap(); + match metric_point_value { + openmetrics_data_model::metric_point::Value::HistogramValue(value) => { + assert_eq!( + Some(openmetrics_data_model::histogram_value::Sum::DoubleValue( + 1.0 + )), + value.sum + ); + assert_eq!(1, value.count); + assert_eq!(11, value.buckets.len()); + } + _ => assert!(false, "wrong value type"), + } + } } From 18ca645cb6de61dabe03dfbad4c0d7f1686056cc Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Sun, 20 Mar 2022 16:47:55 +0900 Subject: [PATCH 08/73] src/encoding/proto: Tweak function name according to encoding::text Signed-off-by: ackintosh --- src/encoding/proto.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index d3bd2f45..b386e73a 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -168,7 +168,7 @@ where &self, labels: Vec, ) -> Vec { - let mut metric = encode_counter(self.get(), None); + let mut metric = encode_counter_with_maybe_exemplar(self.get(), None); metric.labels = labels; vec![metric] @@ -198,7 +198,7 @@ where None }; - let mut metric = encode_counter(value.clone(), exemplar_proto); + let mut metric = encode_counter_with_maybe_exemplar(value.clone(), exemplar_proto); metric.labels = labels; vec![metric] @@ -209,7 +209,7 @@ where } } -fn encode_counter( +fn encode_counter_with_maybe_exemplar( value: N, exemplar: Option, ) -> openmetrics_data_model::Metric From 9c97af5e91b05670fce6cb1ef5d46a7414691090 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Mon, 21 Mar 2022 00:02:07 +0900 Subject: [PATCH 09/73] src/encoding/proto: Implement encoding for HistogramWithExemplars Signed-off-by: ackintosh --- src/encoding/proto.rs | 139 +++++++++++++++++++++++++++++++++--------- 1 file changed, 111 insertions(+), 28 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index b386e73a..15eab1ff 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -4,12 +4,13 @@ pub mod openmetrics_data_model { } use crate::metrics::counter::Counter; -use crate::metrics::exemplar::{CounterWithExemplar, Exemplar}; +use crate::metrics::exemplar::{CounterWithExemplar, Exemplar, HistogramWithExemplars}; use crate::metrics::family::{Family, MetricConstructor}; use crate::metrics::gauge::Gauge; use crate::metrics::histogram::Histogram; use crate::metrics::{counter, gauge, MetricType, TypedMetric}; use crate::registry::{Registry, Unit}; +use std::collections::HashMap; use std::ops::Deref; pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet @@ -140,6 +141,12 @@ impl EncodeLabel for &[T] { } } +impl EncodeLabel for () { + fn encode(&self) -> Vec { + vec![] + } +} + ///////////////////////////////////////////////////////////////////////////////// // Counter @@ -341,34 +348,30 @@ impl EncodeMetric for Histogram { &self, labels: Vec, ) -> Vec { - let mut metric = openmetrics_data_model::Metric::default(); - - metric.metric_points = { - let mut metric_point = openmetrics_data_model::MetricPoint::default(); - metric_point.value = { - let mut histogram_value = openmetrics_data_model::HistogramValue::default(); - let (sum, count, buckets) = self.get(); - histogram_value.sum = Some( - openmetrics_data_model::histogram_value::Sum::DoubleValue(sum), - ); - histogram_value.count = count; - - let mut cummulative = 0; - for (_i, (upper_bound, count)) in buckets.iter().enumerate() { - cummulative += count; - let mut bucket = openmetrics_data_model::histogram_value::Bucket::default(); - bucket.count = cummulative; - bucket.upper_bound = *upper_bound; - histogram_value.buckets.push(bucket); - } - Some(openmetrics_data_model::metric_point::Value::HistogramValue( - histogram_value, - )) - }; + let (sum, count, buckets) = self.get(); + // TODO: Would be better to use never type instead of `()`. + let mut metric = encode_histogram_with_maybe_exemplars::<()>(sum, count, &buckets, None); + metric.labels = labels; + vec![metric] + } - vec![metric_point] - }; + fn metric_type(&self) -> MetricType { + MetricType::Histogram + } +} +impl EncodeMetric for HistogramWithExemplars +where + S: EncodeLabel, +{ + fn encode( + &self, + labels: Vec, + ) -> Vec { + let inner = self.inner(); + let (sum, count, buckets) = inner.histogram.get(); + let mut metric = + encode_histogram_with_maybe_exemplars(sum, count, &buckets, Some(&inner.exemplars)); metric.labels = labels; vec![metric] } @@ -378,11 +381,51 @@ impl EncodeMetric for Histogram { } } +fn encode_histogram_with_maybe_exemplars( + sum: f64, + count: u64, + buckets: &[(f64, u64)], + exemplars: Option<&HashMap>>, +) -> openmetrics_data_model::Metric { + let mut metric = openmetrics_data_model::Metric::default(); + + metric.metric_points = { + let mut metric_point = openmetrics_data_model::MetricPoint::default(); + metric_point.value = { + let mut histogram_value = openmetrics_data_model::HistogramValue::default(); + histogram_value.sum = Some(openmetrics_data_model::histogram_value::Sum::DoubleValue( + sum, + )); + histogram_value.count = count; + + let mut cummulative = 0; + for (i, (upper_bound, count)) in buckets.iter().enumerate() { + cummulative += count; + let mut bucket = openmetrics_data_model::histogram_value::Bucket::default(); + bucket.count = cummulative; + bucket.upper_bound = *upper_bound; + bucket.exemplar = exemplars + .map(|es| es.get(&i)) + .flatten() + .map(|exemplar| encode_exemplar(exemplar)); + histogram_value.buckets.push(bucket); + } + Some(openmetrics_data_model::metric_point::Value::HistogramValue( + histogram_value, + )) + }; + + vec![metric_point] + }; + + metric +} + #[cfg(test)] mod tests { use super::*; use crate::metrics::counter::Counter; - use crate::metrics::exemplar::CounterWithExemplar; + use crate::metrics::exemplar::{CounterWithExemplar, HistogramWithExemplars}; use crate::metrics::family::Family; use crate::metrics::gauge::Gauge; use crate::metrics::histogram::{exponential_buckets, Histogram}; @@ -514,4 +557,44 @@ mod tests { _ => assert!(false, "wrong value type"), } } + + #[test] + fn encode_histogram_with_exemplars() { + let mut registry = Registry::default(); + let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10)); + registry.register("my_histogram", "My histogram", histogram.clone()); + histogram.observe(1.0, Some(("user_id".to_string(), 42u64))); + + let metric_set = encode(®istry); + let metric = metric_set + .metric_families + .first() + .unwrap() + .metrics + .first() + .unwrap(); + let metric_point_value = metric + .metric_points + .first() + .unwrap() + .value + .as_ref() + .unwrap(); + + match metric_point_value { + openmetrics_data_model::metric_point::Value::HistogramValue(value) => { + let exemplar = value.buckets.first().unwrap().exemplar.as_ref().unwrap(); + assert_eq!(1.0, exemplar.value); + + let expected_label = { + let mut label = openmetrics_data_model::Label::default(); + label.name = "user_id".to_string(); + label.value = "42".to_string(); + label + }; + assert_eq!(vec![expected_label], exemplar.label); + } + _ => assert!(false, "wrong value type"), + } + } } From ca45f646a2211bdd8ff63eb8ad9bc7e4a52f2ce7 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Mon, 21 Mar 2022 00:05:17 +0900 Subject: [PATCH 10/73] src/encoding/proto: Move function for readability Signed-off-by: ackintosh --- src/encoding/proto.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 15eab1ff..37db2d03 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -147,6 +147,19 @@ impl EncodeLabel for () { } } +fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar + where + N: Clone, + S: EncodeLabel, + f64: From, // required because Exemplar.value is defined as `double` in protobuf +{ + let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); + exemplar_proto.value = exemplar.value.clone().into(); + exemplar_proto.label = exemplar.label_set.encode(); + + exemplar_proto +} + ///////////////////////////////////////////////////////////////////////////////// // Counter @@ -243,19 +256,6 @@ where metric } -fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar -where - N: Clone, - S: EncodeLabel, - f64: From, // required because Exemplar.value is defined as `double` in protobuf -{ - let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); - exemplar_proto.value = exemplar.value.clone().into(); - exemplar_proto.label = exemplar.label_set.encode(); - - exemplar_proto -} - ///////////////////////////////////////////////////////////////////////////////// // Gauge From d76e7a0e8be361b3324dab16d7cd128480cfee09 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Mon, 21 Mar 2022 08:51:26 +0900 Subject: [PATCH 11/73] src/encoding/proto: Implement encoding for Info Signed-off-by: ackintosh --- src/encoding/proto.rs | 81 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 37db2d03..218e7dcc 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -8,6 +8,7 @@ use crate::metrics::exemplar::{CounterWithExemplar, Exemplar, HistogramWithExemp use crate::metrics::family::{Family, MetricConstructor}; use crate::metrics::gauge::Gauge; use crate::metrics::histogram::Histogram; +use crate::metrics::info::Info; use crate::metrics::{counter, gauge, MetricType, TypedMetric}; use crate::registry::{Registry, Unit}; use std::collections::HashMap; @@ -148,10 +149,10 @@ impl EncodeLabel for () { } fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar - where - N: Clone, - S: EncodeLabel, - f64: From, // required because Exemplar.value is defined as `double` in protobuf +where + N: Clone, + S: EncodeLabel, + f64: From, // required because Exemplar.value is defined as `double` in protobuf { let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); exemplar_proto.value = exemplar.value.clone().into(); @@ -421,6 +422,44 @@ fn encode_histogram_with_maybe_exemplars( metric } +///////////////////////////////////////////////////////////////////////////////// +// Info + +impl EncodeMetric for Info +where + S: EncodeLabel, +{ + fn encode( + &self, + labels: Vec, + ) -> Vec { + let mut metric = openmetrics_data_model::Metric::default(); + + metric.metric_points = { + let mut metric_point = openmetrics_data_model::MetricPoint::default(); + metric_point.value = { + let mut label = self.0.encode(); + label.append(&mut labels.clone()); + + let mut info_value = openmetrics_data_model::InfoValue::default(); + info_value.info = label; + + Some(openmetrics_data_model::metric_point::Value::InfoValue( + info_value, + )) + }; + + vec![metric_point] + }; + + vec![metric] + } + + fn metric_type(&self) -> MetricType { + MetricType::Info + } +} + #[cfg(test)] mod tests { use super::*; @@ -429,6 +468,7 @@ mod tests { use crate::metrics::family::Family; use crate::metrics::gauge::Gauge; use crate::metrics::histogram::{exponential_buckets, Histogram}; + use crate::metrics::info::Info; use crate::registry::Unit; use std::borrow::Cow; use std::sync::atomic::AtomicI64; @@ -597,4 +637,37 @@ mod tests { _ => assert!(false, "wrong value type"), } } + + #[test] + fn encode_info() { + let mut registry = Registry::default(); + let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]); + registry.register("my_info_metric", "My info metric", info); + + let metric_set = encode(®istry); + let metric = metric_set + .metric_families + .first() + .unwrap() + .metrics + .first() + .unwrap(); + let metric_point_value = metric + .metric_points + .first() + .unwrap() + .value + .as_ref() + .unwrap(); + + match metric_point_value { + openmetrics_data_model::metric_point::Value::InfoValue(value) => { + assert_eq!(1, value.info.len()); + let info = value.info.first().unwrap(); + assert_eq!("os", info.name); + assert_eq!("GNU/linux", info.value); + } + _ => assert!(false, "wrong value type"), + } + } } From 4b31a21606118eb2cef084d37808645abd4ecb08 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 15:13:38 +0900 Subject: [PATCH 12/73] src/encoding/proto: Add tests for Counter Signed-off-by: ackintosh --- src/encoding/proto.rs | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 218e7dcc..a8b367a5 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -510,6 +510,48 @@ mod tests { println!("{:?}", encode(®istry)); } + #[test] + fn encode_counter_int() { + let counter: Counter = Counter::default(); + let mut registry = Registry::default(); + registry.register("my_counter", "My counter", counter.clone()); + counter.inc(); + + let metric_point_value = extract_metric_point_value(encode(®istry)); + + match metric_point_value { + openmetrics_data_model::metric_point::Value::CounterValue(value) => { + let expected = openmetrics_data_model::counter_value::Total::IntValue(1); + assert_eq!(Some(expected), value.total); + assert_eq!(None, value.exemplar); + assert_eq!(None, value.created); + } + _ => assert!(false, "wrong value type"), + } + } + + #[test] + fn encode_counter_double() { + // Using `f64` + let counter: Counter = Counter::default(); + let mut registry = Registry::default(); + registry.register("my_counter", "My counter", counter.clone()); + counter.inc(); + + let metric_point_value = extract_metric_point_value(encode(®istry)); + + match metric_point_value { + openmetrics_data_model::metric_point::Value::CounterValue(value) => { + // The counter should be encoded as `DoubleValue` + let expected = openmetrics_data_model::counter_value::Total::DoubleValue(1.0); + assert_eq!(Some(expected), value.total); + assert_eq!(None, value.exemplar); + assert_eq!(None, value.created); + } + _ => assert!(false, "wrong value type"), + } + } + #[test] fn encode_counter_with_exemplar() { let mut registry = Registry::default(); @@ -670,4 +712,25 @@ mod tests { _ => assert!(false, "wrong value type"), } } + + fn extract_metric_point_value( + metric_set: openmetrics_data_model::MetricSet, + ) -> openmetrics_data_model::metric_point::Value { + let metric = metric_set + .metric_families + .first() + .unwrap() + .metrics + .first() + .unwrap(); + + metric + .metric_points + .first() + .unwrap() + .value + .as_ref() + .unwrap() + .clone() + } } From 571c70782b83e33d06ece959b2adf88be9b35157 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 21:41:02 +0900 Subject: [PATCH 13/73] src/encoding/proto: Add assertions for MetricType Signed-off-by: ackintosh --- src/encoding/proto.rs | 49 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index a8b367a5..e11c4ee0 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -517,9 +517,14 @@ mod tests { registry.register("my_counter", "My counter", counter.clone()); counter.inc(); - let metric_point_value = extract_metric_point_value(encode(®istry)); + let metric_set = encode(®istry); - match metric_point_value { + assert_eq!( + openmetrics_data_model::MetricType::Counter as i32, + extract_metric_type(&metric_set) + ); + + match extract_metric_point_value(metric_set) { openmetrics_data_model::metric_point::Value::CounterValue(value) => { let expected = openmetrics_data_model::counter_value::Total::IntValue(1); assert_eq!(Some(expected), value.total); @@ -538,9 +543,14 @@ mod tests { registry.register("my_counter", "My counter", counter.clone()); counter.inc(); - let metric_point_value = extract_metric_point_value(encode(®istry)); + let metric_set = encode(®istry); - match metric_point_value { + assert_eq!( + openmetrics_data_model::MetricType::Counter as i32, + extract_metric_type(&metric_set) + ); + + match extract_metric_point_value(metric_set) { openmetrics_data_model::metric_point::Value::CounterValue(value) => { // The counter should be encoded as `DoubleValue` let expected = openmetrics_data_model::counter_value::Total::DoubleValue(1.0); @@ -566,7 +576,15 @@ mod tests { ); counter_with_exemplar.inc_by(1.0, Some(("user_id".to_string(), 42.0))); - println!("{:?}", encode(®istry)); + + let metric_set = encode(®istry); + + assert_eq!( + openmetrics_data_model::MetricType::Counter as i32, + extract_metric_type(&metric_set) + ); + + // TODO: test the exemplar } #[test] @@ -581,7 +599,7 @@ mod tests { assert_eq!("my_gauge", family.name); assert_eq!( openmetrics_data_model::MetricType::Gauge as i32, - family.r#type + extract_metric_type(&metric_set) ); assert_eq!("My gauge.", family.help); @@ -614,7 +632,7 @@ mod tests { let family = metric_set.metric_families.first().unwrap(); assert_eq!( openmetrics_data_model::MetricType::Histogram as i32, - family.r#type + extract_metric_type(&metric_set) ); let metric = family.metrics.first().unwrap(); @@ -648,6 +666,12 @@ mod tests { histogram.observe(1.0, Some(("user_id".to_string(), 42u64))); let metric_set = encode(®istry); + + assert_eq!( + openmetrics_data_model::MetricType::Histogram as i32, + extract_metric_type(&metric_set) + ); + let metric = metric_set .metric_families .first() @@ -687,6 +711,12 @@ mod tests { registry.register("my_info_metric", "My info metric", info); let metric_set = encode(®istry); + + assert_eq!( + openmetrics_data_model::MetricType::Info as i32, + extract_metric_type(&metric_set) + ); + let metric = metric_set .metric_families .first() @@ -713,6 +743,11 @@ mod tests { } } + fn extract_metric_type(metric_set: &openmetrics_data_model::MetricSet) -> i32 { + let family = metric_set.metric_families.first().unwrap(); + family.r#type + } + fn extract_metric_point_value( metric_set: openmetrics_data_model::MetricSet, ) -> openmetrics_data_model::metric_point::Value { From 84a0745ab35279c767b6377961f3806e92e4ad85 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 21:54:16 +0900 Subject: [PATCH 14/73] src/encoding/proto: DRY-ed tests with `extract_metric_point_value` Signed-off-by: ackintosh --- src/encoding/proto.rs | 73 +++++++++---------------------------------- 1 file changed, 14 insertions(+), 59 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index e11c4ee0..ff9cc9e9 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -597,28 +597,20 @@ mod tests { let metric_set = encode(®istry); let family = metric_set.metric_families.first().unwrap(); assert_eq!("my_gauge", family.name); + assert_eq!("My gauge.", family.help); + assert_eq!( openmetrics_data_model::MetricType::Gauge as i32, extract_metric_type(&metric_set) ); - assert_eq!("My gauge.", family.help); - let metric = family.metrics.first().unwrap(); - let gauge_value = openmetrics_data_model::metric_point::Value::GaugeValue({ - let mut v = openmetrics_data_model::GaugeValue::default(); - v.value = Some(openmetrics_data_model::gauge_value::Value::IntValue(1)); - v - }); - assert_eq!( - &gauge_value, - metric - .metric_points - .first() - .unwrap() - .value - .as_ref() - .unwrap() - ); + match extract_metric_point_value(metric_set) { + openmetrics_data_model::metric_point::Value::GaugeValue(value) => { + let expected = openmetrics_data_model::gauge_value::Value::IntValue(1); + assert_eq!(Some(expected), value.value); + } + _ => assert!(false, "wrong value type"), + } } #[test] @@ -629,21 +621,13 @@ mod tests { histogram.observe(1.0); let metric_set = encode(®istry); - let family = metric_set.metric_families.first().unwrap(); + assert_eq!( openmetrics_data_model::MetricType::Histogram as i32, extract_metric_type(&metric_set) ); - let metric = family.metrics.first().unwrap(); - let metric_point_value = metric - .metric_points - .first() - .unwrap() - .value - .as_ref() - .unwrap(); - match metric_point_value { + match extract_metric_point_value(metric_set) { openmetrics_data_model::metric_point::Value::HistogramValue(value) => { assert_eq!( Some(openmetrics_data_model::histogram_value::Sum::DoubleValue( @@ -672,22 +656,7 @@ mod tests { extract_metric_type(&metric_set) ); - let metric = metric_set - .metric_families - .first() - .unwrap() - .metrics - .first() - .unwrap(); - let metric_point_value = metric - .metric_points - .first() - .unwrap() - .value - .as_ref() - .unwrap(); - - match metric_point_value { + match extract_metric_point_value(metric_set) { openmetrics_data_model::metric_point::Value::HistogramValue(value) => { let exemplar = value.buckets.first().unwrap().exemplar.as_ref().unwrap(); assert_eq!(1.0, exemplar.value); @@ -717,24 +686,10 @@ mod tests { extract_metric_type(&metric_set) ); - let metric = metric_set - .metric_families - .first() - .unwrap() - .metrics - .first() - .unwrap(); - let metric_point_value = metric - .metric_points - .first() - .unwrap() - .value - .as_ref() - .unwrap(); - - match metric_point_value { + match extract_metric_point_value(metric_set) { openmetrics_data_model::metric_point::Value::InfoValue(value) => { assert_eq!(1, value.info.len()); + let info = value.info.first().unwrap(); assert_eq!("os", info.name); assert_eq!("GNU/linux", info.value); From 90d8b00479565d7d240676b9c0656220c9efc74b Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 21:58:41 +0900 Subject: [PATCH 15/73] src/encoding/proto: Assert name and help Signed-off-by: ackintosh --- src/encoding/proto.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index ff9cc9e9..abb509c5 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -519,6 +519,10 @@ mod tests { let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_counter", family.name); + assert_eq!("My counter.", family.help); + assert_eq!( openmetrics_data_model::MetricType::Counter as i32, extract_metric_type(&metric_set) @@ -545,6 +549,10 @@ mod tests { let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_counter", family.name); + assert_eq!("My counter.", family.help); + assert_eq!( openmetrics_data_model::MetricType::Counter as i32, extract_metric_type(&metric_set) @@ -579,6 +587,10 @@ mod tests { let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_counter_with_exemplar", family.name); + assert_eq!("My counter with exemplar.", family.help); + assert_eq!( openmetrics_data_model::MetricType::Counter as i32, extract_metric_type(&metric_set) @@ -622,6 +634,10 @@ mod tests { let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_histogram", family.name); + assert_eq!("My histogram.", family.help); + assert_eq!( openmetrics_data_model::MetricType::Histogram as i32, extract_metric_type(&metric_set) @@ -651,6 +667,10 @@ mod tests { let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_histogram", family.name); + assert_eq!("My histogram.", family.help); + assert_eq!( openmetrics_data_model::MetricType::Histogram as i32, extract_metric_type(&metric_set) @@ -681,6 +701,10 @@ mod tests { let metric_set = encode(®istry); + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_info_metric", family.name); + assert_eq!("My info metric.", family.help); + assert_eq!( openmetrics_data_model::MetricType::Info as i32, extract_metric_type(&metric_set) From 03fc0572f9008f20a96c200cd8bba90e4d60c33f Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 22:07:10 +0900 Subject: [PATCH 16/73] src/encoding/proto: Refactor: use associated constants Signed-off-by: ackintosh --- src/encoding/proto.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index abb509c5..0b6d9ba5 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -196,7 +196,7 @@ where } fn metric_type(&self) -> MetricType { - MetricType::Counter + Self::TYPE } } @@ -226,7 +226,7 @@ where } fn metric_type(&self) -> MetricType { - MetricType::Counter + Counter::::TYPE } } @@ -307,7 +307,7 @@ where } fn metric_type(&self) -> MetricType { - MetricType::Gauge + Self::TYPE } } @@ -357,7 +357,7 @@ impl EncodeMetric for Histogram { } fn metric_type(&self) -> MetricType { - MetricType::Histogram + Self::TYPE } } @@ -378,7 +378,7 @@ where } fn metric_type(&self) -> MetricType { - MetricType::Histogram + Histogram::TYPE } } @@ -456,7 +456,7 @@ where } fn metric_type(&self) -> MetricType { - MetricType::Info + Self::TYPE } } From 745160c0c1280d33531d25f61585f2095ba6aeb5 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 22:13:22 +0900 Subject: [PATCH 17/73] src/encoding/proto: Add assertions for counter.exemplar Signed-off-by: ackintosh --- src/encoding/proto.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 0b6d9ba5..9d0991f2 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -596,7 +596,25 @@ mod tests { extract_metric_type(&metric_set) ); - // TODO: test the exemplar + match extract_metric_point_value(metric_set) { + openmetrics_data_model::metric_point::Value::CounterValue(value) => { + // The counter should be encoded as `DoubleValue` + let expected = openmetrics_data_model::counter_value::Total::DoubleValue(1.0); + assert_eq!(Some(expected), value.total); + + let exemplar = value.exemplar.as_ref().unwrap(); + assert_eq!(1.0, exemplar.value); + + let expected_label = { + let mut label = openmetrics_data_model::Label::default(); + label.name = "user_id".to_string(); + label.value = "42".to_string(); + label + }; + assert_eq!(vec![expected_label], exemplar.label); + } + _ => assert!(false, "wrong value type"), + } } #[test] From 4b839c9e582e17fcb5c7e074fb25ef26b4368cb7 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 22:19:43 +0900 Subject: [PATCH 18/73] src/encoding/proto: Add assertion for Unit Signed-off-by: ackintosh --- src/encoding/proto.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 9d0991f2..7836b6ac 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -570,16 +570,29 @@ mod tests { } } + #[test] + fn encode_counter_with_unit() { + let mut registry = Registry::default(); + let counter: Counter = Counter::default(); + registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter.clone()); + + let metric_set = encode(®istry); + + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_counter", family.name); + assert_eq!("My counter.", family.help); + assert_eq!("seconds", family.unit); + } + #[test] fn encode_counter_with_exemplar() { let mut registry = Registry::default(); let counter_with_exemplar: CounterWithExemplar<(String, f64), f64> = CounterWithExemplar::default(); - registry.register_with_unit( + registry.register( "my_counter_with_exemplar", "My counter with exemplar", - Unit::Seconds, counter_with_exemplar.clone(), ); From 202d457c9ddca0d031e9dc1b88c9a34a288251e7 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 22:48:49 +0900 Subject: [PATCH 19/73] src/encoding/proto: Refactoring: extract a method `Unit::as_str()` Signed-off-by: ackintosh --- src/encoding/proto.rs | 16 ++-------------- src/encoding/text.rs | 15 +-------------- src/registry.rs | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 7836b6ac..0c81e174 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -10,7 +10,7 @@ use crate::metrics::gauge::Gauge; use crate::metrics::histogram::Histogram; use crate::metrics::info::Info; use crate::metrics::{counter, gauge, MetricType, TypedMetric}; -use crate::registry::{Registry, Unit}; +use crate::registry::Registry; use std::collections::HashMap; use std::ops::Deref; @@ -33,19 +33,7 @@ where }; // MetricFamily.unit if let Some(unit) = desc.unit() { - family.unit = match unit { - Unit::Amperes => "amperes", - Unit::Bytes => "bytes", - Unit::Celsius => "celsius", - Unit::Grams => "grams", - Unit::Joules => "joules", - Unit::Meters => "meters", - Unit::Ratios => "ratios", - Unit::Seconds => "seconds", - Unit::Volts => "volts", - Unit::Other(other) => other.as_str(), - } - .to_string(); + family.unit = unit.as_str().to_string(); } // MetricFamily.help family.help = desc.help().to_string(); diff --git a/src/encoding/text.rs b/src/encoding/text.rs index 57914c5e..c3e774a2 100644 --- a/src/encoding/text.rs +++ b/src/encoding/text.rs @@ -193,20 +193,7 @@ impl Encode for MetricType { impl Encode for Unit { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { - let u = match self { - Unit::Amperes => "amperes", - Unit::Bytes => "bytes", - Unit::Celsius => "celsius", - Unit::Grams => "grams", - Unit::Joules => "joules", - Unit::Meters => "meters", - Unit::Ratios => "ratios", - Unit::Seconds => "seconds", - Unit::Volts => "volts", - Unit::Other(other) => other.as_str(), - }; - - writer.write_all(u.as_bytes())?; + writer.write_all(self.as_str().as_bytes())?; Ok(()) } } diff --git a/src/registry.rs b/src/registry.rs index dcb01926..e2122ca0 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -340,6 +340,23 @@ pub enum Unit { Other(String), } +impl Unit { + pub fn as_str(&self) -> &str { + match self { + Unit::Amperes => "amperes", + Unit::Bytes => "bytes", + Unit::Celsius => "celsius", + Unit::Grams => "grams", + Unit::Joules => "joules", + Unit::Meters => "meters", + Unit::Ratios => "ratios", + Unit::Seconds => "seconds", + Unit::Volts => "volts", + Unit::Other(other) => other.as_str(), + } + } +} + #[cfg(test)] mod tests { use super::*; From 2e7e35ae94871dbaa71c17a6c6f67b392632295f Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Thu, 24 Mar 2022 22:52:05 +0900 Subject: [PATCH 20/73] src/encoding/proto: Remove superfluous comments Signed-off-by: ackintosh --- src/encoding/proto.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 0c81e174..953b83ad 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -18,27 +18,21 @@ pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet where M: EncodeMetric, { - // MetricSet let mut metric_set = openmetrics_data_model::MetricSet::default(); for (desc, metric) in registry.iter() { - // MetricFamily let mut family = openmetrics_data_model::MetricFamily::default(); - // MetricFamily.name family.name = desc.name().to_string(); - // MetricFamily.type family.r#type = { let metric_type: openmetrics_data_model::MetricType = metric.metric_type().into(); metric_type as i32 }; - // MetricFamily.unit if let Some(unit) = desc.unit() { family.unit = unit.as_str().to_string(); } - // MetricFamily.help family.help = desc.help().to_string(); - // MetricFamily.Metric family.metrics = metric.encode(desc.labels().encode()); + metric_set.metric_families.push(family); } From bd90dc375710ecbb648f50cc6ffdfb44915002cc Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Fri, 25 Mar 2022 08:54:42 +0900 Subject: [PATCH 21/73] src/encoding/proto: Add tests for Family Signed-off-by: ackintosh --- src/encoding/proto.rs | 89 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 953b83ad..40e61c84 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -638,6 +638,95 @@ mod tests { } } + #[test] + fn encode_counter_family() { + let mut registry = Registry::default(); + let family = Family::, Counter>::default(); + registry.register("my_counter_family", "My counter family", family.clone()); + + family + .get_or_create(&vec![ + ("method".to_string(), "GET".to_string()), + ("status".to_string(), "200".to_string()), + ]) + .inc(); + + let metric_set = encode(®istry); + + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_counter_family", family.name); + assert_eq!("My counter family.", family.help); + + assert_eq!( + openmetrics_data_model::MetricType::Counter as i32, + extract_metric_type(&metric_set) + ); + + let metric = family.metrics.first().unwrap(); + assert_eq!(2, metric.labels.len()); + assert_eq!("method", metric.labels[0].name); + assert_eq!("GET", metric.labels[0].value); + assert_eq!("status", metric.labels[1].name); + assert_eq!("200", metric.labels[1].value); + + match extract_metric_point_value(metric_set) { + openmetrics_data_model::metric_point::Value::CounterValue(value) => { + let expected = openmetrics_data_model::counter_value::Total::IntValue(1); + assert_eq!(Some(expected), value.total); + assert_eq!(None, value.exemplar); + assert_eq!(None, value.created); + } + _ => assert!(false, "wrong value type"), + } + } + + #[test] + fn encode_counter_family_with_prefix_with_label() { + let mut registry = Registry::default(); + let sub_registry = registry.sub_registry_with_prefix("my_prefix"); + let sub_sub_registry = sub_registry + .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value"))); + let family = Family::, Counter>::default(); + sub_sub_registry.register("my_counter_family", "My counter family", family.clone()); + + family + .get_or_create(&vec![ + ("method".to_string(), "GET".to_string()), + ("status".to_string(), "200".to_string()), + ]) + .inc(); + + let metric_set = encode(®istry); + + let family = metric_set.metric_families.first().unwrap(); + assert_eq!("my_prefix_my_counter_family", family.name); + assert_eq!("My counter family.", family.help); + + assert_eq!( + openmetrics_data_model::MetricType::Counter as i32, + extract_metric_type(&metric_set) + ); + + let metric = family.metrics.first().unwrap(); + assert_eq!(3, metric.labels.len()); + assert_eq!("method", metric.labels[0].name); + assert_eq!("GET", metric.labels[0].value); + assert_eq!("status", metric.labels[1].name); + assert_eq!("200", metric.labels[1].value); + assert_eq!("my_key", metric.labels[2].name); + assert_eq!("my_value", metric.labels[2].value); + + match extract_metric_point_value(metric_set) { + openmetrics_data_model::metric_point::Value::CounterValue(value) => { + let expected = openmetrics_data_model::counter_value::Total::IntValue(1); + assert_eq!(Some(expected), value.total); + assert_eq!(None, value.exemplar); + assert_eq!(None, value.created); + } + _ => assert!(false, "wrong value type"), + } + } + #[test] fn encode_histogram() { let mut registry = Registry::default(); From c1b38a28861de21bc9c63a13a73521d4c54038eb Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Fri, 25 Mar 2022 09:27:11 +0900 Subject: [PATCH 22/73] src/encoding/proto: Remove redundant test Signed-off-by: ackintosh --- src/encoding/proto.rs | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 40e61c84..267a6011 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -455,43 +455,6 @@ mod tests { use std::borrow::Cow; use std::sync::atomic::AtomicI64; - #[test] - fn test_encode() { - let mut registry: Registry> = Registry::default(); - - let counter: Counter = Counter::default(); - registry.register_with_unit( - "my_counter", - "My counter", - Unit::Seconds, - Box::new(counter.clone()), - ); - counter.inc(); - - let family = Family::, Counter>::default(); - let sub_registry = - registry.sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value"))); - sub_registry.register( - "my_counter_family", - "My counter family", - Box::new(family.clone()), - ); - family - .get_or_create(&vec![ - ("method".to_string(), "GET".to_string()), - ("status".to_string(), "200".to_string()), - ]) - .inc(); - family - .get_or_create(&vec![ - ("method".to_string(), "POST".to_string()), - ("status".to_string(), "503".to_string()), - ]) - .inc(); - - println!("{:?}", encode(®istry)); - } - #[test] fn encode_counter_int() { let counter: Counter = Counter::default(); From 92b4493e196cca89c3d738bb6ab03d0ec16a6e47 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Sat, 26 Mar 2022 11:16:48 +0900 Subject: [PATCH 23/73] src/encoding/proto: Add doc Signed-off-by: ackintosh --- src/encoding/proto.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 267a6011..c30f2809 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -1,3 +1,27 @@ +//! Open Metrics protobuf implementation. +//! +//! ``` +//! # use prometheus_client::encoding::proto::encode; +//! # use prometheus_client::metrics::counter::Counter; +//! # use prometheus_client::registry::Registry; +//! # +//! # // Create registry and counter and register the latter with the former. +//! # let mut registry = Registry::default(); +//! # let counter: Counter = Counter::default(); +//! # registry.register( +//! # "my_counter", +//! # "This is my counter", +//! # counter.clone(), +//! # ); +//! # counter.inc(); +//! // Returns `MetricSet`, the top-level container type. Please refer to [openmetrics_data_model.proto](https://github.com/OpenObservability/OpenMetrics/blob/main/proto/openmetrics_data_model.proto) for details. +//! let metric_set = encode(®istry); +//! +//! let family = metric_set.metric_families.first().unwrap(); +//! assert_eq!("my_counter", family.name); +//! assert_eq!("My counter.", family.help); +//! ``` + // Include the `openmetrics_data_model` module, which is generated from `proto/openmetrics_data_model.proto`. pub mod openmetrics_data_model { include!(concat!(env!("OUT_DIR"), "/openmetrics.rs")); From 3cfe0858538632bb0e3ea341e72c8c5ef3890e73 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 27 Jul 2022 11:23:39 +0900 Subject: [PATCH 24/73] Introduce `protobuf` feature Signed-off-by: ackintosh --- Cargo.toml | 9 ++++++--- build.rs | 2 ++ src/encoding.rs | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7b2d4264..b943e921 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,9 @@ 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"] + [workspace] members = ["derive-text-encode"] @@ -18,8 +21,8 @@ dtoa = "1.0" itoa = "1.0" parking_lot = "0.12" prometheus-client-derive-text-encode = { version = "0.3.0", path = "derive-text-encode" } -prost = "0.9.0" -prost-types = "0.9.0" +prost = { version = "0.9.0", optional = true } +prost-types = { version = "0.9.0", optional = true } [dev-dependencies] async-std = { version = "1", features = ["attributes"] } @@ -32,7 +35,7 @@ tide = "0.16" actix-web = "4" [build-dependencies] -prost-build = "0.9.0" +prost-build = { version = "0.9.0", optional = true } [[bench]] name = "family" diff --git a/build.rs b/build.rs index 36a14ae6..d10b0197 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +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(()) } diff --git a/src/encoding.rs b/src/encoding.rs index 084e5100..d62cb3fc 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,4 +1,5 @@ //! Exposition format implementations. +#[cfg(feature = "protobuf")] pub mod proto; pub mod text; From ee6344af7d561a594c655fa6fccf207e65142009 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 27 Jul 2022 11:42:46 +0900 Subject: [PATCH 25/73] src/encoding/proto: Refactoring for loop Signed-off-by: ackintosh --- src/encoding/proto.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index c30f2809..4f4ab654 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -130,21 +130,13 @@ impl EncodeLabel for (K, V) { impl EncodeLabel for Vec { fn encode(&self) -> Vec { - let mut label = vec![]; - for t in self { - label.append(&mut t.encode()); - } - label + self.iter().map(|t| t.encode()).flatten().collect() } } impl EncodeLabel for &[T] { fn encode(&self) -> Vec { - let mut label = vec![]; - for t in self.iter() { - label.append(&mut t.encode()); - } - label + self.iter().map(|t| t.encode()).flatten().collect() } } From cc0df245454f5d776e1b65ce11713bebade20211 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Mon, 8 Aug 2022 17:00:11 +0900 Subject: [PATCH 26/73] Implement proc macro `EncodeProtobuf` Signed-off-by: ackintosh --- Cargo.toml | 5 +- derive-proto-encode/Cargo.toml | 23 ++++++ derive-proto-encode/src/lib.rs | 132 +++++++++++++++++++++++++++++++ derive-proto-encode/tests/lib.rs | 103 ++++++++++++++++++++++++ src/encoding/proto.rs | 3 + 5 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 derive-proto-encode/Cargo.toml create mode 100644 derive-proto-encode/src/lib.rs create mode 100644 derive-proto-encode/tests/lib.rs diff --git a/Cargo.toml b/Cargo.toml index b943e921..243dd333 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,15 +11,16 @@ homepage = "https://github.com/prometheus/client_rust" documentation = "https://docs.rs/prometheus-client" [features] -protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build"] +protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build", "dep:prometheus-client-derive-proto-encode"] [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 } 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 } diff --git a/derive-proto-encode/Cargo.toml b/derive-proto-encode/Cargo.toml new file mode 100644 index 00000000..2baf445c --- /dev/null +++ b/derive-proto-encode/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "prometheus-client-derive-proto-encode" +version = "0.1.0" +authors = ["Akihito Nakano "] +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 diff --git a/derive-proto-encode/src/lib.rs b/derive-proto-encode/src/lib.rs new file mode 100644 index 00000000..61589652 --- /dev/null +++ b/derive-proto-encode/src/lib.rs @@ -0,0 +1,132 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::DeriveInput; + +#[proc_macro_derive(EncodeProtobuf)] +pub fn derive_encode(input: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(input).unwrap(); + let name = &ast.ident; + + let body: TokenStream2 = 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! { + let mut labels = vec![]; + #push_labels + 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(); + label + } + } + }) + .collect(); + + quote! { + let label = match self { + #match_arms + }; + + vec![label] + } + } + syn::Data::Union(_) => panic!("Can not derive Encode for union."), + }; + + let gen = quote! { + impl prometheus_client::encoding::proto::EncodeLabel for #name { + fn encode(&self) -> Vec { + #body + } + } + }; + gen.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"), +]; diff --git a/derive-proto-encode/tests/lib.rs b/derive-proto-encode/tests/lib.rs new file mode 100644 index 00000000..f5fc97c4 --- /dev/null +++ b/derive-proto-encode/tests/lib.rs @@ -0,0 +1,103 @@ +use prometheus_client::encoding::proto::EncodeLabel; +use prometheus_client::encoding::proto::{encode, EncodeProtobuf}; +use prometheus_client::metrics::counter::Counter; +use prometheus_client::metrics::family::Family; +use prometheus_client::registry::Registry; +use std::fmt::{Display, Formatter}; + +#[test] +fn structs() { + #[derive(Clone, Hash, PartialEq, Eq, EncodeProtobuf)] + struct Labels { + method: Method, + path: String, + } + + #[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 => f.write_str("Get"), + Method::Put => f.write_str("Put"), + } + } + } + + let mut registry = Registry::default(); + let family = Family::::default(); + registry.register("my_counter", "This is my counter", family.clone()); + + // Record a single HTTP GET request. + family + .get_or_create(&Labels { + method: Method::Get, + path: "/metrics".to_string(), + }) + .inc(); + + // Encode all metrics in the registry in the OpenMetrics protobuf format. + let mut metric_set = encode(®istry); + let mut family: prometheus_client::encoding::proto::MetricFamily = + metric_set.metric_families.pop().unwrap(); + let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap(); + + let method = &metric.labels[0]; + assert_eq!("method", method.name); + assert_eq!("Get", method.value); + + let path = &metric.labels[1]; + assert_eq!("path", path.name); + assert_eq!("/metrics", path.value); +} + +#[test] +fn enums() { + #[derive(Clone, Hash, PartialEq, Eq, EncodeProtobuf)] + enum Method { + Get, + #[allow(dead_code)] + Put, + } + + let mut registry = Registry::default(); + let family = Family::::default(); + registry.register("my_counter", "This is my counter", family.clone()); + + // Record a single HTTP GET request. + family.get_or_create(&Method::Get).inc(); + + // Encode all metrics in the registry in the OpenMetrics protobuf format. + let mut metric_set = encode(®istry); + let mut family: prometheus_client::encoding::proto::MetricFamily = + metric_set.metric_families.pop().unwrap(); + let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap(); + + let label = &metric.labels[0]; + assert_eq!("Method", label.name); + assert_eq!("Get", label.value); +} + +#[test] +fn remap_keyword_identifiers() { + #[derive(EncodeProtobuf, Hash, Clone, Eq, PartialEq)] + struct Labels { + // `r#type` is problematic as `r#` is not a valid OpenMetrics label name + // but one needs to use keyword identifier syntax (aka. raw identifiers) + // as `type` is a keyword. + // + // Test makes sure `r#type` is replaced by `type` in the OpenMetrics + // output. + r#type: u64, + } + + let labels = Labels { r#type: 42 }.encode(); + + assert_eq!("type", labels[0].name); + assert_eq!("42", labels[0].value); +} diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 4f4ab654..1580aedc 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -38,6 +38,9 @@ use crate::registry::Registry; use std::collections::HashMap; use std::ops::Deref; +pub use openmetrics_data_model::*; +pub use prometheus_client_derive_proto_encode::*; + pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet where M: EncodeMetric, From 7040f2a18d5be324e7a4b98839f156d1a1ccaac2 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 10 Aug 2022 16:39:02 +0900 Subject: [PATCH 27/73] Add impl EncodeGaugeValue for u64 Signed-off-by: ackintosh --- src/encoding/proto.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 1580aedc..ebd25675 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -272,6 +272,12 @@ impl EncodeGaugeValue for i64 { } } +impl EncodeGaugeValue for u64 { + fn encode(&self) -> openmetrics_data_model::gauge_value::Value { + openmetrics_data_model::gauge_value::Value::IntValue(*self as i64) + } +} + impl EncodeGaugeValue for f64 { fn encode(&self) -> openmetrics_data_model::gauge_value::Value { openmetrics_data_model::gauge_value::Value::DoubleValue(*self) From 6cf6e09fa5aedf2ff517fdf960e4131f47de7d96 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sun, 31 Jul 2022 11:51:24 +0900 Subject: [PATCH 28/73] Try Associated Type Build error below occurs: error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> src/encoding/proto.rs:199:6 | 199 | impl<'a, N, A> EncodeMetric for Counter | ^^ unconstrained lifetime parameter Signed-off-by: ackintosh --- src/encoding/proto.rs | 70 +++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index ebd25675..fe2e49bc 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -80,19 +80,23 @@ impl From for openmetrics_data_model::MetricType { /// Trait implemented by each metric type, e.g. [`Counter`], to implement its encoding. pub trait EncodeMetric { + type Iterator: Iterator; + fn encode( &self, labels: Vec, - ) -> Vec; + ) -> Self::Iterator; fn metric_type(&self) -> MetricType; } -impl EncodeMetric for Box { +impl<'a> EncodeMetric for Box>> { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { self.deref().encode(labels) } @@ -105,11 +109,13 @@ pub trait SendEncodeMetric: EncodeMetric + Send {} impl SendEncodeMetric for T {} -impl EncodeMetric for Box { +impl<'a> EncodeMetric for Box>> { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { self.deref().encode(labels) } @@ -186,14 +192,16 @@ where N: EncodeCounterValue, A: counter::Atomic, { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { let mut metric = encode_counter_with_maybe_exemplar(self.get(), None); metric.labels = labels; - vec![metric] + vec![metric].iter() } fn metric_type(&self) -> MetricType { @@ -201,17 +209,19 @@ where } } -impl EncodeMetric for CounterWithExemplar +impl<'a, S, N, A> EncodeMetric for CounterWithExemplar where S: EncodeLabel, N: Clone + EncodeCounterValue, A: counter::Atomic, f64: From, { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { let (value, exemplar) = self.get(); let exemplar_proto = if let Some(e) = exemplar.as_ref() { @@ -223,7 +233,7 @@ where let mut metric = encode_counter_with_maybe_exemplar(value.clone(), exemplar_proto); metric.labels = labels; - vec![metric] + vec![metric].iter() } fn metric_type(&self) -> MetricType { @@ -284,15 +294,17 @@ impl EncodeGaugeValue for f64 { } } -impl EncodeMetric for Gauge +impl<'a, N, A> EncodeMetric for Gauge where N: EncodeGaugeValue, A: gauge::Atomic, { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { @@ -310,7 +322,7 @@ where }; metric.labels = labels; - vec![metric] + vec![metric].iter() } fn metric_type(&self) -> MetricType { @@ -321,16 +333,18 @@ where ///////////////////////////////////////////////////////////////////////////////// // Family -impl EncodeMetric for Family +impl<'a, S, M, C> EncodeMetric for Family where S: Clone + std::hash::Hash + Eq + EncodeLabel, M: EncodeMetric + TypedMetric, C: MetricConstructor, { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { let mut metrics = vec![]; let guard = self.read(); @@ -340,7 +354,7 @@ where metrics.extend(metric.encode(label)); } - metrics + metrics.iter() } fn metric_type(&self) -> MetricType { @@ -351,16 +365,18 @@ where ///////////////////////////////////////////////////////////////////////////////// // Histogram -impl EncodeMetric for Histogram { +impl<'a> EncodeMetric for Histogram { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { let (sum, count, buckets) = self.get(); // TODO: Would be better to use never type instead of `()`. let mut metric = encode_histogram_with_maybe_exemplars::<()>(sum, count, &buckets, None); metric.labels = labels; - vec![metric] + vec![metric].iter() } fn metric_type(&self) -> MetricType { @@ -368,20 +384,22 @@ impl EncodeMetric for Histogram { } } -impl EncodeMetric for HistogramWithExemplars +impl<'a, S> EncodeMetric for HistogramWithExemplars where S: EncodeLabel, { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { let inner = self.inner(); let (sum, count, buckets) = inner.histogram.get(); let mut metric = encode_histogram_with_maybe_exemplars(sum, count, &buckets, Some(&inner.exemplars)); metric.labels = labels; - vec![metric] + vec![metric].iter() } fn metric_type(&self) -> MetricType { @@ -432,14 +450,16 @@ fn encode_histogram_with_maybe_exemplars( ///////////////////////////////////////////////////////////////////////////////// // Info -impl EncodeMetric for Info +impl<'a, S> EncodeMetric for Info where S: EncodeLabel, { + type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + fn encode( &self, labels: Vec, - ) -> Vec { + ) -> Self::Iterator { let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { @@ -459,7 +479,7 @@ where vec![metric_point] }; - vec![metric] + vec![metric].iter() } fn metric_type(&self) -> MetricType { From 887beffd680c2e4cdb7a85550986b5a778e5838f Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 3 Aug 2022 16:31:44 +0900 Subject: [PATCH 29/73] Fix life parameter issue Build error: error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> src/encoding/proto.rs:199:6 | 199 | impl<'a, N, A> EncodeMetric for Counter | ^^ unconstrained lifetime parameter Signed-off-by: ackintosh --- src/encoding/proto.rs | 105 ++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 66 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index fe2e49bc..898e65ed 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -37,6 +37,7 @@ use crate::metrics::{counter, gauge, MetricType, TypedMetric}; use crate::registry::Registry; use std::collections::HashMap; use std::ops::Deref; +use std::vec::IntoIter; pub use openmetrics_data_model::*; pub use prometheus_client_derive_proto_encode::*; @@ -58,7 +59,7 @@ where family.unit = unit.as_str().to_string(); } family.help = desc.help().to_string(); - family.metrics = metric.encode(desc.labels().encode()); + family.metrics = metric.encode(desc.labels().encode()).collect::>(); metric_set.metric_families.push(family); } @@ -82,21 +83,15 @@ impl From for openmetrics_data_model::MetricType { pub trait EncodeMetric { type Iterator: Iterator; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator; + fn encode(&self, labels: Vec) -> Self::Iterator; fn metric_type(&self) -> MetricType; } -impl<'a> EncodeMetric for Box>> { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; - - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { +impl EncodeMetric for Box>> { + type Iterator = IntoIter; + + fn encode(&self, labels: Vec) -> Self::Iterator { self.deref().encode(labels) } @@ -109,13 +104,12 @@ pub trait SendEncodeMetric: EncodeMetric + Send {} impl SendEncodeMetric for T {} -impl<'a> EncodeMetric for Box>> { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; +impl EncodeMetric + for Box>> +{ + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { self.deref().encode(labels) } @@ -192,16 +186,13 @@ where N: EncodeCounterValue, A: counter::Atomic, { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { let mut metric = encode_counter_with_maybe_exemplar(self.get(), None); metric.labels = labels; - vec![metric].iter() + vec![metric].into_iter() } fn metric_type(&self) -> MetricType { @@ -209,19 +200,16 @@ where } } -impl<'a, S, N, A> EncodeMetric for CounterWithExemplar +impl EncodeMetric for CounterWithExemplar where S: EncodeLabel, N: Clone + EncodeCounterValue, A: counter::Atomic, f64: From, { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { let (value, exemplar) = self.get(); let exemplar_proto = if let Some(e) = exemplar.as_ref() { @@ -233,7 +221,7 @@ where let mut metric = encode_counter_with_maybe_exemplar(value.clone(), exemplar_proto); metric.labels = labels; - vec![metric].iter() + vec![metric].into_iter() } fn metric_type(&self) -> MetricType { @@ -294,17 +282,14 @@ impl EncodeGaugeValue for f64 { } } -impl<'a, N, A> EncodeMetric for Gauge +impl EncodeMetric for Gauge where N: EncodeGaugeValue, A: gauge::Atomic, { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { @@ -322,7 +307,7 @@ where }; metric.labels = labels; - vec![metric].iter() + vec![metric].into_iter() } fn metric_type(&self) -> MetricType { @@ -333,18 +318,15 @@ where ///////////////////////////////////////////////////////////////////////////////// // Family -impl<'a, S, M, C> EncodeMetric for Family +impl EncodeMetric for Family where S: Clone + std::hash::Hash + Eq + EncodeLabel, M: EncodeMetric + TypedMetric, C: MetricConstructor, { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { let mut metrics = vec![]; let guard = self.read(); @@ -354,7 +336,7 @@ where metrics.extend(metric.encode(label)); } - metrics.iter() + metrics.into_iter() } fn metric_type(&self) -> MetricType { @@ -365,18 +347,15 @@ where ///////////////////////////////////////////////////////////////////////////////// // Histogram -impl<'a> EncodeMetric for Histogram { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; +impl EncodeMetric for Histogram { + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { let (sum, count, buckets) = self.get(); // TODO: Would be better to use never type instead of `()`. let mut metric = encode_histogram_with_maybe_exemplars::<()>(sum, count, &buckets, None); metric.labels = labels; - vec![metric].iter() + vec![metric].into_iter() } fn metric_type(&self) -> MetricType { @@ -384,22 +363,19 @@ impl<'a> EncodeMetric for Histogram { } } -impl<'a, S> EncodeMetric for HistogramWithExemplars +impl EncodeMetric for HistogramWithExemplars where S: EncodeLabel, { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { let inner = self.inner(); let (sum, count, buckets) = inner.histogram.get(); let mut metric = encode_histogram_with_maybe_exemplars(sum, count, &buckets, Some(&inner.exemplars)); metric.labels = labels; - vec![metric].iter() + vec![metric].into_iter() } fn metric_type(&self) -> MetricType { @@ -450,16 +426,13 @@ fn encode_histogram_with_maybe_exemplars( ///////////////////////////////////////////////////////////////////////////////// // Info -impl<'a, S> EncodeMetric for Info +impl EncodeMetric for Info where S: EncodeLabel, { - type Iterator = std::slice::Iter<'a, openmetrics_data_model::Metric>; + type Iterator = IntoIter; - fn encode( - &self, - labels: Vec, - ) -> Self::Iterator { + fn encode(&self, labels: Vec) -> Self::Iterator { let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { @@ -479,7 +452,7 @@ where vec![metric_point] }; - vec![metric].iter() + vec![metric].into_iter() } fn metric_type(&self) -> MetricType { From 49a3dfb6e5648fe2f2efc5daa27dd92b5e95f5de Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 3 Aug 2022 16:49:35 +0900 Subject: [PATCH 30/73] Make EncodeLabel::encode return iterator Signed-off-by: ackintosh --- src/encoding/proto.rs | 46 +++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 898e65ed..01304948 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -59,7 +59,9 @@ where family.unit = unit.as_str().to_string(); } family.help = desc.help().to_string(); - family.metrics = metric.encode(desc.labels().encode()).collect::>(); + family.metrics = metric + .encode(desc.labels().encode().collect::>()) + .collect::>(); metric_set.metric_families.push(family); } @@ -119,33 +121,51 @@ impl EncodeMetric } pub trait EncodeLabel { - fn encode(&self) -> Vec; + type Iterator: Iterator; + + fn encode(&self) -> Self::Iterator; } impl EncodeLabel for (K, V) { - fn encode(&self) -> Vec { + type Iterator = IntoIter; + + fn encode(&self) -> Self::Iterator { let mut label = openmetrics_data_model::Label::default(); label.name = self.0.to_string(); label.value = self.1.to_string(); - vec![label] + vec![label].into_iter() } } impl EncodeLabel for Vec { - fn encode(&self) -> Vec { - self.iter().map(|t| t.encode()).flatten().collect() + type Iterator = IntoIter; + + fn encode(&self) -> Self::Iterator { + self.iter() + .map(|t| t.encode()) + .flatten() + .collect::>() + .into_iter() } } impl EncodeLabel for &[T] { - fn encode(&self) -> Vec { - self.iter().map(|t| t.encode()).flatten().collect() + type Iterator = IntoIter; + + fn encode(&self) -> Self::Iterator { + self.iter() + .map(|t| t.encode()) + .flatten() + .collect::>() + .into_iter() } } impl EncodeLabel for () { - fn encode(&self) -> Vec { - vec![] + type Iterator = IntoIter; + + fn encode(&self) -> Self::Iterator { + vec![].into_iter() } } @@ -157,7 +177,7 @@ where { let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); exemplar_proto.value = exemplar.value.clone().into(); - exemplar_proto.label = exemplar.label_set.encode(); + exemplar_proto.label = exemplar.label_set.encode().collect::>(); exemplar_proto } @@ -331,7 +351,7 @@ where let guard = self.read(); for (label_set, metric) in guard.iter() { - let mut label = label_set.encode(); + let mut label = label_set.encode().collect::>(); label.append(&mut labels.clone()); metrics.extend(metric.encode(label)); } @@ -438,7 +458,7 @@ where metric.metric_points = { let mut metric_point = openmetrics_data_model::MetricPoint::default(); metric_point.value = { - let mut label = self.0.encode(); + let mut label = self.0.encode().collect::>(); label.append(&mut labels.clone()); let mut info_value = openmetrics_data_model::InfoValue::default(); From 3b8dc35606e14dba18aec5118f5b46f877a99195 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 8 Aug 2022 20:07:23 +0900 Subject: [PATCH 31/73] src/encoding/proto: Refactor EncodeLabel and EncodeMetric Signed-off-by: ackintosh --- src/encoding/proto.rs | 154 ++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 75 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 01304948..24880c81 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -90,8 +90,10 @@ pub trait EncodeMetric { fn metric_type(&self) -> MetricType; } -impl EncodeMetric for Box>> { - type Iterator = IntoIter; +impl EncodeMetric + for Box>>> +{ + type Iterator = Box>; fn encode(&self, labels: Vec) -> Self::Iterator { self.deref().encode(labels) @@ -107,9 +109,11 @@ pub trait SendEncodeMetric: EncodeMetric + Send {} impl SendEncodeMetric for T {} impl EncodeMetric - for Box>> + for Box< + dyn SendEncodeMetric>>, + > { - type Iterator = IntoIter; + type Iterator = Box>; fn encode(&self, labels: Vec) -> Self::Iterator { self.deref().encode(labels) @@ -120,59 +124,49 @@ impl EncodeMetric } } +// TODO: Rename to EncodeLabels? pub trait EncodeLabel { type Iterator: Iterator; - fn encode(&self) -> Self::Iterator; + fn encode(self) -> Self::Iterator; } -impl EncodeLabel for (K, V) { - type Iterator = IntoIter; - - fn encode(&self) -> Self::Iterator { +impl Into for &(K, V) { + fn into(self) -> openmetrics_data_model::Label { let mut label = openmetrics_data_model::Label::default(); label.name = self.0.to_string(); label.value = self.1.to_string(); - vec![label].into_iter() - } -} - -impl EncodeLabel for Vec { - type Iterator = IntoIter; - - fn encode(&self) -> Self::Iterator { - self.iter() - .map(|t| t.encode()) - .flatten() - .collect::>() - .into_iter() + label } } -impl EncodeLabel for &[T] { - type Iterator = IntoIter; +// TODO: Is this needed? We already have `&'a [T]` below. +impl<'a, T> EncodeLabel for &'a Vec +where + for<'b> &'b T: Into, +{ + type Iterator = Box + 'a>; - fn encode(&self) -> Self::Iterator { - self.iter() - .map(|t| t.encode()) - .flatten() - .collect::>() - .into_iter() + fn encode(self) -> Self::Iterator { + Box::new(self.iter().map(|t| t.into())) } } -impl EncodeLabel for () { - type Iterator = IntoIter; +impl<'a, T> EncodeLabel for &'a [T] +where + for<'b> &'b T: Into, +{ + type Iterator = Box + 'a>; - fn encode(&self) -> Self::Iterator { - vec![].into_iter() + fn encode(self) -> Self::Iterator { + Box::new(self.iter().map(|t| t.into())) } } fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar where N: Clone, - S: EncodeLabel, + for<'a> &'a S: EncodeLabel, f64: From, // required because Exemplar.value is defined as `double` in protobuf { let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); @@ -206,13 +200,13 @@ where N: EncodeCounterValue, A: counter::Atomic, { - type Iterator = IntoIter; + type Iterator = std::iter::Once; fn encode(&self, labels: Vec) -> Self::Iterator { let mut metric = encode_counter_with_maybe_exemplar(self.get(), None); metric.labels = labels; - vec![metric].into_iter() + std::iter::once(metric) } fn metric_type(&self) -> MetricType { @@ -220,14 +214,14 @@ where } } -impl EncodeMetric for CounterWithExemplar +impl<'a, S, N, A> EncodeMetric for CounterWithExemplar where - S: EncodeLabel, + for<'b> &'b S: EncodeLabel, N: Clone + EncodeCounterValue, A: counter::Atomic, f64: From, { - type Iterator = IntoIter; + type Iterator = std::iter::Once; fn encode(&self, labels: Vec) -> Self::Iterator { let (value, exemplar) = self.get(); @@ -241,7 +235,7 @@ where let mut metric = encode_counter_with_maybe_exemplar(value.clone(), exemplar_proto); metric.labels = labels; - vec![metric].into_iter() + std::iter::once(metric) } fn metric_type(&self) -> MetricType { @@ -302,12 +296,12 @@ impl EncodeGaugeValue for f64 { } } -impl EncodeMetric for Gauge +impl<'a, N, A> EncodeMetric for Gauge where N: EncodeGaugeValue, A: gauge::Atomic, { - type Iterator = IntoIter; + type Iterator = std::iter::Once; fn encode(&self, labels: Vec) -> Self::Iterator { let mut metric = openmetrics_data_model::Metric::default(); @@ -327,7 +321,7 @@ where }; metric.labels = labels; - vec![metric].into_iter() + std::iter::once(metric) } fn metric_type(&self) -> MetricType { @@ -338,25 +332,30 @@ where ///////////////////////////////////////////////////////////////////////////////// // Family -impl EncodeMetric for Family +impl<'c, S, M, C> EncodeMetric for Family where - S: Clone + std::hash::Hash + Eq + EncodeLabel, + S: Clone + std::hash::Hash + Eq, + for<'b> &'b S: EncodeLabel, M: EncodeMetric + TypedMetric, C: MetricConstructor, { type Iterator = IntoIter; fn encode(&self, labels: Vec) -> Self::Iterator { - let mut metrics = vec![]; - - let guard = self.read(); - for (label_set, metric) in guard.iter() { - let mut label = label_set.encode().collect::>(); - label.append(&mut labels.clone()); - metrics.extend(metric.encode(label)); - } - - metrics.into_iter() + self.read() + .iter() + .map(|(label_set, metric)| { + let mut labels = labels.clone(); + labels.extend(label_set.encode()); + metric.encode(labels) + }) + .flatten() + // TODO: Ideally we would not have to collect into a vector here, + // though we have to as we borrow from the `MutexGuard`. Once + // https://github.com/prometheus/client_rust/pull/78/ merged, we + // might be able to leverage `MutexGuard::map`. + .collect::>() + .into_iter() } fn metric_type(&self) -> MetricType { @@ -368,14 +367,17 @@ where // Histogram impl EncodeMetric for Histogram { - type Iterator = IntoIter; + type Iterator = std::iter::Once; fn encode(&self, labels: Vec) -> Self::Iterator { let (sum, count, buckets) = self.get(); // TODO: Would be better to use never type instead of `()`. - let mut metric = encode_histogram_with_maybe_exemplars::<()>(sum, count, &buckets, None); + // TODO: Revert (String, String)? + let mut metric = encode_histogram_with_maybe_exemplars::>( + sum, count, &buckets, None, + ); metric.labels = labels; - vec![metric].into_iter() + std::iter::once(metric) } fn metric_type(&self) -> MetricType { @@ -385,9 +387,9 @@ impl EncodeMetric for Histogram { impl EncodeMetric for HistogramWithExemplars where - S: EncodeLabel, + for<'b> &'b S: EncodeLabel, { - type Iterator = IntoIter; + type Iterator = std::iter::Once; fn encode(&self, labels: Vec) -> Self::Iterator { let inner = self.inner(); @@ -395,7 +397,7 @@ where let mut metric = encode_histogram_with_maybe_exemplars(sum, count, &buckets, Some(&inner.exemplars)); metric.labels = labels; - vec![metric].into_iter() + std::iter::once(metric) } fn metric_type(&self) -> MetricType { @@ -403,12 +405,15 @@ where } } -fn encode_histogram_with_maybe_exemplars( +fn encode_histogram_with_maybe_exemplars<'a, S>( sum: f64, count: u64, buckets: &[(f64, u64)], - exemplars: Option<&HashMap>>, -) -> openmetrics_data_model::Metric { + exemplars: Option<&'a HashMap>>, +) -> openmetrics_data_model::Metric +where + for<'b> &'b S: EncodeLabel, +{ let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { @@ -448,21 +453,20 @@ fn encode_histogram_with_maybe_exemplars( impl EncodeMetric for Info where - S: EncodeLabel, + for<'b> &'b S: EncodeLabel, { - type Iterator = IntoIter; + type Iterator = std::iter::Once; - fn encode(&self, labels: Vec) -> Self::Iterator { + fn encode(&self, mut labels: Vec) -> Self::Iterator { let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { let mut metric_point = openmetrics_data_model::MetricPoint::default(); metric_point.value = { - let mut label = self.0.encode().collect::>(); - label.append(&mut labels.clone()); + labels.extend(self.0.encode()); let mut info_value = openmetrics_data_model::InfoValue::default(); - info_value.info = label; + info_value.info = labels; Some(openmetrics_data_model::metric_point::Value::InfoValue( info_value, @@ -472,7 +476,7 @@ where vec![metric_point] }; - vec![metric].into_iter() + std::iter::once(metric) } fn metric_type(&self) -> MetricType { @@ -571,7 +575,7 @@ mod tests { fn encode_counter_with_exemplar() { let mut registry = Registry::default(); - let counter_with_exemplar: CounterWithExemplar<(String, f64), f64> = + let counter_with_exemplar: CounterWithExemplar, f64> = CounterWithExemplar::default(); registry.register( "my_counter_with_exemplar", @@ -579,7 +583,7 @@ mod tests { counter_with_exemplar.clone(), ); - counter_with_exemplar.inc_by(1.0, Some(("user_id".to_string(), 42.0))); + counter_with_exemplar.inc_by(1.0, Some(vec![("user_id".to_string(), 42.0)])); let metric_set = encode(®istry); @@ -766,7 +770,7 @@ mod tests { let mut registry = Registry::default(); let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10)); registry.register("my_histogram", "My histogram", histogram.clone()); - histogram.observe(1.0, Some(("user_id".to_string(), 42u64))); + histogram.observe(1.0, Some(vec![("user_id".to_string(), 42u64)])); let metric_set = encode(®istry); From f07148b0979c65a8f0bbce3cd51e023176bcf341 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Fri, 12 Aug 2022 10:02:24 +0900 Subject: [PATCH 32/73] Fix assertions because of changes of the order of result Signed-off-by: ackintosh --- src/encoding/proto.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 24880c81..28d421a2 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -714,12 +714,12 @@ mod tests { let metric = family.metrics.first().unwrap(); assert_eq!(3, metric.labels.len()); - assert_eq!("method", metric.labels[0].name); - assert_eq!("GET", metric.labels[0].value); - assert_eq!("status", metric.labels[1].name); - assert_eq!("200", metric.labels[1].value); - assert_eq!("my_key", metric.labels[2].name); - assert_eq!("my_value", metric.labels[2].value); + assert_eq!("my_key", metric.labels[0].name); + assert_eq!("my_value", metric.labels[0].value); + assert_eq!("method", metric.labels[1].name); + assert_eq!("GET", metric.labels[1].value); + assert_eq!("status", metric.labels[2].name); + assert_eq!("200", metric.labels[2].value); match extract_metric_point_value(metric_set) { openmetrics_data_model::metric_point::Value::CounterValue(value) => { From 0583a66f0ef9215316eac80f7bb990ceac7e53e9 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Fri, 12 Aug 2022 10:03:01 +0900 Subject: [PATCH 33/73] Remove unnecessary lifetime parameter Signed-off-by: ackintosh --- src/encoding/proto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 28d421a2..d01f8536 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -332,7 +332,7 @@ where ///////////////////////////////////////////////////////////////////////////////// // Family -impl<'c, S, M, C> EncodeMetric for Family +impl EncodeMetric for Family where S: Clone + std::hash::Hash + Eq, for<'b> &'b S: EncodeLabel, From 88fe41ed972e7183cf53d98446f38ca3112b7d59 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Fri, 12 Aug 2022 10:04:18 +0900 Subject: [PATCH 34/73] Rename EncodeLabel -> EncodeLabels Signed-off-by: ackintosh --- src/encoding/proto.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index d01f8536..2a19d4df 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -124,8 +124,7 @@ impl EncodeMetric } } -// TODO: Rename to EncodeLabels? -pub trait EncodeLabel { +pub trait EncodeLabels { type Iterator: Iterator; fn encode(self) -> Self::Iterator; @@ -141,7 +140,7 @@ impl Into for &(K, V) { } // TODO: Is this needed? We already have `&'a [T]` below. -impl<'a, T> EncodeLabel for &'a Vec +impl<'a, T> EncodeLabels for &'a Vec where for<'b> &'b T: Into, { @@ -152,7 +151,7 @@ where } } -impl<'a, T> EncodeLabel for &'a [T] +impl<'a, T> EncodeLabels for &'a [T] where for<'b> &'b T: Into, { @@ -166,7 +165,7 @@ where fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar where N: Clone, - for<'a> &'a S: EncodeLabel, + for<'a> &'a S: EncodeLabels, f64: From, // required because Exemplar.value is defined as `double` in protobuf { let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); @@ -216,7 +215,7 @@ where impl<'a, S, N, A> EncodeMetric for CounterWithExemplar where - for<'b> &'b S: EncodeLabel, + for<'b> &'b S: EncodeLabels, N: Clone + EncodeCounterValue, A: counter::Atomic, f64: From, @@ -335,7 +334,7 @@ where impl EncodeMetric for Family where S: Clone + std::hash::Hash + Eq, - for<'b> &'b S: EncodeLabel, + for<'b> &'b S: EncodeLabels, M: EncodeMetric + TypedMetric, C: MetricConstructor, { @@ -387,7 +386,7 @@ impl EncodeMetric for Histogram { impl EncodeMetric for HistogramWithExemplars where - for<'b> &'b S: EncodeLabel, + for<'b> &'b S: EncodeLabels, { type Iterator = std::iter::Once; @@ -412,7 +411,7 @@ fn encode_histogram_with_maybe_exemplars<'a, S>( exemplars: Option<&'a HashMap>>, ) -> openmetrics_data_model::Metric where - for<'b> &'b S: EncodeLabel, + for<'b> &'b S: EncodeLabels, { let mut metric = openmetrics_data_model::Metric::default(); @@ -453,7 +452,7 @@ where impl EncodeMetric for Info where - for<'b> &'b S: EncodeLabel, + for<'b> &'b S: EncodeLabels, { type Iterator = std::iter::Once; From e5a37a3361b4500c2b5dfc2102bc3ca63d8abf16 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Fri, 12 Aug 2022 10:38:57 +0900 Subject: [PATCH 35/73] Using `Void` to indicate the impossible case Signed-off-by: ackintosh --- Cargo.toml | 1 + src/encoding/proto.rs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 243dd333..d8e76bae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ prometheus-client-derive-proto-encode = { version = "0.1.0", path = "derive-prot 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 = "1.0" [dev-dependencies] async-std = { version = "1", features = ["attributes"] } diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 2a19d4df..b1be09e4 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -38,6 +38,7 @@ use crate::registry::Registry; use std::collections::HashMap; use std::ops::Deref; use std::vec::IntoIter; +use void::Void; pub use openmetrics_data_model::*; pub use prometheus_client_derive_proto_encode::*; @@ -162,6 +163,14 @@ where } } +impl<'a> EncodeLabels for &'a Void { + type Iterator = Box>; + + fn encode(self) -> Self::Iterator { + unreachable!() + } +} + fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar where N: Clone, @@ -370,11 +379,8 @@ impl EncodeMetric for Histogram { fn encode(&self, labels: Vec) -> Self::Iterator { let (sum, count, buckets) = self.get(); - // TODO: Would be better to use never type instead of `()`. - // TODO: Revert (String, String)? - let mut metric = encode_histogram_with_maybe_exemplars::>( - sum, count, &buckets, None, - ); + // TODO: Would be better to use never type instead of `Void`. + let mut metric = encode_histogram_with_maybe_exemplars::(sum, count, &buckets, None); metric.labels = labels; std::iter::once(metric) } From 7d2c6a024d022ac1e706c575d41e792579b79b3a Mon Sep 17 00:00:00 2001 From: ackintosh Date: Fri, 12 Aug 2022 11:17:28 +0900 Subject: [PATCH 36/73] Changed proc macro accordingly since `encode` returns iterator Signed-off-by: ackintosh --- derive-proto-encode/src/lib.rs | 10 ++++++---- derive-proto-encode/tests/lib.rs | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/derive-proto-encode/src/lib.rs b/derive-proto-encode/src/lib.rs index 61589652..09b30e73 100644 --- a/derive-proto-encode/src/lib.rs +++ b/derive-proto-encode/src/lib.rs @@ -33,7 +33,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { quote! { let mut labels = vec![]; #push_labels - labels + Box::new(labels.into_iter()) } } syn::Fields::Unnamed(_) => { @@ -62,15 +62,17 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { #match_arms }; - vec![label] + Box::new(vec![label].into_iter()) } } syn::Data::Union(_) => panic!("Can not derive Encode for union."), }; let gen = quote! { - impl prometheus_client::encoding::proto::EncodeLabel for #name { - fn encode(&self) -> Vec { + impl<'a> prometheus_client::encoding::proto::EncodeLabels for &'a #name { + type Iterator = Box + 'a>; + + fn encode(self) -> Self::Iterator { #body } } diff --git a/derive-proto-encode/tests/lib.rs b/derive-proto-encode/tests/lib.rs index f5fc97c4..5c0e1ad2 100644 --- a/derive-proto-encode/tests/lib.rs +++ b/derive-proto-encode/tests/lib.rs @@ -1,4 +1,4 @@ -use prometheus_client::encoding::proto::EncodeLabel; +use prometheus_client::encoding::proto::EncodeLabels; use prometheus_client::encoding::proto::{encode, EncodeProtobuf}; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; @@ -96,7 +96,7 @@ fn remap_keyword_identifiers() { r#type: u64, } - let labels = Labels { r#type: 42 }.encode(); + let labels = Labels { r#type: 42 }.encode().collect::>(); assert_eq!("type", labels[0].name); assert_eq!("42", labels[0].value); From 2e55b46a413d4b1bab3653de5d12ad6c3922634c Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 13 Aug 2022 08:37:50 +0900 Subject: [PATCH 37/73] Rename the proc macro since the name was a bit redundant Signed-off-by: ackintosh --- derive-proto-encode/src/lib.rs | 2 +- derive-proto-encode/tests/lib.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/derive-proto-encode/src/lib.rs b/derive-proto-encode/src/lib.rs index 09b30e73..0ad0ad98 100644 --- a/derive-proto-encode/src/lib.rs +++ b/derive-proto-encode/src/lib.rs @@ -3,7 +3,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::DeriveInput; -#[proc_macro_derive(EncodeProtobuf)] +#[proc_macro_derive(Encode)] pub fn derive_encode(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); let name = &ast.ident; diff --git a/derive-proto-encode/tests/lib.rs b/derive-proto-encode/tests/lib.rs index 5c0e1ad2..de18b4ab 100644 --- a/derive-proto-encode/tests/lib.rs +++ b/derive-proto-encode/tests/lib.rs @@ -1,5 +1,5 @@ use prometheus_client::encoding::proto::EncodeLabels; -use prometheus_client::encoding::proto::{encode, EncodeProtobuf}; +use prometheus_client::encoding::proto::{encode, Encode}; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; @@ -7,7 +7,7 @@ use std::fmt::{Display, Formatter}; #[test] fn structs() { - #[derive(Clone, Hash, PartialEq, Eq, EncodeProtobuf)] + #[derive(Clone, Hash, PartialEq, Eq, Encode)] struct Labels { method: Method, path: String, @@ -58,7 +58,7 @@ fn structs() { #[test] fn enums() { - #[derive(Clone, Hash, PartialEq, Eq, EncodeProtobuf)] + #[derive(Clone, Hash, PartialEq, Eq, Encode)] enum Method { Get, #[allow(dead_code)] @@ -85,7 +85,7 @@ fn enums() { #[test] fn remap_keyword_identifiers() { - #[derive(EncodeProtobuf, Hash, Clone, Eq, PartialEq)] + #[derive(Encode, Hash, Clone, Eq, PartialEq)] struct Labels { // `r#type` is problematic as `r#` is not a valid OpenMetrics label name // but one needs to use keyword identifier syntax (aka. raw identifiers) From 83a0b89975330913692680cfa73867f5583e05ef Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 13 Aug 2022 10:00:55 +0900 Subject: [PATCH 38/73] Add a test for Family (counter and histogram) Signed-off-by: ackintosh --- src/encoding/proto.rs | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index b1be09e4..0d801e96 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -109,12 +109,8 @@ pub trait SendEncodeMetric: EncodeMetric + Send {} impl SendEncodeMetric for T {} -impl EncodeMetric - for Box< - dyn SendEncodeMetric>>, - > -{ - type Iterator = Box>; +impl EncodeMetric for Box>> { + type Iterator = IntoIter; fn encode(&self, labels: Vec) -> Self::Iterator { self.deref().encode(labels) @@ -805,6 +801,38 @@ mod tests { } } + #[test] + fn encode_family_counter_histogram() { + let mut registry = Registry::< + Box>>, + >::default(); + + let counter_family = Family::, Counter>::default(); + let histogram_family = + Family::, Histogram>::new_with_constructor(|| { + Histogram::new(exponential_buckets(1.0, 2.0, 10)) + }); + + registry.register("my_counter", "My counter", Box::new(counter_family.clone())); + registry.register( + "my_histogram", + "My histogram", + Box::new(histogram_family.clone()), + ); + + counter_family + .get_or_create(&vec![("path".to_string(), "/".to_string())]) + .inc(); + + histogram_family + .get_or_create(&vec![("path".to_string(), "/".to_string())]) + .observe(1.0); + + let metric_set = encode(®istry); + assert_eq!("my_counter", metric_set.metric_families[0].name); + assert_eq!("my_histogram", metric_set.metric_families[1].name); + } + #[test] fn encode_info() { let mut registry = Registry::default(); From 00dcdd1833ed6768a4b54a47bfadbd8f8cfe9583 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 13 Aug 2022 10:20:40 +0900 Subject: [PATCH 39/73] Add benchmark code for proto encoding Signed-off-by: ackintosh --- Cargo.toml | 5 +++ benches/encoding/proto.rs | 85 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 benches/encoding/proto.rs diff --git a/Cargo.toml b/Cargo.toml index d8e76bae..eec63f34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,3 +47,8 @@ harness = false name = "text" path = "benches/encoding/text.rs" harness = false + +[[bench]] +name = "proto" +path = "benches/encoding/proto.rs" +harness = false diff --git a/benches/encoding/proto.rs b/benches/encoding/proto.rs new file mode 100644 index 00000000..81dbc73a --- /dev/null +++ b/benches/encoding/proto.rs @@ -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}; +use std::vec::IntoIter; + +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"), + } + } + } + + let mut registry = Registry::< + Box>>, + >::default(); + + for i in 0..100 { + let counter_family = Family::::default(); + let histogram_family = Family::::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(&Labels { + path: format!("/path/{}", i), + method: Method::Get, + some_number: j.into(), + }) + .observe(j.into()); + } + } + + b.iter(|| { + let metric_set = encode(®istry); + black_box(metric_set); + }) + }); +} + +criterion_group!(benches, proto); +criterion_main!(benches); From f20ae6b9b341d7ade4fdb0a0914f4bbf1df1c173 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 13 Aug 2022 11:19:22 +0900 Subject: [PATCH 40/73] Add a test for Family, Counter and Histogram Signed-off-by: ackintosh --- src/encoding/proto.rs | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 0d801e96..7fbd28af 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -833,6 +833,53 @@ mod tests { assert_eq!("my_histogram", metric_set.metric_families[1].name); } + #[test] + fn encode_family_and_counter_and_histogram() { + let mut registry = Registry::< + Box>>>, + >::default(); + + // Family + let counter_family = Family::, Counter>::default(); + let histogram_family = + Family::, Histogram>::new_with_constructor(|| { + Histogram::new(exponential_buckets(1.0, 2.0, 10)) + }); + + registry.register( + "my_family_counter", + "My counter", + Box::new(counter_family.clone()), + ); + registry.register( + "my_family_histogram", + "My histogram", + Box::new(histogram_family.clone()), + ); + + counter_family + .get_or_create(&vec![("path".to_string(), "/".to_string())]) + .inc(); + + histogram_family + .get_or_create(&vec![("path".to_string(), "/".to_string())]) + .observe(1.0); + + // Counter + let counter: Counter = Counter::default(); + registry.register("my_counter", "My counter", Box::new(counter.clone())); + counter.inc(); + + // Histogram + let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10)); + registry.register("my_histogram", "My histogram", Box::new(histogram.clone())); + histogram.observe(1.0); + + let metric_set = encode(®istry); + assert_eq!("my_counter", metric_set.metric_families[0].name); + assert_eq!("my_histogram", metric_set.metric_families[1].name); + } + #[test] fn encode_info() { let mut registry = Registry::default(); From 50ca0a968c7a7051c2b9b24d92ba8de7329a5fe7 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Mon, 15 Aug 2022 09:23:38 +0900 Subject: [PATCH 41/73] Remove `Box` to reduce allocation count Signed-off-by: ackintosh --- derive-proto-encode/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/derive-proto-encode/src/lib.rs b/derive-proto-encode/src/lib.rs index 0ad0ad98..95e3be84 100644 --- a/derive-proto-encode/src/lib.rs +++ b/derive-proto-encode/src/lib.rs @@ -33,7 +33,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { quote! { let mut labels = vec![]; #push_labels - Box::new(labels.into_iter()) + labels.into_iter() } } syn::Fields::Unnamed(_) => { @@ -62,7 +62,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { #match_arms }; - Box::new(vec![label].into_iter()) + vec![label].into_iter() } } syn::Data::Union(_) => panic!("Can not derive Encode for union."), @@ -70,7 +70,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { let gen = quote! { impl<'a> prometheus_client::encoding::proto::EncodeLabels for &'a #name { - type Iterator = Box + 'a>; + type Iterator = std::vec::IntoIter; fn encode(self) -> Self::Iterator { #body From 6afc9b77b0781acb4e322da36ab9f46f23991a75 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Mon, 15 Aug 2022 09:53:03 +0900 Subject: [PATCH 42/73] Use std::iter::Once on enums as enum returns single element Signed-off-by: ackintosh --- benches/encoding/proto.rs | 15 ++++++++----- derive-proto-encode/src/lib.rs | 41 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/benches/encoding/proto.rs b/benches/encoding/proto.rs index 81dbc73a..59356687 100644 --- a/benches/encoding/proto.rs +++ b/benches/encoding/proto.rs @@ -34,13 +34,20 @@ pub fn proto(c: &mut Criterion) { } } + #[derive(Clone, Hash, PartialEq, Eq, Encode)] + enum Region { + Africa, + #[allow(dead_code)] + Asia, + } + let mut registry = Registry::< Box>>, >::default(); for i in 0..100 { let counter_family = Family::::default(); - let histogram_family = Family::::new_with_constructor(|| { + let histogram_family = Family::::new_with_constructor(|| { Histogram::new(exponential_buckets(1.0, 2.0, 10)) }); @@ -65,11 +72,7 @@ pub fn proto(c: &mut Criterion) { .inc(); histogram_family - .get_or_create(&Labels { - path: format!("/path/{}", i), - method: Method::Get, - some_number: j.into(), - }) + .get_or_create(&Region::Africa) .observe(j.into()); } } diff --git a/derive-proto-encode/src/lib.rs b/derive-proto-encode/src/lib.rs index 95e3be84..e73c3a30 100644 --- a/derive-proto-encode/src/lib.rs +++ b/derive-proto-encode/src/lib.rs @@ -8,7 +8,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); let name = &ast.ident; - let body: TokenStream2 = match ast.data { + match ast.data { syn::Data::Struct(s) => match s.fields { syn::Fields::Named(syn::FieldsNamed { named, .. }) => { let push_labels: TokenStream2 = named @@ -31,9 +31,15 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { .collect(); quote! { - let mut labels = vec![]; - #push_labels - labels.into_iter() + impl<'a> prometheus_client::encoding::proto::EncodeLabels for &'a #name { + type Iterator = std::vec::IntoIter; + + fn encode(self) -> Self::Iterator { + let mut labels = vec![]; + #push_labels + labels.into_iter() + } + } } } syn::Fields::Unnamed(_) => { @@ -58,26 +64,21 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { .collect(); quote! { - let label = match self { - #match_arms - }; - - vec![label].into_iter() - } - } - syn::Data::Union(_) => panic!("Can not derive Encode for union."), - }; + impl<'a> prometheus_client::encoding::proto::EncodeLabels for &'a #name { + type Iterator = std::iter::Once; - let gen = quote! { - impl<'a> prometheus_client::encoding::proto::EncodeLabels for &'a #name { - type Iterator = std::vec::IntoIter; + fn encode(self) -> Self::Iterator { + let label = match self { + #match_arms + }; - fn encode(self) -> Self::Iterator { - #body + std::iter::once(label) + } + } } } - }; - gen.into() + syn::Data::Union(_) => panic!("Can not derive Encode for union."), + }.into() } // Copied from https://github.com/djc/askama (MIT and APACHE licensed) and From 289241ca95f2ef677bf869e2b31e350e276565fb Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 19 Aug 2022 14:45:32 +0900 Subject: [PATCH 43/73] encoding/proto: Pass metric and label vec Signed-off-by: ackintosh --- src/encoding/proto.rs | 196 +++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 106 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 7fbd28af..2c345490 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -19,7 +19,7 @@ //! //! let family = metric_set.metric_families.first().unwrap(); //! assert_eq!("my_counter", family.name); -//! assert_eq!("My counter.", family.help); +//! assert_eq!("This is my counter.", family.help); //! ``` // Include the `openmetrics_data_model` module, which is generated from `proto/openmetrics_data_model.proto`. @@ -37,7 +37,6 @@ use crate::metrics::{counter, gauge, MetricType, TypedMetric}; use crate::registry::Registry; use std::collections::HashMap; use std::ops::Deref; -use std::vec::IntoIter; use void::Void; pub use openmetrics_data_model::*; @@ -60,9 +59,9 @@ where family.unit = unit.as_str().to_string(); } family.help = desc.help().to_string(); - family.metrics = metric - .encode(desc.labels().encode().collect::>()) - .collect::>(); + let mut labels = vec![]; + desc.labels().encode(&mut labels); + metric.encode(labels, &mut family.metrics); metric_set.metric_families.push(family); } @@ -84,20 +83,22 @@ impl From for openmetrics_data_model::MetricType { /// Trait implemented by each metric type, e.g. [`Counter`], to implement its encoding. pub trait EncodeMetric { - type Iterator: Iterator; - - fn encode(&self, labels: Vec) -> Self::Iterator; + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ); fn metric_type(&self) -> MetricType; } -impl EncodeMetric - for Box>>> -{ - type Iterator = Box>; - - fn encode(&self, labels: Vec) -> Self::Iterator { - self.deref().encode(labels) +impl EncodeMetric for Box { + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ) { + self.deref().encode(labels, family) } fn metric_type(&self) -> MetricType { @@ -109,22 +110,8 @@ pub trait SendEncodeMetric: EncodeMetric + Send {} impl SendEncodeMetric for T {} -impl EncodeMetric for Box>> { - type Iterator = IntoIter; - - fn encode(&self, labels: Vec) -> Self::Iterator { - self.deref().encode(labels) - } - - fn metric_type(&self) -> MetricType { - self.deref().metric_type() - } -} - pub trait EncodeLabels { - type Iterator: Iterator; - - fn encode(self) -> Self::Iterator; + fn encode(&self, labels: &mut Vec); } impl Into for &(K, V) { @@ -137,45 +124,39 @@ impl Into for &(K, V) { } // TODO: Is this needed? We already have `&'a [T]` below. -impl<'a, T> EncodeLabels for &'a Vec +impl EncodeLabels for Vec where - for<'b> &'b T: Into, + for<'a> &'a T: Into, { - type Iterator = Box + 'a>; - - fn encode(self) -> Self::Iterator { - Box::new(self.iter().map(|t| t.into())) + fn encode(&self, labels: &mut Vec) { + labels.extend(self.iter().map(|t| t.into())) } } - -impl<'a, T> EncodeLabels for &'a [T] +// TODO: Is this needed? We already have `&'a [T]` below. +impl EncodeLabels for [T] where - for<'b> &'b T: Into, + for<'a> &'a T: Into, { - type Iterator = Box + 'a>; - - fn encode(self) -> Self::Iterator { - Box::new(self.iter().map(|t| t.into())) + fn encode(&self, labels: &mut Vec) { + labels.extend(self.iter().map(|t| t.into())) } } -impl<'a> EncodeLabels for &'a Void { - type Iterator = Box>; - - fn encode(self) -> Self::Iterator { - unreachable!() +impl EncodeLabels for Void { + fn encode(&self, _labels: &mut Vec) { + void::unreachable(*self) } } fn encode_exemplar(exemplar: &Exemplar) -> openmetrics_data_model::Exemplar where N: Clone, - for<'a> &'a S: EncodeLabels, + S: EncodeLabels, f64: From, // required because Exemplar.value is defined as `double` in protobuf { let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); exemplar_proto.value = exemplar.value.clone().into(); - exemplar_proto.label = exemplar.label_set.encode().collect::>(); + exemplar.label_set.encode(&mut exemplar_proto.label); exemplar_proto } @@ -204,13 +185,15 @@ where N: EncodeCounterValue, A: counter::Atomic, { - type Iterator = std::iter::Once; - - fn encode(&self, labels: Vec) -> Self::Iterator { + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ) { let mut metric = encode_counter_with_maybe_exemplar(self.get(), None); metric.labels = labels; - std::iter::once(metric) + family.push(metric); } fn metric_type(&self) -> MetricType { @@ -220,14 +203,16 @@ where impl<'a, S, N, A> EncodeMetric for CounterWithExemplar where - for<'b> &'b S: EncodeLabels, + S: EncodeLabels, N: Clone + EncodeCounterValue, A: counter::Atomic, f64: From, { - type Iterator = std::iter::Once; - - fn encode(&self, labels: Vec) -> Self::Iterator { + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ) { let (value, exemplar) = self.get(); let exemplar_proto = if let Some(e) = exemplar.as_ref() { @@ -239,7 +224,7 @@ where let mut metric = encode_counter_with_maybe_exemplar(value.clone(), exemplar_proto); metric.labels = labels; - std::iter::once(metric) + family.push(metric); } fn metric_type(&self) -> MetricType { @@ -305,9 +290,11 @@ where N: EncodeGaugeValue, A: gauge::Atomic, { - type Iterator = std::iter::Once; - - fn encode(&self, labels: Vec) -> Self::Iterator { + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ) { let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { @@ -325,7 +312,8 @@ where }; metric.labels = labels; - std::iter::once(metric) + + family.push(metric) } fn metric_type(&self) -> MetricType { @@ -338,28 +326,20 @@ where impl EncodeMetric for Family where - S: Clone + std::hash::Hash + Eq, - for<'b> &'b S: EncodeLabels, + S: EncodeLabels + Clone + std::hash::Hash + Eq, M: EncodeMetric + TypedMetric, C: MetricConstructor, { - type Iterator = IntoIter; - - fn encode(&self, labels: Vec) -> Self::Iterator { - self.read() - .iter() - .map(|(label_set, metric)| { - let mut labels = labels.clone(); - labels.extend(label_set.encode()); - metric.encode(labels) - }) - .flatten() - // TODO: Ideally we would not have to collect into a vector here, - // though we have to as we borrow from the `MutexGuard`. Once - // https://github.com/prometheus/client_rust/pull/78/ merged, we - // might be able to leverage `MutexGuard::map`. - .collect::>() - .into_iter() + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ) { + for (label_set, metric) in self.read().iter() { + let mut labels = labels.clone(); + label_set.encode(&mut labels); + metric.encode(labels, family) + } } fn metric_type(&self) -> MetricType { @@ -371,14 +351,17 @@ where // Histogram impl EncodeMetric for Histogram { - type Iterator = std::iter::Once; - - fn encode(&self, labels: Vec) -> Self::Iterator { + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ) { let (sum, count, buckets) = self.get(); // TODO: Would be better to use never type instead of `Void`. let mut metric = encode_histogram_with_maybe_exemplars::(sum, count, &buckets, None); metric.labels = labels; - std::iter::once(metric) + + family.push(metric) } fn metric_type(&self) -> MetricType { @@ -388,17 +371,20 @@ impl EncodeMetric for Histogram { impl EncodeMetric for HistogramWithExemplars where - for<'b> &'b S: EncodeLabels, + S: EncodeLabels, { - type Iterator = std::iter::Once; - - fn encode(&self, labels: Vec) -> Self::Iterator { + fn encode( + &self, + labels: Vec, + family: &mut Vec, + ) { let inner = self.inner(); let (sum, count, buckets) = inner.histogram.get(); let mut metric = encode_histogram_with_maybe_exemplars(sum, count, &buckets, Some(&inner.exemplars)); metric.labels = labels; - std::iter::once(metric) + + family.push(metric) } fn metric_type(&self) -> MetricType { @@ -413,7 +399,7 @@ fn encode_histogram_with_maybe_exemplars<'a, S>( exemplars: Option<&'a HashMap>>, ) -> openmetrics_data_model::Metric where - for<'b> &'b S: EncodeLabels, + S: EncodeLabels, { let mut metric = openmetrics_data_model::Metric::default(); @@ -454,17 +440,19 @@ where impl EncodeMetric for Info where - for<'b> &'b S: EncodeLabels, + S: EncodeLabels, { - type Iterator = std::iter::Once; - - fn encode(&self, mut labels: Vec) -> Self::Iterator { + fn encode( + &self, + mut labels: Vec, + family: &mut Vec, + ) { let mut metric = openmetrics_data_model::Metric::default(); metric.metric_points = { let mut metric_point = openmetrics_data_model::MetricPoint::default(); metric_point.value = { - labels.extend(self.0.encode()); + self.0.encode(&mut labels); let mut info_value = openmetrics_data_model::InfoValue::default(); info_value.info = labels; @@ -477,7 +465,7 @@ where vec![metric_point] }; - std::iter::once(metric) + family.push(metric); } fn metric_type(&self) -> MetricType { @@ -803,9 +791,7 @@ mod tests { #[test] fn encode_family_counter_histogram() { - let mut registry = Registry::< - Box>>, - >::default(); + let mut registry = Registry::>::default(); let counter_family = Family::, Counter>::default(); let histogram_family = @@ -835,9 +821,7 @@ mod tests { #[test] fn encode_family_and_counter_and_histogram() { - let mut registry = Registry::< - Box>>>, - >::default(); + let mut registry = Registry::>::default(); // Family let counter_family = Family::, Counter>::default(); @@ -876,8 +860,8 @@ mod tests { histogram.observe(1.0); let metric_set = encode(®istry); - assert_eq!("my_counter", metric_set.metric_families[0].name); - assert_eq!("my_histogram", metric_set.metric_families[1].name); + assert_eq!("my_family_counter", metric_set.metric_families[0].name); + assert_eq!("my_family_histogram", metric_set.metric_families[1].name); } #[test] From dd69b896583153a62686a3328b0520e44e0d741b Mon Sep 17 00:00:00 2001 From: ackintosh Date: Tue, 23 Aug 2022 14:38:00 +0900 Subject: [PATCH 44/73] derive-proto-encode: Update according to the changes in encoding/proto Signed-off-by: ackintosh --- derive-proto-encode/src/lib.rs | 20 ++++++-------------- derive-proto-encode/tests/lib.rs | 4 +++- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/derive-proto-encode/src/lib.rs b/derive-proto-encode/src/lib.rs index e73c3a30..90b33c00 100644 --- a/derive-proto-encode/src/lib.rs +++ b/derive-proto-encode/src/lib.rs @@ -31,13 +31,9 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { .collect(); quote! { - impl<'a> prometheus_client::encoding::proto::EncodeLabels for &'a #name { - type Iterator = std::vec::IntoIter; - - fn encode(self) -> Self::Iterator { - let mut labels = vec![]; + impl prometheus_client::encoding::proto::EncodeLabels for #name { + fn encode(&self, labels: &mut Vec) { #push_labels - labels.into_iter() } } } @@ -57,22 +53,18 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { let mut label = prometheus_client::encoding::proto::Label::default(); label.name = stringify!(#name).to_string(); label.value = stringify!(#ident).to_string(); - label + labels.push(label); } } }) .collect(); quote! { - impl<'a> prometheus_client::encoding::proto::EncodeLabels for &'a #name { - type Iterator = std::iter::Once; - - fn encode(self) -> Self::Iterator { - let label = match self { + impl prometheus_client::encoding::proto::EncodeLabels for #name { + fn encode(&self, labels: &mut Vec) { + match self { #match_arms }; - - std::iter::once(label) } } } diff --git a/derive-proto-encode/tests/lib.rs b/derive-proto-encode/tests/lib.rs index de18b4ab..e8c09445 100644 --- a/derive-proto-encode/tests/lib.rs +++ b/derive-proto-encode/tests/lib.rs @@ -96,7 +96,9 @@ fn remap_keyword_identifiers() { r#type: u64, } - let labels = Labels { r#type: 42 }.encode().collect::>(); + let mut labels = vec![]; + + Labels { r#type: 42 }.encode(&mut labels); assert_eq!("type", labels[0].name); assert_eq!("42", labels[0].value); From 2f8d1a3edc390db9c0176e57a13ea7609de9d0ed Mon Sep 17 00:00:00 2001 From: ackintosh Date: Tue, 23 Aug 2022 14:54:34 +0900 Subject: [PATCH 45/73] Cargo.toml: Make `void` optional void is used in only encoding/proto. Signed-off-by: ackintosh --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eec63f34..2d2e693b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ 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"] +protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build", "dep:prometheus-client-derive-proto-encode", "dep:void"] [workspace] members = ["derive-text-encode", "derive-proto-encode"] @@ -24,7 +24,7 @@ prometheus-client-derive-proto-encode = { version = "0.1.0", path = "derive-prot 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 = "1.0" +void = { version = "1.0", optional = true } [dev-dependencies] async-std = { version = "1", features = ["attributes"] } From 547e1b49f4a0e1700c504353807761b4389a6f85 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Tue, 23 Aug 2022 14:57:41 +0900 Subject: [PATCH 46/73] benches: Update according to the changes in encoding/proto Signed-off-by: ackintosh --- benches/encoding/proto.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/benches/encoding/proto.rs b/benches/encoding/proto.rs index 59356687..068a4d58 100644 --- a/benches/encoding/proto.rs +++ b/benches/encoding/proto.rs @@ -7,7 +7,6 @@ use prometheus_client::metrics::family::Family; use prometheus_client::metrics::histogram::{exponential_buckets, Histogram}; use prometheus_client::registry::Registry; use std::fmt::{Display, Formatter}; -use std::vec::IntoIter; pub fn proto(c: &mut Criterion) { c.bench_function("encode", |b| { @@ -41,9 +40,7 @@ pub fn proto(c: &mut Criterion) { Asia, } - let mut registry = Registry::< - Box>>, - >::default(); + let mut registry = Registry::>::default(); for i in 0..100 { let counter_family = Family::::default(); From f8c621a9d8a8e800419c6636370638e69375e1f5 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Tue, 23 Aug 2022 16:14:27 +0900 Subject: [PATCH 47/73] encoding/proto: same implementation to slice Signed-off-by: ackintosh --- src/encoding/proto.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 2c345490..4f9e19ff 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -123,28 +123,27 @@ impl Into for &(K, V) { } } -// TODO: Is this needed? We already have `&'a [T]` below. impl EncodeLabels for Vec where for<'a> &'a T: Into, { fn encode(&self, labels: &mut Vec) { - labels.extend(self.iter().map(|t| t.into())) + self.as_slice().encode(labels); } } -// TODO: Is this needed? We already have `&'a [T]` below. + impl EncodeLabels for [T] where for<'a> &'a T: Into, { fn encode(&self, labels: &mut Vec) { - labels.extend(self.iter().map(|t| t.into())) + labels.extend(self.iter().map(|t| t.into())); } } impl EncodeLabels for Void { fn encode(&self, _labels: &mut Vec) { - void::unreachable(*self) + void::unreachable(*self); } } From 2d169ad20c00e71a7b299756045fbb443dc5e84b Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 27 Aug 2022 09:38:01 +0900 Subject: [PATCH 48/73] Bump the crate version and add a changelog entry Signed-off-by: ackintosh --- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18229832..e81431da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ 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.18.1] - unreleased + +### Added +- Added support for the OpenMetrics protobuf format. See [PR 47] + +[PR 47]: https://github.com/prometheus/client_rust/pull/47 + ## [0.18.0] ### Changed diff --git a/Cargo.toml b/Cargo.toml index 2d2e693b..73679895 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prometheus-client" -version = "0.18.0" +version = "0.18.1" authors = ["Max Inden "] edition = "2021" description = "Open Metrics client library allowing users to natively instrument applications." From 4b7c75f39b2b27b0be2273bc8e5ca4e840aa9b41 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 27 Aug 2022 09:51:56 +0900 Subject: [PATCH 49/73] Add --all-features Signed-off-by: ackintosh --- .github/workflows/rust.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7d00d45d..d9fdb9b1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -39,7 +39,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - args: --benches + args: --benches --all-features test: name: Test Suite @@ -73,7 +73,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - args: --all + args: --all --all-features fmt: name: Rustfmt @@ -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 @@ -159,4 +159,4 @@ jobs: with: use-cross: true command: build - args: --release --target=${{ matrix.target }} + args: --release --target=${{ matrix.target }} --all-features From 48745c4b77d582166ddac7a05acc27ff03ff3b85 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Tue, 30 Aug 2022 09:24:10 +0900 Subject: [PATCH 50/73] Remove unnecessary generic parameter Signed-off-by: ackintosh --- src/encoding/proto.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 4f9e19ff..5fd14e4a 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -200,7 +200,7 @@ where } } -impl<'a, S, N, A> EncodeMetric for CounterWithExemplar +impl EncodeMetric for CounterWithExemplar where S: EncodeLabels, N: Clone + EncodeCounterValue, @@ -284,7 +284,7 @@ impl EncodeGaugeValue for f64 { } } -impl<'a, N, A> EncodeMetric for Gauge +impl EncodeMetric for Gauge where N: EncodeGaugeValue, A: gauge::Atomic, From f367ebfe43a862ef37e573231ac2c9415aed949d Mon Sep 17 00:00:00 2001 From: ackintosh Date: Mon, 5 Sep 2022 12:08:38 +0900 Subject: [PATCH 51/73] Merge derive-proto-encode into derive-text-encode Signed-off-by: ackintosh --- Cargo.toml | 5 +- derive-proto-encode/Cargo.toml | 23 ------ derive-proto-encode/src/lib.rs | 127 ------------------------------- derive-proto-encode/tests/lib.rs | 105 ------------------------- derive-text-encode/Cargo.toml | 5 +- derive-text-encode/src/lib.rs | 83 +++++++++++++++++++- derive-text-encode/tests/lib.rs | 95 ++++++++++++++++++++--- src/encoding/proto.rs | 2 +- 8 files changed, 171 insertions(+), 274 deletions(-) delete mode 100644 derive-proto-encode/Cargo.toml delete mode 100644 derive-proto-encode/src/lib.rs delete mode 100644 derive-proto-encode/tests/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 73679895..fd83f068 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,16 +11,15 @@ 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"] +protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build", "dep:void", "prometheus-client-derive-text-encode/protobuf"] [workspace] -members = ["derive-text-encode", "derive-proto-encode"] +members = ["derive-text-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 } 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 } diff --git a/derive-proto-encode/Cargo.toml b/derive-proto-encode/Cargo.toml deleted file mode 100644 index 2baf445c..00000000 --- a/derive-proto-encode/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "prometheus-client-derive-proto-encode" -version = "0.1.0" -authors = ["Akihito Nakano "] -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 diff --git a/derive-proto-encode/src/lib.rs b/derive-proto-encode/src/lib.rs deleted file mode 100644 index 90b33c00..00000000 --- a/derive-proto-encode/src/lib.rs +++ /dev/null @@ -1,127 +0,0 @@ -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) { - #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) { - 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"), -]; diff --git a/derive-proto-encode/tests/lib.rs b/derive-proto-encode/tests/lib.rs deleted file mode 100644 index e8c09445..00000000 --- a/derive-proto-encode/tests/lib.rs +++ /dev/null @@ -1,105 +0,0 @@ -use prometheus_client::encoding::proto::EncodeLabels; -use prometheus_client::encoding::proto::{encode, Encode}; -use prometheus_client::metrics::counter::Counter; -use prometheus_client::metrics::family::Family; -use prometheus_client::registry::Registry; -use std::fmt::{Display, Formatter}; - -#[test] -fn structs() { - #[derive(Clone, Hash, PartialEq, Eq, Encode)] - struct Labels { - method: Method, - path: String, - } - - #[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 => f.write_str("Get"), - Method::Put => f.write_str("Put"), - } - } - } - - let mut registry = Registry::default(); - let family = Family::::default(); - registry.register("my_counter", "This is my counter", family.clone()); - - // Record a single HTTP GET request. - family - .get_or_create(&Labels { - method: Method::Get, - path: "/metrics".to_string(), - }) - .inc(); - - // Encode all metrics in the registry in the OpenMetrics protobuf format. - let mut metric_set = encode(®istry); - let mut family: prometheus_client::encoding::proto::MetricFamily = - metric_set.metric_families.pop().unwrap(); - let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap(); - - let method = &metric.labels[0]; - assert_eq!("method", method.name); - assert_eq!("Get", method.value); - - let path = &metric.labels[1]; - assert_eq!("path", path.name); - assert_eq!("/metrics", path.value); -} - -#[test] -fn enums() { - #[derive(Clone, Hash, PartialEq, Eq, Encode)] - enum Method { - Get, - #[allow(dead_code)] - Put, - } - - let mut registry = Registry::default(); - let family = Family::::default(); - registry.register("my_counter", "This is my counter", family.clone()); - - // Record a single HTTP GET request. - family.get_or_create(&Method::Get).inc(); - - // Encode all metrics in the registry in the OpenMetrics protobuf format. - let mut metric_set = encode(®istry); - let mut family: prometheus_client::encoding::proto::MetricFamily = - metric_set.metric_families.pop().unwrap(); - let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap(); - - let label = &metric.labels[0]; - assert_eq!("Method", label.name); - assert_eq!("Get", label.value); -} - -#[test] -fn remap_keyword_identifiers() { - #[derive(Encode, Hash, Clone, Eq, PartialEq)] - struct Labels { - // `r#type` is problematic as `r#` is not a valid OpenMetrics label name - // but one needs to use keyword identifier syntax (aka. raw identifiers) - // as `type` is a keyword. - // - // Test makes sure `r#type` is replaced by `type` in the OpenMetrics - // output. - r#type: u64, - } - - let mut labels = vec![]; - - Labels { r#type: 42 }.encode(&mut labels); - - assert_eq!("type", labels[0].name); - assert_eq!("42", labels[0].value); -} diff --git a/derive-text-encode/Cargo.toml b/derive-text-encode/Cargo.toml index f243c24e..f9108e90 100644 --- a/derive-text-encode/Cargo.toml +++ b/derive-text-encode/Cargo.toml @@ -9,6 +9,9 @@ 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] @@ -17,7 +20,7 @@ quote = "1" syn = "1" [dev-dependencies] -prometheus-client = { path = "../" } +prometheus-client = { path = "../", features = ["protobuf"] } [lib] proc-macro = true \ No newline at end of file diff --git a/derive-text-encode/src/lib.rs b/derive-text-encode/src/lib.rs index 186aae43..3de858d4 100644 --- a/derive-text-encode/src/lib.rs +++ b/derive-text-encode/src/lib.rs @@ -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() @@ -61,7 +61,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { syn::Data::Union(_) => panic!("Can not derive Encode for union."), }; - let gen = quote! { + let mut gen = quote! { impl prometheus_client::encoding::text::Encode for #name { fn encode(&self, writer: &mut dyn std::io::Write) -> std::result::Result<(), std::io::Error> { #body @@ -70,9 +70,88 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { } } }; + + if cfg!(feature = "protobuf") { + let protobuf = derive_protobuf_encode(ast); + gen = 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 = 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) { + #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) { + 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] = [ diff --git a/derive-text-encode/tests/lib.rs b/derive-text-encode/tests/lib.rs index 2e4cbbfc..230e9c8e 100644 --- a/derive-text-encode/tests/lib.rs +++ b/derive-text-encode/tests/lib.rs @@ -2,22 +2,35 @@ use prometheus_client::encoding::text::{encode, Encode}; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; +#[cfg(feature = "protobuf")] +use std::fmt::{Display, Formatter}; + +#[derive(Clone, Hash, PartialEq, Eq, Encode)] +struct Labels { + method: Method, + path: String, +} + +#[derive(Clone, Hash, PartialEq, Eq, Encode)] +enum Method { + Get, + #[allow(dead_code)] + Put, +} + +#[cfg(feature = "protobuf")] +impl Display for Method { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Method::Get => f.write_str("Get"), + Method::Put => f.write_str("Put"), + } + } +} #[test] fn basic_flow() { let mut registry = Registry::default(); - #[derive(Clone, Hash, PartialEq, Eq, Encode)] - struct Labels { - method: Method, - path: String, - } - - #[derive(Clone, Hash, PartialEq, Eq, Encode)] - enum Method { - Get, - #[allow(dead_code)] - Put, - } let family = Family::::default(); registry.register("my_counter", "This is my counter", family.clone()); @@ -41,6 +54,64 @@ fn basic_flow() { assert_eq!(expected, String::from_utf8(buffer).unwrap()); } +#[cfg(feature = "protobuf")] +mod protobuf { + use crate::{Labels, Method}; + use prometheus_client::encoding::proto::encode; + use prometheus_client::metrics::counter::Counter; + use prometheus_client::metrics::family::Family; + use prometheus_client::registry::Registry; + + #[test] + fn structs() { + let mut registry = Registry::default(); + let family = Family::::default(); + registry.register("my_counter", "This is my counter", family.clone()); + + // Record a single HTTP GET request. + family + .get_or_create(&Labels { + method: Method::Get, + path: "/metrics".to_string(), + }) + .inc(); + + // Encode all metrics in the registry in the OpenMetrics protobuf format. + let mut metric_set = encode(®istry); + let mut family: prometheus_client::encoding::proto::MetricFamily = + metric_set.metric_families.pop().unwrap(); + let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap(); + + let method = &metric.labels[0]; + assert_eq!("method", method.name); + assert_eq!("Get", method.value); + + let path = &metric.labels[1]; + assert_eq!("path", path.name); + assert_eq!("/metrics", path.value); + } + + #[test] + fn enums() { + let mut registry = Registry::default(); + let family = Family::::default(); + registry.register("my_counter", "This is my counter", family.clone()); + + // Record a single HTTP GET request. + family.get_or_create(&Method::Get).inc(); + + // Encode all metrics in the registry in the OpenMetrics protobuf format. + let mut metric_set = encode(®istry); + let mut family: prometheus_client::encoding::proto::MetricFamily = + metric_set.metric_families.pop().unwrap(); + let metric: prometheus_client::encoding::proto::Metric = family.metrics.pop().unwrap(); + + let label = &metric.labels[0]; + assert_eq!("Method", label.name); + assert_eq!("Get", label.value); + } +} + #[test] fn remap_keyword_identifiers() { #[derive(Encode, Hash, Clone, Eq, PartialEq)] diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 5fd14e4a..b1b89fa9 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -40,7 +40,7 @@ use std::ops::Deref; use void::Void; pub use openmetrics_data_model::*; -pub use prometheus_client_derive_proto_encode::*; +pub use prometheus_client_derive_text_encode::*; pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet where From 870cdaeeae7d543105abb930fb8a3fc42a49017d Mon Sep 17 00:00:00 2001 From: ackintosh Date: Mon, 5 Sep 2022 17:03:35 +0900 Subject: [PATCH 52/73] `protobuf` feature requires Display Signed-off-by: ackintosh --- examples/actix-web.rs | 10 ++++++++++ examples/tide.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/examples/actix-web.rs b/examples/actix-web.rs index 20fae762..98f364c6 100644 --- a/examples/actix-web.rs +++ b/examples/actix-web.rs @@ -12,6 +12,16 @@ pub enum Method { Post, } +#[cfg(feature = "protobuf")] +impl std::fmt::Display for Method { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Method::Get => f.write_str("Get"), + Method::Post => f.write_str("Post"), + } + } +} + #[derive(Clone, Hash, PartialEq, Eq, Encode)] pub struct MethodLabels { pub method: Method, diff --git a/examples/tide.rs b/examples/tide.rs index dfb68125..5f35606b 100644 --- a/examples/tide.rs +++ b/examples/tide.rs @@ -55,6 +55,16 @@ enum Method { Put, } +#[cfg(feature = "protobuf")] +impl std::fmt::Display for Method { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Method::Get => f.write_str("Get"), + Method::Put => f.write_str("Put"), + } + } +} + #[derive(Clone)] struct State { registry: Arc>>, From 26ec648b814dc3cc7eff56f51d6b26b78a9983a4 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Mon, 5 Sep 2022 17:11:04 +0900 Subject: [PATCH 53/73] Rename `prometheus-client-derive-text-encode` to `prometheus-client-derive-encode` Signed-off-by: ackintosh --- Cargo.toml | 6 +++--- {derive-text-encode => derive-encode}/Cargo.toml | 4 ++-- {derive-text-encode => derive-encode}/src/lib.rs | 0 {derive-text-encode => derive-encode}/tests/lib.rs | 0 src/encoding/proto.rs | 2 +- src/encoding/text.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename {derive-text-encode => derive-encode}/Cargo.toml (81%) rename {derive-text-encode => derive-encode}/src/lib.rs (100%) rename {derive-text-encode => derive-encode}/tests/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index fd83f068..af861a7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,16 +11,16 @@ 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-text-encode/protobuf"] +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 } diff --git a/derive-text-encode/Cargo.toml b/derive-encode/Cargo.toml similarity index 81% rename from derive-text-encode/Cargo.toml rename to derive-encode/Cargo.toml index f9108e90..7dc98bf1 100644 --- a/derive-text-encode/Cargo.toml +++ b/derive-encode/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "prometheus-client-derive-text-encode" +name = "prometheus-client-derive-encode" version = "0.3.0" authors = ["Max Inden "] 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" diff --git a/derive-text-encode/src/lib.rs b/derive-encode/src/lib.rs similarity index 100% rename from derive-text-encode/src/lib.rs rename to derive-encode/src/lib.rs diff --git a/derive-text-encode/tests/lib.rs b/derive-encode/tests/lib.rs similarity index 100% rename from derive-text-encode/tests/lib.rs rename to derive-encode/tests/lib.rs diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index b1b89fa9..b84a5f1b 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -40,7 +40,7 @@ use std::ops::Deref; use void::Void; pub use openmetrics_data_model::*; -pub use prometheus_client_derive_text_encode::*; +pub use prometheus_client_derive_encode::*; pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet where diff --git a/src/encoding/text.rs b/src/encoding/text.rs index c3e774a2..5ca036ab 100644 --- a/src/encoding/text.rs +++ b/src/encoding/text.rs @@ -38,7 +38,7 @@ use std::collections::HashMap; use std::io::Write; use std::ops::Deref; -pub use prometheus_client_derive_text_encode::*; +pub use prometheus_client_derive_encode::*; pub fn encode(writer: &mut W, registry: &Registry) -> Result<(), std::io::Error> where From 2fb5f1f26be551dc7a8f406085af9d8a0daf4dde Mon Sep 17 00:00:00 2001 From: ackintosh Date: Mon, 5 Sep 2022 17:14:18 +0900 Subject: [PATCH 54/73] derive-encode: Bump up patch version because of adding protobuf feature Signed-off-by: ackintosh --- derive-encode/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive-encode/Cargo.toml b/derive-encode/Cargo.toml index 7dc98bf1..9762fd3c 100644 --- a/derive-encode/Cargo.toml +++ b/derive-encode/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prometheus-client-derive-encode" -version = "0.3.0" +version = "0.3.1" authors = ["Max Inden "] edition = "2021" description = "Auxiliary crate to derive Encode trait from prometheus-client." From 15dc7a725a2da889cfa17ac94b2c36093aaa7c70 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 09:52:19 +0900 Subject: [PATCH 55/73] Fix missing docs Signed-off-by: ackintosh --- src/encoding/proto.rs | 21 ++++++++++++++++++++- src/registry.rs | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index b84a5f1b..0e9d9b84 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -22,7 +22,10 @@ //! assert_eq!("This is my counter.", family.help); //! ``` -// Include the `openmetrics_data_model` module, which is generated from `proto/openmetrics_data_model.proto`. +// Allowing `missing docs` here as the `openmetrics.rs` is an automatically generated file. +#[allow(missing_docs)] +/// Data models that are automatically generated from OpenMetrics protobuf +/// format. pub mod openmetrics_data_model { include!(concat!(env!("OUT_DIR"), "/openmetrics.rs")); } @@ -42,6 +45,8 @@ use void::Void; pub use openmetrics_data_model::*; pub use prometheus_client_derive_encode::*; +/// Encode the metrics registered with the provided [`Registry`] into MetricSet +/// using the OpenMetrics protobuf format. pub fn encode(registry: &Registry) -> openmetrics_data_model::MetricSet where M: EncodeMetric, @@ -83,12 +88,14 @@ impl From for openmetrics_data_model::MetricType { /// Trait implemented by each metric type, e.g. [`Counter`], to implement its encoding. pub trait EncodeMetric { + /// Encode to OpenMetrics protobuf encoding. fn encode( &self, labels: Vec, family: &mut Vec, ); + /// The OpenMetrics metric type of the instance. fn metric_type(&self) -> MetricType; } @@ -106,11 +113,15 @@ impl EncodeMetric for Box { } } +/// Trait combining [`EncodeMetric`] and [`Send`]. pub trait SendEncodeMetric: EncodeMetric + Send {} impl 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 + /// encoding. fn encode(&self, labels: &mut Vec); } @@ -163,7 +174,11 @@ where ///////////////////////////////////////////////////////////////////////////////// // Counter +/// Trait to implement its counter value encoding in the OpenMetrics protobuf +/// format. pub trait EncodeCounterValue { + /// Encode the given instance into counter value in the OpenMetrics protobuf + /// encoding. fn encode(&self) -> openmetrics_data_model::counter_value::Total; } @@ -261,7 +276,11 @@ where ///////////////////////////////////////////////////////////////////////////////// // Gauge +/// Trait to implement its gauge value encoding in the OpenMetrics protobuf +/// format. pub trait EncodeGaugeValue { + /// Encode the given instance into gauge value in the OpenMetrics protobuf + /// encoding. fn encode(&self) -> openmetrics_data_model::gauge_value::Value; } diff --git a/src/registry.rs b/src/registry.rs index eac01806..b88dc46b 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -348,6 +348,7 @@ pub enum Unit { } impl Unit { + /// Returns the given Unit's str representation. pub fn as_str(&self) -> &str { match self { Unit::Amperes => "amperes", From bb0812279dc302065327a90f24dfc53d3b6a91b7 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 10:23:31 +0900 Subject: [PATCH 56/73] Fix clippy warnings: field assignment outside of initializer for an instance created with Default::default() https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default Signed-off-by: ackintosh --- src/encoding/proto.rs | 198 ++++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 93 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 0e9d9b84..a9dba26f 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -54,16 +54,21 @@ where let mut metric_set = openmetrics_data_model::MetricSet::default(); for (desc, metric) in registry.iter() { - let mut family = openmetrics_data_model::MetricFamily::default(); - family.name = desc.name().to_string(); - family.r#type = { - let metric_type: openmetrics_data_model::MetricType = metric.metric_type().into(); - metric_type as i32 + 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(); + metric_type as i32 + }, + unit: if let Some(unit) = desc.unit() { + unit.as_str().to_string() + } else { + String::new() + }, + help: desc.help().to_string(), + ..Default::default() }; - if let Some(unit) = desc.unit() { - family.unit = unit.as_str().to_string(); - } - family.help = desc.help().to_string(); + let mut labels = vec![]; desc.labels().encode(&mut labels); metric.encode(labels, &mut family.metrics); @@ -127,10 +132,10 @@ pub trait EncodeLabels { impl Into for &(K, V) { fn into(self) -> openmetrics_data_model::Label { - let mut label = openmetrics_data_model::Label::default(); - label.name = self.0.to_string(); - label.value = self.1.to_string(); - label + openmetrics_data_model::Label { + name: self.0.to_string(), + value: self.1.to_string(), + } } } @@ -164,8 +169,10 @@ where S: EncodeLabels, f64: From, // required because Exemplar.value is defined as `double` in protobuf { - let mut exemplar_proto = openmetrics_data_model::Exemplar::default(); - exemplar_proto.value = exemplar.value.clone().into(); + let mut exemplar_proto = openmetrics_data_model::Exemplar { + value: exemplar.value.clone().into(), + ..Default::default() + }; exemplar.label_set.encode(&mut exemplar_proto.label); exemplar_proto @@ -253,24 +260,25 @@ fn encode_counter_with_maybe_exemplar( where N: EncodeCounterValue, { - let mut metric = openmetrics_data_model::Metric::default(); - - metric.metric_points = { - let mut metric_point = openmetrics_data_model::MetricPoint::default(); - metric_point.value = { - let mut counter_value = openmetrics_data_model::CounterValue::default(); - counter_value.total = Some(value.encode()); - counter_value.exemplar = exemplar; - - Some(openmetrics_data_model::metric_point::Value::CounterValue( - counter_value, - )) - }; - - vec![metric_point] - }; + openmetrics_data_model::Metric { + metric_points: { + let metric_point = openmetrics_data_model::MetricPoint { + value: { + Some(openmetrics_data_model::metric_point::Value::CounterValue( + openmetrics_data_model::CounterValue { + total: Some(value.encode()), + exemplar, + ..Default::default() + }, + )) + }, + ..Default::default() + }; - metric + vec![metric_point] + }, + ..Default::default() + } } ///////////////////////////////////////////////////////////////////////////////// @@ -313,24 +321,24 @@ where labels: Vec, family: &mut Vec, ) { - let mut metric = openmetrics_data_model::Metric::default(); - - metric.metric_points = { - let mut metric_point = openmetrics_data_model::MetricPoint::default(); - metric_point.value = { - let mut gauge_value = openmetrics_data_model::GaugeValue::default(); - gauge_value.value = Some(self.get().encode()); - - Some(openmetrics_data_model::metric_point::Value::GaugeValue( - gauge_value, - )) - }; + let metric = openmetrics_data_model::Metric { + metric_points: { + let metric_point = openmetrics_data_model::MetricPoint { + value: { + Some(openmetrics_data_model::metric_point::Value::GaugeValue( + openmetrics_data_model::GaugeValue { + value: Some(self.get().encode()), + }, + )) + }, + ..Default::default() + }; - vec![metric_point] + vec![metric_point] + }, + labels, }; - metric.labels = labels; - family.push(metric) } @@ -419,38 +427,42 @@ fn encode_histogram_with_maybe_exemplars<'a, S>( where S: EncodeLabels, { - let mut metric = openmetrics_data_model::Metric::default(); - - metric.metric_points = { - let mut metric_point = openmetrics_data_model::MetricPoint::default(); - metric_point.value = { - let mut histogram_value = openmetrics_data_model::HistogramValue::default(); - histogram_value.sum = Some(openmetrics_data_model::histogram_value::Sum::DoubleValue( - sum, - )); - histogram_value.count = count; - - let mut cummulative = 0; - for (i, (upper_bound, count)) in buckets.iter().enumerate() { - cummulative += count; - let mut bucket = openmetrics_data_model::histogram_value::Bucket::default(); - bucket.count = cummulative; - bucket.upper_bound = *upper_bound; - bucket.exemplar = exemplars - .map(|es| es.get(&i)) - .flatten() - .map(|exemplar| encode_exemplar(exemplar)); - histogram_value.buckets.push(bucket); - } - Some(openmetrics_data_model::metric_point::Value::HistogramValue( - histogram_value, - )) - }; - - vec![metric_point] - }; + openmetrics_data_model::Metric { + metric_points: { + let metric_point = openmetrics_data_model::MetricPoint { + value: { + let mut histogram_value = openmetrics_data_model::HistogramValue { + sum: Some(openmetrics_data_model::histogram_value::Sum::DoubleValue( + sum, + )), + count, + ..Default::default() + }; + + let mut cummulative = 0; + for (i, (upper_bound, count)) in buckets.iter().enumerate() { + cummulative += count; + let bucket = openmetrics_data_model::histogram_value::Bucket { + count: cummulative, + upper_bound: *upper_bound, + exemplar: exemplars + .map(|es| es.get(&i)) + .flatten() + .map(|exemplar| encode_exemplar(exemplar)), + }; + histogram_value.buckets.push(bucket); + } + Some(openmetrics_data_model::metric_point::Value::HistogramValue( + histogram_value, + )) + }, + ..Default::default() + }; - metric + vec![metric_point] + }, + ..Default::default() + } } ///////////////////////////////////////////////////////////////////////////////// @@ -465,22 +477,22 @@ where mut labels: Vec, family: &mut Vec, ) { - let mut metric = openmetrics_data_model::Metric::default(); - - metric.metric_points = { - let mut metric_point = openmetrics_data_model::MetricPoint::default(); - metric_point.value = { - self.0.encode(&mut labels); - - let mut info_value = openmetrics_data_model::InfoValue::default(); - info_value.info = labels; - - Some(openmetrics_data_model::metric_point::Value::InfoValue( - info_value, - )) - }; + let metric = openmetrics_data_model::Metric { + metric_points: { + let metric_point = openmetrics_data_model::MetricPoint { + value: { + self.0.encode(&mut labels); + + Some(openmetrics_data_model::metric_point::Value::InfoValue( + openmetrics_data_model::InfoValue { info: labels }, + )) + }, + ..Default::default() + }; - vec![metric_point] + vec![metric_point] + }, + ..Default::default() }; family.push(metric); From 766c650221a860624329078dd1fab1faae438db9 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 10:27:43 +0900 Subject: [PATCH 57/73] Fix clippy warnings: an implementation of `From` is preferred https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into Signed-off-by: ackintosh --- src/encoding/proto.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index a9dba26f..428d9ba1 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -130,11 +130,11 @@ pub trait EncodeLabels { fn encode(&self, labels: &mut Vec); } -impl Into for &(K, V) { - fn into(self) -> openmetrics_data_model::Label { +impl From<&(K, V)> for openmetrics_data_model::Label { + fn from(kv: &(K, V)) -> Self { openmetrics_data_model::Label { - name: self.0.to_string(), - value: self.1.to_string(), + name: kv.0.to_string(), + value: kv.1.to_string(), } } } From e99575bb1dc2eba41bd1a180eb7bdb5aea6073ce Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 10:28:42 +0900 Subject: [PATCH 58/73] Fix redundant clone Signed-off-by: ackintosh --- src/encoding/proto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 428d9ba1..e5ba56f8 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -242,7 +242,7 @@ where None }; - let mut metric = encode_counter_with_maybe_exemplar(value.clone(), exemplar_proto); + let mut metric = encode_counter_with_maybe_exemplar(value, exemplar_proto); metric.labels = labels; family.push(metric); From a6c797b7dab89fc950d88c5e627053ce8603fa8e Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 10:30:55 +0900 Subject: [PATCH 59/73] Fix clippy warnings: manual implementation of `Option::map` https://rust-lang.github.io/rust-clippy/master/index.html#manual_map Signed-off-by: ackintosh --- src/encoding/proto.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index e5ba56f8..77b46dea 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -235,13 +235,7 @@ where family: &mut Vec, ) { let (value, exemplar) = self.get(); - - let exemplar_proto = if let Some(e) = exemplar.as_ref() { - Some(encode_exemplar(e)) - } else { - None - }; - + let exemplar_proto = exemplar.as_ref().map(|e| encode_exemplar(e)); let mut metric = encode_counter_with_maybe_exemplar(value, exemplar_proto); metric.labels = labels; From cc9f7393da0912ac8bed152650fd9c92760e511a Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 10:34:10 +0900 Subject: [PATCH 60/73] Fix clippy warnings: explicit lifetimes given in parameter types where they could be elided https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes Signed-off-by: ackintosh --- src/encoding/proto.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 77b46dea..98c66ce1 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -412,11 +412,11 @@ where } } -fn encode_histogram_with_maybe_exemplars<'a, S>( +fn encode_histogram_with_maybe_exemplars( sum: f64, count: u64, buckets: &[(f64, u64)], - exemplars: Option<&'a HashMap>>, + exemplars: Option<&HashMap>>, ) -> openmetrics_data_model::Metric where S: EncodeLabels, From d106aa342991914b158a6ffd14836297efa6f72c Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 10:40:16 +0900 Subject: [PATCH 61/73] Fix clippy warnings: called `map(..).flatten()` on `Option` https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten Signed-off-by: ackintosh --- src/encoding/proto.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 98c66ce1..bb6b4b69 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -440,8 +440,7 @@ where count: cummulative, upper_bound: *upper_bound, exemplar: exemplars - .map(|es| es.get(&i)) - .flatten() + .and_then(|es| es.get(&i)) .map(|exemplar| encode_exemplar(exemplar)), }; histogram_value.buckets.push(bucket); From 77c9c22d6bdc1ffa7bbda96daf23c759f68b075a Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 10:42:46 +0900 Subject: [PATCH 62/73] Allow some lint warnings on openmetrics.rs which is an auto-generated file Signed-off-by: ackintosh --- src/encoding/proto.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index bb6b4b69..05b7965b 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -22,8 +22,8 @@ //! assert_eq!("This is my counter.", family.help); //! ``` -// Allowing `missing docs` here as the `openmetrics.rs` is an automatically generated file. -#[allow(missing_docs)] +// Allowing some lints here as the `openmetrics.rs` is an automatically generated file. +#[allow(missing_docs, clippy::derive_partial_eq_without_eq)] /// Data models that are automatically generated from OpenMetrics protobuf /// format. pub mod openmetrics_data_model { From ec562247a13dec6cf5cd9859521abe01bc1b4b47 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 11:11:03 +0900 Subject: [PATCH 63/73] Remove unnecessary cfg `not found in this scope` error occurs when no feature specified Signed-off-by: ackintosh --- derive-encode/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/derive-encode/src/lib.rs b/derive-encode/src/lib.rs index 3de858d4..7820bba9 100644 --- a/derive-encode/src/lib.rs +++ b/derive-encode/src/lib.rs @@ -83,7 +83,6 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { gen.into() } -#[cfg(feature = "protobuf")] fn derive_protobuf_encode(ast: DeriveInput) -> TokenStream2 { let name = &ast.ident; From d992badbe2aaecd29a3de82110d3fb9fade3d24b Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 11:16:21 +0900 Subject: [PATCH 64/73] Move`Encode` trait from `prometheus_client::encoding::text` to `prometheus_client::encoding` Signed-off-by: ackintosh --- CHANGELOG.md | 6 +++++- examples/actix-web.rs | 3 ++- examples/tide.rs | 3 ++- src/encoding.rs | 2 ++ src/encoding/text.rs | 2 -- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e81431da..1a17e594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.18.1] - unreleased ### Added -- Added support for the OpenMetrics protobuf format. See [PR 47] +- Added support for the OpenMetrics protobuf format. See [PR 47]. + +### Changed + +- Move`Encode` trait from `prometheus_client::encoding::text` to `prometheus_client::encoding`. See [PR 47]. [PR 47]: https://github.com/prometheus/client_rust/pull/47 diff --git a/examples/actix-web.rs b/examples/actix-web.rs index 98f364c6..0951e76f 100644 --- a/examples/actix-web.rs +++ b/examples/actix-web.rs @@ -1,7 +1,8 @@ use std::sync::Mutex; use actix_web::{web, App, HttpResponse, HttpServer, Responder, Result}; -use prometheus_client::encoding::text::{encode, Encode}; +use prometheus_client::encoding::text::encode; +use prometheus_client::encoding::Encode; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; diff --git a/examples/tide.rs b/examples/tide.rs index 5f35606b..b71de8d6 100644 --- a/examples/tide.rs +++ b/examples/tide.rs @@ -1,4 +1,5 @@ -use prometheus_client::encoding::text::{encode, Encode}; +use prometheus_client::encoding::text::encode; +use prometheus_client::encoding::Encode; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; diff --git a/src/encoding.rs b/src/encoding.rs index d62cb3fc..8d9a0c68 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,5 +1,7 @@ //! Exposition format implementations. +pub use prometheus_client_derive_encode::*; + #[cfg(feature = "protobuf")] pub mod proto; pub mod text; diff --git a/src/encoding/text.rs b/src/encoding/text.rs index e2c851b5..754b08a7 100644 --- a/src/encoding/text.rs +++ b/src/encoding/text.rs @@ -38,8 +38,6 @@ use std::collections::HashMap; use std::io::Write; use std::ops::Deref; -pub use prometheus_client_derive_encode::*; - /// Encode the metrics registered with the provided [`Registry`] into the /// provided [`Write`]r using the OpenMetrics text format. pub fn encode(writer: &mut W, registry: &Registry) -> Result<(), std::io::Error> From 8fce75651c11705fbbfd31a93f18ea75d4d71c06 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 11:22:34 +0900 Subject: [PATCH 65/73] Bump up the minor version as the unreleased contains breaking changes Signed-off-by: ackintosh --- CHANGELOG.md | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a17e594..109995a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ 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.18.1] - unreleased +## [0.19.0] - unreleased ### Added - Added support for the OpenMetrics protobuf format. See [PR 47]. diff --git a/Cargo.toml b/Cargo.toml index af861a7e..d9b4edd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prometheus-client" -version = "0.18.1" +version = "0.19.0" authors = ["Max Inden "] edition = "2021" description = "Open Metrics client library allowing users to natively instrument applications." From 639cb0a6d4de5d23dc0d49388e475812cf7c2973 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Wed, 7 Sep 2022 11:23:37 +0900 Subject: [PATCH 66/73] Update a link to PR Signed-off-by: ackintosh --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 109995a3..901b2c91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.19.0] - unreleased ### Added -- Added support for the OpenMetrics protobuf format. See [PR 47]. +- 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 47]. +- Move`Encode` trait from `prometheus_client::encoding::text` to `prometheus_client::encoding`. See [PR 83]. -[PR 47]: https://github.com/prometheus/client_rust/pull/47 +[PR 83]: https://github.com/prometheus/client_rust/pull/83 ## [0.18.0] From ca0f583e649ef3d4c379184f21e600fc59a51ccb Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 8 Sep 2022 16:47:26 -0500 Subject: [PATCH 67/73] misc fixes Signed-off-by: ackintosh --- Cargo.toml | 2 ++ benches/encoding/proto.rs | 8 +++++--- benches/encoding/text.rs | 21 +++++++++++++++++++++ derive-encode/src/lib.rs | 10 ++++++---- derive-encode/tests/lib.rs | 1 + 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d9b4edd2..eef8be6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,8 +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"] diff --git a/benches/encoding/proto.rs b/benches/encoding/proto.rs index 068a4d58..4de33a1a 100644 --- a/benches/encoding/proto.rs +++ b/benches/encoding/proto.rs @@ -1,7 +1,9 @@ -// Benchmark inspired by https://github.com/tikv/rust-prometheus/blob/ab1ca7285d3463504381a5025ae1951e020d6796/benches/text_encoder.rs +// 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, Encode, EncodeMetric}; +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}; @@ -17,7 +19,7 @@ pub fn proto(c: &mut Criterion) { some_number: u64, } - #[derive(Clone, Hash, PartialEq, Eq)] + #[derive(Clone, Hash, PartialEq, Eq, Encode)] enum Method { Get, #[allow(dead_code)] diff --git a/benches/encoding/text.rs b/benches/encoding/text.rs index 78c2c4f3..fc02b5c8 100644 --- a/benches/encoding/text.rs +++ b/benches/encoding/text.rs @@ -2,6 +2,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use prometheus_client::encoding::text::{encode, 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}; @@ -33,6 +34,26 @@ pub fn text(c: &mut Criterion) { Five, } + #[cfg(feature = "protobuf")] + impl std::fmt::Display for Method { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Method::Get => f.write_str("Get"), + Method::Put => f.write_str("Put"), + } + } + } + + #[cfg(feature = "protobuf")] + impl std::fmt::Display for Status { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Status::Two => f.write_str("2"), + Status::Four => f.write_str("4"), + Status::Five => f.write_str("5"), + } + } + } impl Encode for Status { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { let status = match self { diff --git a/derive-encode/src/lib.rs b/derive-encode/src/lib.rs index 7820bba9..e03926b9 100644 --- a/derive-encode/src/lib.rs +++ b/derive-encode/src/lib.rs @@ -61,7 +61,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { syn::Data::Union(_) => panic!("Can not derive Encode for union."), }; - let mut gen = quote! { + let gen = quote! { impl prometheus_client::encoding::text::Encode for #name { fn encode(&self, writer: &mut dyn std::io::Write) -> std::result::Result<(), std::io::Error> { #body @@ -71,18 +71,20 @@ pub fn derive_encode(input: TokenStream) -> TokenStream { } }; - if cfg!(feature = "protobuf") { + #[cfg(feature = "protobuf")] + let gen = { let protobuf = derive_protobuf_encode(ast); - gen = quote! { + quote! { #gen #protobuf } - } + }; gen.into() } +#[cfg(feature = "protobuf")] fn derive_protobuf_encode(ast: DeriveInput) -> TokenStream2 { let name = &ast.ident; diff --git a/derive-encode/tests/lib.rs b/derive-encode/tests/lib.rs index 230e9c8e..77bc322f 100644 --- a/derive-encode/tests/lib.rs +++ b/derive-encode/tests/lib.rs @@ -1,4 +1,5 @@ use prometheus_client::encoding::text::{encode, Encode}; +use prometheus_client::encoding::Encode; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; From 8baf256ff856443a25f42ccd5db78a07eaed8297 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 10 Sep 2022 09:39:56 +0900 Subject: [PATCH 68/73] Remove a `Display` requirement Signed-off-by: ackintosh --- derive-encode/src/lib.rs | 9 +++++++-- derive-encode/tests/lib.rs | 19 +++++-------------- src/encoding/proto.rs | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/derive-encode/src/lib.rs b/derive-encode/src/lib.rs index e03926b9..4612ecc9 100644 --- a/derive-encode/src/lib.rs +++ b/derive-encode/src/lib.rs @@ -102,9 +102,14 @@ fn derive_protobuf_encode(ast: DeriveInput) -> TokenStream2 { .unwrap_or_else(|| ident.to_string()); quote! { - let mut label = prometheus_client::encoding::proto::Label::default(); + 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(); - label.value = format!("{}", self.#ident); labels.push(label); } }) diff --git a/derive-encode/tests/lib.rs b/derive-encode/tests/lib.rs index 77bc322f..7056aa25 100644 --- a/derive-encode/tests/lib.rs +++ b/derive-encode/tests/lib.rs @@ -1,10 +1,8 @@ -use prometheus_client::encoding::text::{encode, Encode}; +use prometheus_client::encoding::text::encode; use prometheus_client::encoding::Encode; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; -#[cfg(feature = "protobuf")] -use std::fmt::{Display, Formatter}; #[derive(Clone, Hash, PartialEq, Eq, Encode)] struct Labels { @@ -19,16 +17,6 @@ enum Method { Put, } -#[cfg(feature = "protobuf")] -impl Display for Method { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Method::Get => f.write_str("Get"), - Method::Put => f.write_str("Put"), - } - } -} - #[test] fn basic_flow() { let mut registry = Registry::default(); @@ -130,7 +118,10 @@ fn remap_keyword_identifiers() { let mut buffer = vec![]; - labels.encode(&mut buffer).unwrap(); + { + use prometheus_client::encoding::text::Encode; + labels.encode(&mut buffer).unwrap(); + } assert_eq!( "type=\"42\"".to_string(), diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index 05b7965b..db4af50a 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -139,6 +139,42 @@ impl From<&(K, V)> for openmetrics_data_model::Label { } } +impl EncodeLabels for f64 { + fn encode(&self, labels: &mut Vec) { + labels.push(openmetrics_data_model::Label { + name: self.to_string(), + value: self.to_string(), + }) + } +} + +impl EncodeLabels for u64 { + fn encode(&self, labels: &mut Vec) { + labels.push(openmetrics_data_model::Label { + name: self.to_string(), + value: self.to_string(), + }) + } +} + +impl EncodeLabels for u32 { + fn encode(&self, labels: &mut Vec) { + labels.push(openmetrics_data_model::Label { + name: self.to_string(), + value: self.to_string(), + }) + } +} + +impl EncodeLabels for String { + fn encode(&self, labels: &mut Vec) { + labels.push(openmetrics_data_model::Label { + name: self.clone(), + value: self.clone(), + }) + } +} + impl EncodeLabels for Vec where for<'a> &'a T: Into, From 3a2190d36af83e8ffd3fa324845776c392b0394c Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 10 Sep 2022 10:17:44 +0900 Subject: [PATCH 69/73] Remove unnecessary Display implementation --- benches/encoding/text.rs | 20 -------------------- examples/actix-web.rs | 10 ---------- examples/tide.rs | 10 ---------- 3 files changed, 40 deletions(-) diff --git a/benches/encoding/text.rs b/benches/encoding/text.rs index fc02b5c8..3202dcbf 100644 --- a/benches/encoding/text.rs +++ b/benches/encoding/text.rs @@ -34,26 +34,6 @@ pub fn text(c: &mut Criterion) { Five, } - #[cfg(feature = "protobuf")] - impl std::fmt::Display for Method { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Method::Get => f.write_str("Get"), - Method::Put => f.write_str("Put"), - } - } - } - - #[cfg(feature = "protobuf")] - impl std::fmt::Display for Status { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Status::Two => f.write_str("2"), - Status::Four => f.write_str("4"), - Status::Five => f.write_str("5"), - } - } - } impl Encode for Status { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { let status = match self { diff --git a/examples/actix-web.rs b/examples/actix-web.rs index 0951e76f..80144aa2 100644 --- a/examples/actix-web.rs +++ b/examples/actix-web.rs @@ -13,16 +13,6 @@ pub enum Method { Post, } -#[cfg(feature = "protobuf")] -impl std::fmt::Display for Method { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Method::Get => f.write_str("Get"), - Method::Post => f.write_str("Post"), - } - } -} - #[derive(Clone, Hash, PartialEq, Eq, Encode)] pub struct MethodLabels { pub method: Method, diff --git a/examples/tide.rs b/examples/tide.rs index b71de8d6..244db06d 100644 --- a/examples/tide.rs +++ b/examples/tide.rs @@ -56,16 +56,6 @@ enum Method { Put, } -#[cfg(feature = "protobuf")] -impl std::fmt::Display for Method { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Method::Get => f.write_str("Get"), - Method::Put => f.write_str("Put"), - } - } -} - #[derive(Clone)] struct State { registry: Arc>>, From 396512fb661715711f7fb50b73e7c9bf8d599049 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 10 Sep 2022 10:43:04 +0900 Subject: [PATCH 70/73] Disambiguate `Encode` --- benches/encoding/text.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/benches/encoding/text.rs b/benches/encoding/text.rs index 3202dcbf..1ab6be52 100644 --- a/benches/encoding/text.rs +++ b/benches/encoding/text.rs @@ -1,7 +1,7 @@ // 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; @@ -34,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", @@ -46,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) { + 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::>::default(); for i in 0..100 { From 20fd62e53bef82670496052f621868b66601afb2 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 10 Sep 2022 11:41:18 +0900 Subject: [PATCH 71/73] Fix `cannot find derive macro Encode in this scope` in docs --- src/lib.rs | 2 +- src/metrics/family.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dd9b6541..dbc70f31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! # Examples //! //! ``` -//! use prometheus_client::encoding::text::Encode; +//! use prometheus_client::encoding::Encode; //! use prometheus_client::encoding::text::encode; //! use prometheus_client::metrics::counter::{Atomic, Counter}; //! use prometheus_client::metrics::family::Family; diff --git a/src/metrics/family.rs b/src/metrics/family.rs index b5f54a62..304b0e43 100644 --- a/src/metrics/family.rs +++ b/src/metrics/family.rs @@ -57,7 +57,7 @@ use std::sync::Arc; /// [`Encode`](crate::encoding::text::Encode) implementation. /// /// ``` -/// # use prometheus_client::encoding::text::Encode; +/// # use prometheus_client::encoding::Encode; /// # use prometheus_client::encoding::text::encode; /// # use prometheus_client::metrics::counter::{Atomic, Counter}; /// # use prometheus_client::metrics::family::Family; From 8ac25a52ceea37243950f1328e8407c16e4388ec Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 10 Sep 2022 11:54:34 +0900 Subject: [PATCH 72/73] Fix clippy warnings - `assert!(false, ..)` should probably be replaced - redundant clone - field assignment outside of initializer for an instance created with Default::default() --- src/encoding/proto.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/encoding/proto.rs b/src/encoding/proto.rs index db4af50a..1230ee4b 100644 --- a/src/encoding/proto.rs +++ b/src/encoding/proto.rs @@ -570,7 +570,7 @@ mod tests { assert_eq!(None, value.exemplar); assert_eq!(None, value.created); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -601,7 +601,7 @@ mod tests { assert_eq!(None, value.exemplar); assert_eq!(None, value.created); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -609,7 +609,7 @@ mod tests { fn encode_counter_with_unit() { let mut registry = Registry::default(); let counter: Counter = Counter::default(); - registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter.clone()); + registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter); let metric_set = encode(®istry); @@ -654,14 +654,14 @@ mod tests { assert_eq!(1.0, exemplar.value); let expected_label = { - let mut label = openmetrics_data_model::Label::default(); - label.name = "user_id".to_string(); - label.value = "42".to_string(); - label + openmetrics_data_model::Label { + name: "user_id".to_string(), + value: "42".to_string(), + } }; assert_eq!(vec![expected_label], exemplar.label); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -687,7 +687,7 @@ mod tests { let expected = openmetrics_data_model::gauge_value::Value::IntValue(1); assert_eq!(Some(expected), value.value); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -729,7 +729,7 @@ mod tests { assert_eq!(None, value.exemplar); assert_eq!(None, value.created); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -776,7 +776,7 @@ mod tests { assert_eq!(None, value.exemplar); assert_eq!(None, value.created); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -809,7 +809,7 @@ mod tests { assert_eq!(1, value.count); assert_eq!(11, value.buckets.len()); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -837,14 +837,14 @@ mod tests { assert_eq!(1.0, exemplar.value); let expected_label = { - let mut label = openmetrics_data_model::Label::default(); - label.name = "user_id".to_string(); - label.value = "42".to_string(); - label + openmetrics_data_model::Label { + name: "user_id".to_string(), + value: "42".to_string(), + } }; assert_eq!(vec![expected_label], exemplar.label); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } @@ -948,7 +948,7 @@ mod tests { assert_eq!("os", info.name); assert_eq!("GNU/linux", info.value); } - _ => assert!(false, "wrong value type"), + _ => panic!("wrong value type"), } } From f5b61f1d81e2e51ff61c58dbd75d4f159119ec09 Mon Sep 17 00:00:00 2001 From: ackintosh Date: Sat, 10 Sep 2022 12:04:22 +0900 Subject: [PATCH 73/73] Don't use AtomicI64 on unsupported platforms --- src/metrics/gauge.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/metrics/gauge.rs b/src/metrics/gauge.rs index 50d9542e..297872f3 100644 --- a/src/metrics/gauge.rs +++ b/src/metrics/gauge.rs @@ -5,8 +5,8 @@ use super::{MetricType, TypedMetric}; use std::marker::PhantomData; #[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))] -use std::sync::atomic::AtomicU64; -use std::sync::atomic::{AtomicI64, AtomicU32, Ordering}; +use std::sync::atomic::{AtomicI64, AtomicU64}; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; /// Open Metrics [`Gauge`] to record current measurements. @@ -234,6 +234,7 @@ impl Atomic for AtomicU64 { } } +#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))] impl Atomic for AtomicI64 { fn inc(&self) -> i64 { self.inc_by(1)