diff --git a/CHANGELOG.md b/CHANGELOG.md index 36a27bc0..839d97e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implement `Atomic` for `AtomicU64` for gauges. See [PR 226]. -[PR 226]: https://github.com/prometheus/client_rust/pull/198 +- Implement `EnableLabelValue` for `bool`. + See [PR 237] + +[PR 173]: https://github.com/prometheus/client_rust/pull/173 [PR 198]: https://github.com/prometheus/client_rust/pull/198 +[PR 226]: https://github.com/prometheus/client_rust/pull/226 +[PR 237]: https://github.com/prometheus/client_rust/pull/237 ### Added @@ -32,10 +37,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `histogram::exponential_buckets_range`. See [PR 233]. +- Added `get` method to `Family`. + See [PR 234]. + [PR 173]: https://github.com/prometheus/client_rust/pull/173 [PR 216]: https://github.com/prometheus/client_rust/pull/216 [PR 217]: https://github.com/prometheus/client_rust/pull/217 [PR 233]: https://github.com/prometheus/client_rust/pull/233 +[PR 234]: https://github.com/prometheus/client_rust/pull/234 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e20a178b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing + +## Protocol Buffers + +The `build.rs` script in this library depends upon the +[Protocol Buffers compiler][protoc]. Be sure that `protoc` is installed and +available within your `PATH`. + +[protoc]: https://docs.rs/prost-build/latest/prost_build/#sourcing-protoc + +## Python Dependencies + +This repository uses the [`prometheus-client`][client-python] Python client +library in its test suite. + +You may create and activate a virtual environment with this dependency +installed by running the following shell commands from the root of this +repository: + +```shell +python -m venv ./venv +source venv/bin/activate +pip install prometheus-client +``` + +[client-python]: https://github.com/prometheus/client_python diff --git a/Cargo.toml b/Cargo.toml index 5fae8d0c..8a9c4ed1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ log = "0.4.22" async-std = { version = "1", features = ["attributes"] } axum = "0.7" criterion = "0.5" +futures = "0.3" http-types = "2" pyo3 = "0.22" quickcheck = "1" diff --git a/examples/hyper.rs b/examples/hyper.rs index 82ee121b..bbab0058 100644 --- a/examples/hyper.rs +++ b/examples/hyper.rs @@ -1,3 +1,4 @@ +use futures::future::BoxFuture; use http_body_util::{combinators, BodyExt, Full}; use hyper::{ body::{Bytes, Incoming}, @@ -8,10 +9,8 @@ use hyper::{ use hyper_util::rt::TokioIo; use prometheus_client::{encoding::text::encode, metrics::counter::Counter, registry::Registry}; use std::{ - future::Future, io, net::{IpAddr, Ipv4Addr, SocketAddr}, - pin::Pin, sync::Arc, }; use tokio::{ @@ -69,8 +68,7 @@ type BoxBody = combinators::BoxBody; /// This function returns a HTTP handler (i.e. another function) pub fn make_handler( registry: Arc, -) -> impl Fn(Request) -> Pin>> + Send>> -{ +) -> impl Fn(Request) -> BoxFuture<'static, io::Result>> { // This closure accepts a request and responds with the OpenMetrics encoding of our metrics. move |_req: Request| { let reg = registry.clone(); diff --git a/src/encoding.rs b/src/encoding.rs index 7f77efa0..9e2acac0 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -206,12 +206,6 @@ pub trait EncodeLabelSet { fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error>; } -impl<'a> From> for LabelSetEncoder<'a> { - fn from(e: text::LabelSetEncoder<'a>) -> Self { - Self(LabelSetEncoderInner::Text(e)) - } -} - /// Encoder for a label set. #[derive(Debug)] pub struct LabelSetEncoder<'a>(LabelSetEncoderInner<'a>); @@ -223,6 +217,12 @@ enum LabelSetEncoderInner<'a> { Protobuf(protobuf::LabelSetEncoder<'a>), } +impl<'a> From> for LabelSetEncoder<'a> { + fn from(e: text::LabelSetEncoder<'a>) -> Self { + Self(LabelSetEncoderInner::Text(e)) + } +} + #[cfg(feature = "protobuf")] impl<'a> From> for LabelSetEncoder<'a> { fn from(e: protobuf::LabelSetEncoder<'a>) -> Self { @@ -230,13 +230,49 @@ impl<'a> From> for LabelSetEncoder<'a> { } } -impl<'a> LabelSetEncoder<'a> { +impl LabelSetEncoder<'_> { /// Encode the given label. pub fn encode_label(&mut self) -> LabelEncoder { for_both_mut!(self, LabelSetEncoderInner, e, e.encode_label().into()) } } +impl EncodeLabelSet for [T; N] { + fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { + self.as_ref().encode(encoder) + } +} + +impl EncodeLabelSet for &[T] { + fn encode(&self, mut encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { + if self.is_empty() { + return Ok(()); + } + + for label in self.iter() { + label.encode(encoder.encode_label())? + } + + Ok(()) + } +} + +impl EncodeLabelSet for Vec { + fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { + self.as_slice().encode(encoder) + } +} + +/// Uninhabited type to represent the lack of a label set for a metric +#[derive(Debug)] +pub enum NoLabelSet {} + +impl EncodeLabelSet for NoLabelSet { + fn encode(&self, _encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { + Ok(()) + } +} + /// An encodable label. pub trait EncodeLabel { /// Encode oneself into the given encoder. @@ -247,10 +283,6 @@ pub trait EncodeLabel { #[derive(Debug)] pub struct LabelEncoder<'a>(LabelEncoderInner<'a>); -/// Uninhabited type to represent the lack of a label set for a metric -#[derive(Debug)] -pub enum NoLabelSet {} - #[derive(Debug)] enum LabelEncoderInner<'a> { Text(text::LabelEncoder<'a>), @@ -271,7 +303,7 @@ impl<'a> From> for LabelEncoder<'a> { } } -impl<'a> LabelEncoder<'a> { +impl LabelEncoder<'_> { /// Encode a label. pub fn encode_label_key(&mut self) -> Result { for_both_mut!( @@ -283,6 +315,21 @@ impl<'a> LabelEncoder<'a> { } } +impl EncodeLabel for (K, V) { + fn encode(&self, mut encoder: LabelEncoder) -> Result<(), std::fmt::Error> { + let (key, value) = self; + + let mut label_key_encoder = encoder.encode_label_key()?; + key.encode(&mut label_key_encoder)?; + + let mut label_value_encoder = label_key_encoder.encode_label_value()?; + value.encode(&mut label_value_encoder)?; + label_value_encoder.finish()?; + + Ok(()) + } +} + /// An encodable label key. pub trait EncodeLabelKey { /// Encode oneself into the given encoder. @@ -313,7 +360,7 @@ impl<'a> From> for LabelKeyEncoder<'a> { } } -impl<'a> std::fmt::Write for LabelKeyEncoder<'a> { +impl std::fmt::Write for LabelKeyEncoder<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { for_both_mut!(self, LabelKeyEncoderInner, e, e.write_str(s)) } @@ -330,52 +377,6 @@ impl<'a> LabelKeyEncoder<'a> { ) } } -impl EncodeLabelSet for [T; N] { - fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { - self.as_ref().encode(encoder) - } -} - -impl EncodeLabelSet for &[T] { - fn encode(&self, mut encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { - if self.is_empty() { - return Ok(()); - } - - for label in self.iter() { - label.encode(encoder.encode_label())? - } - - Ok(()) - } -} - -impl EncodeLabelSet for Vec { - fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { - self.as_slice().encode(encoder) - } -} - -impl EncodeLabelSet for NoLabelSet { - fn encode(&self, _encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> { - Ok(()) - } -} - -impl EncodeLabel for (K, V) { - fn encode(&self, mut encoder: LabelEncoder) -> Result<(), std::fmt::Error> { - let (key, value) = self; - - let mut label_key_encoder = encoder.encode_label_key()?; - key.encode(&mut label_key_encoder)?; - - let mut label_value_encoder = label_key_encoder.encode_label_value()?; - value.encode(&mut label_value_encoder)?; - label_value_encoder.finish()?; - - Ok(()) - } -} impl EncodeLabelKey for &str { fn encode(&self, encoder: &mut LabelKeyEncoder) -> Result<(), std::fmt::Error> { @@ -390,7 +391,7 @@ impl EncodeLabelKey for String { } } -impl<'a> EncodeLabelKey for Cow<'a, str> { +impl EncodeLabelKey for Cow<'_, str> { fn encode(&self, encoder: &mut LabelKeyEncoder) -> Result<(), std::fmt::Error> { EncodeLabelKey::encode(&self.as_ref(), encoder) } @@ -433,6 +434,13 @@ pub trait EncodeLabelValue { #[derive(Debug)] pub struct LabelValueEncoder<'a>(LabelValueEncoderInner<'a>); +#[derive(Debug)] +enum LabelValueEncoderInner<'a> { + Text(text::LabelValueEncoder<'a>), + #[cfg(feature = "protobuf")] + Protobuf(protobuf::LabelValueEncoder<'a>), +} + impl<'a> From> for LabelValueEncoder<'a> { fn from(e: text::LabelValueEncoder<'a>) -> Self { LabelValueEncoder(LabelValueEncoderInner::Text(e)) @@ -446,26 +454,19 @@ impl<'a> From> for LabelValueEncoder<'a> { } } -#[derive(Debug)] -enum LabelValueEncoderInner<'a> { - Text(text::LabelValueEncoder<'a>), - #[cfg(feature = "protobuf")] - Protobuf(protobuf::LabelValueEncoder<'a>), +impl std::fmt::Write for LabelValueEncoder<'_> { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + for_both_mut!(self, LabelValueEncoderInner, e, e.write_str(s)) + } } -impl<'a> LabelValueEncoder<'a> { +impl LabelValueEncoder<'_> { /// Finish encoding the label value. pub fn finish(self) -> Result<(), std::fmt::Error> { for_both!(self, LabelValueEncoderInner, e, e.finish()) } } -impl<'a> std::fmt::Write for LabelValueEncoder<'a> { - fn write_str(&mut self, s: &str) -> std::fmt::Result { - for_both_mut!(self, LabelValueEncoderInner, e, e.write_str(s)) - } -} - impl EncodeLabelValue for &str { fn encode(&self, encoder: &mut LabelValueEncoder) -> Result<(), std::fmt::Error> { encoder.write_str(self)?; @@ -485,7 +486,7 @@ impl EncodeLabelValue for &String { } } -impl<'a> EncodeLabelValue for Cow<'a, str> { +impl EncodeLabelValue for Cow<'_, str> { fn encode(&self, encoder: &mut LabelValueEncoder) -> Result<(), std::fmt::Error> { EncodeLabelValue::encode(&self.as_ref(), encoder) } @@ -536,6 +537,12 @@ where } } +impl EncodeLabelValue for bool { + fn encode(&self, encoder: &mut LabelValueEncoder) -> Result<(), std::fmt::Error> { + encoder.write_str(if *self { "true" } else { "false" }) + } +} + macro_rules! impl_encode_label_value_for_integer { ($($t:ident),*) => {$( impl EncodeLabelValue for $t { @@ -610,7 +617,7 @@ enum GaugeValueEncoderInner<'a> { Protobuf(protobuf::GaugeValueEncoder<'a>), } -impl<'a> GaugeValueEncoder<'a> { +impl GaugeValueEncoder<'_> { fn encode_u32(&mut self, v: u32) -> Result<(), std::fmt::Error> { for_both_mut!(self, GaugeValueEncoderInner, e, e.encode_u32(v)) } @@ -678,7 +685,20 @@ enum CounterValueEncoderInner<'a> { Protobuf(protobuf::CounterValueEncoder<'a>), } -impl<'a> CounterValueEncoder<'a> { +impl<'a> From> for CounterValueEncoder<'a> { + fn from(e: text::CounterValueEncoder<'a>) -> Self { + CounterValueEncoder(CounterValueEncoderInner::Text(e)) + } +} + +#[cfg(feature = "protobuf")] +impl<'a> From> for CounterValueEncoder<'a> { + fn from(e: protobuf::CounterValueEncoder<'a>) -> Self { + CounterValueEncoder(CounterValueEncoderInner::Protobuf(e)) + } +} + +impl CounterValueEncoder<'_> { fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> { for_both_mut!(self, CounterValueEncoderInner, e, e.encode_f64(v)) } @@ -718,19 +738,6 @@ impl EncodeExemplarValue for u32 { } } -impl<'a> From> for CounterValueEncoder<'a> { - fn from(e: text::CounterValueEncoder<'a>) -> Self { - CounterValueEncoder(CounterValueEncoderInner::Text(e)) - } -} - -#[cfg(feature = "protobuf")] -impl<'a> From> for CounterValueEncoder<'a> { - fn from(e: protobuf::CounterValueEncoder<'a>) -> Self { - CounterValueEncoder(CounterValueEncoderInner::Protobuf(e)) - } -} - /// Encoder for an exemplar value. #[derive(Debug)] pub struct ExemplarValueEncoder<'a>(ExemplarValueEncoderInner<'a>); @@ -742,12 +749,6 @@ enum ExemplarValueEncoderInner<'a> { Protobuf(protobuf::ExemplarValueEncoder<'a>), } -impl<'a> ExemplarValueEncoder<'a> { - fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> { - for_both_mut!(self, ExemplarValueEncoderInner, e, e.encode(v)) - } -} - impl<'a> From> for ExemplarValueEncoder<'a> { fn from(e: text::ExemplarValueEncoder<'a>) -> Self { ExemplarValueEncoder(ExemplarValueEncoderInner::Text(e)) @@ -760,3 +761,9 @@ impl<'a> From> for ExemplarValueEncoder<'a> { ExemplarValueEncoder(ExemplarValueEncoderInner::Protobuf(e)) } } + +impl ExemplarValueEncoder<'_> { + fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> { + for_both_mut!(self, ExemplarValueEncoderInner, e, e.encode(v)) + } +} diff --git a/src/encoding/protobuf.rs b/src/encoding/protobuf.rs index 095a3dba..3c598144 100644 --- a/src/encoding/protobuf.rs +++ b/src/encoding/protobuf.rs @@ -14,7 +14,7 @@ //! # 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. +//! // Returns `MetricSet`, the top-level container type. Please refer to [openmetrics_data_model.proto](https://github.com/prometheus/OpenMetrics/blob/v1.0.0/proto/openmetrics_data_model.proto) for details. //! let metric_set = encode(®istry).unwrap(); //! //! let family = metric_set.metric_families.first().unwrap(); @@ -322,7 +322,7 @@ pub(crate) struct GaugeValueEncoder<'a> { value: &'a mut openmetrics_data_model::gauge_value::Value, } -impl<'a> GaugeValueEncoder<'a> { +impl GaugeValueEncoder<'_> { pub fn encode_u32(&mut self, v: u32) -> Result<(), std::fmt::Error> { self.encode_i64(v as i64) } @@ -343,7 +343,7 @@ pub(crate) struct ExemplarValueEncoder<'a> { value: &'a mut f64, } -impl<'a> ExemplarValueEncoder<'a> { +impl ExemplarValueEncoder<'_> { pub fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> { *self.value = v; Ok(()) @@ -364,7 +364,7 @@ pub(crate) struct CounterValueEncoder<'a> { value: &'a mut openmetrics_data_model::counter_value::Total, } -impl<'a> CounterValueEncoder<'a> { +impl CounterValueEncoder<'_> { pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> { *self.value = openmetrics_data_model::counter_value::Total::DoubleValue(v); Ok(()) @@ -381,7 +381,7 @@ pub(crate) struct LabelSetEncoder<'a> { labels: &'a mut Vec, } -impl<'a> LabelSetEncoder<'a> { +impl LabelSetEncoder<'_> { pub fn encode_label(&mut self) -> LabelEncoder { LabelEncoder { labels: self.labels, @@ -394,7 +394,7 @@ pub(crate) struct LabelEncoder<'a> { labels: &'a mut Vec, } -impl<'a> LabelEncoder<'a> { +impl LabelEncoder<'_> { pub fn encode_label_key(&mut self) -> Result { self.labels.push(openmetrics_data_model::Label::default()); @@ -409,7 +409,7 @@ pub(crate) struct LabelKeyEncoder<'a> { label: &'a mut openmetrics_data_model::Label, } -impl<'a> std::fmt::Write for LabelKeyEncoder<'a> { +impl std::fmt::Write for LabelKeyEncoder<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { self.label.name.write_str(s) } @@ -428,13 +428,13 @@ pub(crate) struct LabelValueEncoder<'a> { label_value: &'a mut String, } -impl<'a> LabelValueEncoder<'a> { +impl LabelValueEncoder<'_> { pub fn finish(self) -> Result<(), std::fmt::Error> { Ok(()) } } -impl<'a> std::fmt::Write for LabelValueEncoder<'a> { +impl std::fmt::Write for LabelValueEncoder<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { self.label_value.write_str(s) } diff --git a/src/encoding/text.rs b/src/encoding/text.rs index 4946fe35..f10cf1d3 100644 --- a/src/encoding/text.rs +++ b/src/encoding/text.rs @@ -53,7 +53,7 @@ use std::fmt::Write; /// /// Use [`encode_registry`] or [`encode_eof`] if partial encoding is needed. /// -/// See [OpenMetrics exposition format](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#text-format) +/// See [OpenMetrics exposition format](https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#text-format) /// for additional details. /// /// # Examples @@ -188,7 +188,7 @@ pub(crate) struct DescriptorEncoder<'a> { labels: &'a [(Cow<'static, str>, Cow<'static, str>)], } -impl<'a> std::fmt::Debug for DescriptorEncoder<'a> { +impl std::fmt::Debug for DescriptorEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DescriptorEncoder").finish() } @@ -293,7 +293,7 @@ pub(crate) struct MetricEncoder<'a> { family_labels: Option<&'a dyn super::EncodeLabelSet>, } -impl<'a> std::fmt::Debug for MetricEncoder<'a> { +impl std::fmt::Debug for MetricEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut labels = String::new(); if let Some(l) = self.family_labels { @@ -310,7 +310,7 @@ impl<'a> std::fmt::Debug for MetricEncoder<'a> { } } -impl<'a> MetricEncoder<'a> { +impl MetricEncoder<'_> { pub fn encode_counter< S: EncodeLabelSet, CounterValue: super::EncodeCounterValue, @@ -555,13 +555,13 @@ pub(crate) struct CounterValueEncoder<'a> { writer: &'a mut dyn Write, } -impl<'a> std::fmt::Debug for CounterValueEncoder<'a> { +impl std::fmt::Debug for CounterValueEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CounterValueEncoder").finish() } } -impl<'a> CounterValueEncoder<'a> { +impl CounterValueEncoder<'_> { pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> { self.writer.write_str(" ")?; self.writer.write_str(dtoa::Buffer::new().format(v))?; @@ -579,13 +579,13 @@ pub(crate) struct GaugeValueEncoder<'a> { writer: &'a mut dyn Write, } -impl<'a> std::fmt::Debug for GaugeValueEncoder<'a> { +impl std::fmt::Debug for GaugeValueEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("GaugeValueEncoder").finish() } } -impl<'a> GaugeValueEncoder<'a> { +impl GaugeValueEncoder<'_> { pub fn encode_u32(&mut self, v: u32) -> Result<(), std::fmt::Error> { self.writer.write_str(" ")?; self.writer.write_str(itoa::Buffer::new().format(v))?; @@ -609,13 +609,13 @@ pub(crate) struct ExemplarValueEncoder<'a> { writer: &'a mut dyn Write, } -impl<'a> std::fmt::Debug for ExemplarValueEncoder<'a> { +impl std::fmt::Debug for ExemplarValueEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ExemplarValueEncoder").finish() } } -impl<'a> ExemplarValueEncoder<'a> { +impl ExemplarValueEncoder<'_> { pub fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> { self.writer.write_str(dtoa::Buffer::new().format(v)) } @@ -626,7 +626,7 @@ pub(crate) struct LabelSetEncoder<'a> { first: bool, } -impl<'a> std::fmt::Debug for LabelSetEncoder<'a> { +impl std::fmt::Debug for LabelSetEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("LabelSetEncoder") .field("first", &self.first) @@ -657,7 +657,7 @@ pub(crate) struct LabelEncoder<'a> { first: bool, } -impl<'a> std::fmt::Debug for LabelEncoder<'a> { +impl std::fmt::Debug for LabelEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("LabelEncoder") .field("first", &self.first) @@ -665,7 +665,7 @@ impl<'a> std::fmt::Debug for LabelEncoder<'a> { } } -impl<'a> LabelEncoder<'a> { +impl LabelEncoder<'_> { pub fn encode_label_key(&mut self) -> Result { if !self.first { self.writer.write_str(",")?; @@ -680,7 +680,7 @@ pub(crate) struct LabelKeyEncoder<'a> { writer: &'a mut dyn Write, } -impl<'a> std::fmt::Debug for LabelKeyEncoder<'a> { +impl std::fmt::Debug for LabelKeyEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("LabelKeyEncoder").finish() } @@ -695,7 +695,7 @@ impl<'a> LabelKeyEncoder<'a> { } } -impl<'a> std::fmt::Write for LabelKeyEncoder<'a> { +impl std::fmt::Write for LabelKeyEncoder<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { self.writer.write_str(s) } @@ -705,19 +705,19 @@ pub(crate) struct LabelValueEncoder<'a> { writer: &'a mut dyn Write, } -impl<'a> std::fmt::Debug for LabelValueEncoder<'a> { +impl std::fmt::Debug for LabelValueEncoder<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("LabelValueEncoder").finish() } } -impl<'a> LabelValueEncoder<'a> { +impl LabelValueEncoder<'_> { pub fn finish(self) -> Result<(), std::fmt::Error> { self.writer.write_str("\"") } } -impl<'a> std::fmt::Write for LabelValueEncoder<'a> { +impl std::fmt::Write for LabelValueEncoder<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { self.writer.write_str(s) } diff --git a/src/metrics/family.rs b/src/metrics/family.rs index 2f23b198..1a76cf8f 100644 --- a/src/metrics/family.rs +++ b/src/metrics/family.rs @@ -226,9 +226,7 @@ impl> Family MappedRwLockReadGuard { - if let Ok(metric) = - RwLockReadGuard::try_map(self.metrics.read(), |metrics| metrics.get(label_set)) - { + if let Some(metric) = self.get(label_set) { return metric; } @@ -247,6 +245,23 @@ impl> Family, Counter>::default(); + /// + /// if let Some(metric) = family.get(&vec![("method".to_owned(), "GET".to_owned())]) { + /// metric.inc(); + /// }; + /// ``` + pub fn get(&self, label_set: &S) -> Option> { + RwLockReadGuard::try_map(self.metrics.read(), |metrics| metrics.get(label_set)).ok() + } + /// Remove a label set from the metric family. /// /// Returns a bool indicating if a label set was removed or not. @@ -452,4 +467,47 @@ mod tests { .get() ); } + + #[test] + fn test_get() { + let family = Family::, Counter>::default(); + + // Test getting a non-existent metric. + let non_existent = family.get(&vec![("method".to_string(), "GET".to_string())]); + assert!(non_existent.is_none()); + + // Create a metric. + family + .get_or_create(&vec![("method".to_string(), "GET".to_string())]) + .inc(); + + // Test getting an existing metric. + let existing = family.get(&vec![("method".to_string(), "GET".to_string())]); + assert!(existing.is_some()); + assert_eq!(existing.unwrap().get(), 1); + + // Test getting a different non-existent metric. + let another_non_existent = family.get(&vec![("method".to_string(), "POST".to_string())]); + assert!(another_non_existent.is_none()); + + // Test modifying the metric through the returned reference. + if let Some(metric) = family.get(&vec![("method".to_string(), "GET".to_string())]) { + metric.inc(); + } + + // Verify the modification. + let modified = family.get(&vec![("method".to_string(), "GET".to_string())]); + assert_eq!(modified.unwrap().get(), 2); + + // Test with a different label set type. + let string_family = Family::::default(); + string_family.get_or_create(&"test".to_string()).inc(); + + let string_metric = string_family.get(&"test".to_string()); + assert!(string_metric.is_some()); + assert_eq!(string_metric.unwrap().get(), 1); + + let non_existent_string = string_family.get(&"non_existent".to_string()); + assert!(non_existent_string.is_none()); + } }