Skip to content

Commit ee9e3aa

Browse files
authored
feat: support prometheus-client v0.24 (#30)
1 parent d98b16b commit ee9e3aa

File tree

5 files changed

+309
-3
lines changed

5 files changed

+309
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ Supports the following metrics backends:
1515

1616
- Prometheus
1717
- [`prometheus`](https://crates.io/crates/prometheus): `0.13`, `0.14`
18-
- [`prometheus-client`](https://crates.io/crates/prometheus-client): `0.22`, `0.23`
18+
- [`prometheus-client`](https://crates.io/crates/prometheus-client): `0.22`, `0.23`, `0.24`
1919
- OpenTemeletry Metrics
2020
- [`opentelemetry`](https://crates.io/crates/opentelemetry): `0.26`, `0.27`, `0.28`, `0.29`

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ features = [
1818
"prometheus-client",
1919
"prometheus-client_0_22",
2020
"prometheus-client_0_23",
21+
"prometheus-client_0_24",
2122
"opentelemetry",
2223
"opentelemetry_0_26",
2324
"opentelemetry_0_27",
@@ -32,7 +33,8 @@ default = []
3233
prometheus = ["prometheus_0_14"]
3334
prometheus_0_13 = ["dep:prometheus_0_13"]
3435
prometheus_0_14 = ["dep:prometheus_0_14"]
35-
prometheus-client = ["prometheus-client_0_23"]
36+
prometheus-client = ["prometheus-client_0_24"]
37+
prometheus-client_0_24 = ["dep:prometheus-client_0_24"]
3638
prometheus-client_0_23 = ["dep:prometheus-client_0_23"]
3739
prometheus-client_0_22 = ["dep:prometheus-client_0_22"]
3840
opentelemetry = ["opentelemetry_0_30"]
@@ -48,6 +50,7 @@ test-utils = [
4850
"prometheus-client",
4951
"prometheus-client_0_22",
5052
"prometheus-client_0_23",
53+
"prometheus-client_0_24",
5154
"opentelemetry",
5255
"opentelemetry_0_26",
5356
"opentelemetry_0_27",
@@ -67,6 +70,7 @@ opentelemetry_0_30 = { package = "opentelemetry", version = "0.30", default-feat
6770
parking_lot = "0.12"
6871
prometheus-client_0_22 = { package = "prometheus-client", version = "0.22", default-features = false, optional = true }
6972
prometheus-client_0_23 = { package = "prometheus-client", version = "0.23", default-features = false, optional = true }
73+
prometheus-client_0_24 = { package = "prometheus-client", version = "0.24", default-features = false, optional = true }
7074
prometheus_0_13 = { package = "prometheus", version = "0.13", default-features = false, optional = true }
7175
prometheus_0_14 = { package = "prometheus", version = "0.14", default-features = false, optional = true }
7276

src/registry/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub mod prometheus_0_14;
3232

3333
#[cfg(feature = "prometheus-client")]
3434
#[cfg_attr(docsrs, doc(cfg(feature = "prometheus-client")))]
35-
pub use prometheus_client_0_23 as prometheus_client;
35+
pub use prometheus_client_0_24 as prometheus_client;
3636

3737
/// Prometheus metrics components.
3838
#[cfg(feature = "prometheus-client_0_22")]
@@ -44,6 +44,11 @@ pub mod prometheus_client_0_22;
4444
#[cfg_attr(docsrs, doc(cfg(feature = "prometheus-client_0_23")))]
4545
pub mod prometheus_client_0_23;
4646

47+
/// Prometheus metrics components.
48+
#[cfg(feature = "prometheus-client_0_24")]
49+
#[cfg_attr(docsrs, doc(cfg(feature = "prometheus-client_0_24")))]
50+
pub mod prometheus_client_0_24;
51+
4752
#[cfg(feature = "opentelemetry")]
4853
#[cfg_attr(docsrs, doc(cfg(feature = "opentelemetry")))]
4954
pub use opentelemetry_0_29 as opentelemetry;
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
// Copyright 2025 mixtrics Project Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use std::{borrow::Cow, sync::Arc};
16+
17+
use itertools::Itertools;
18+
use parking_lot::Mutex;
19+
use prometheus_client_0_24::{
20+
encoding::{EncodeLabel, EncodeLabelSet, LabelSetEncoder},
21+
metrics::{
22+
counter::Counter as PcCounter,
23+
family::{Family, MetricConstructor},
24+
gauge::Gauge as PcGauge,
25+
histogram::Histogram as PcHistogram,
26+
},
27+
registry::Registry,
28+
};
29+
30+
use crate::{
31+
metrics::{
32+
BoxedCounter, BoxedCounterVec, BoxedGauge, BoxedGaugeVec, BoxedHistogram, BoxedHistogramVec, CounterOps,
33+
CounterVecOps, GaugeOps, GaugeVecOps, HistogramOps, HistogramVecOps, RegistryOps,
34+
},
35+
utils::Boxer,
36+
};
37+
38+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
39+
struct Labels {
40+
pairs: Vec<(&'static str, Cow<'static, str>)>,
41+
}
42+
43+
impl EncodeLabelSet for Labels {
44+
fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), std::fmt::Error> {
45+
for pair in self.pairs.iter() {
46+
pair.encode(encoder.encode_label())?;
47+
}
48+
Ok(())
49+
}
50+
}
51+
52+
#[derive(Debug)]
53+
struct Counter {
54+
counter: Family<Labels, PcCounter>,
55+
labels: Labels,
56+
}
57+
58+
impl CounterOps for Counter {
59+
fn increase(&self, val: u64) {
60+
self.counter.get_or_create(&self.labels).inc_by(val);
61+
}
62+
}
63+
64+
#[derive(Debug)]
65+
struct CounterVec {
66+
counter: Family<Labels, PcCounter>,
67+
label_names: &'static [&'static str],
68+
}
69+
70+
impl CounterVecOps for CounterVec {
71+
fn counter(&self, labels: &[Cow<'static, str>]) -> BoxedCounter {
72+
Counter {
73+
counter: self.counter.clone(),
74+
labels: Labels {
75+
pairs: self
76+
.label_names
77+
.iter()
78+
.zip_eq(labels.iter())
79+
.map(|(name, label)| (*name, label.clone()))
80+
.collect(),
81+
},
82+
}
83+
.boxed()
84+
}
85+
}
86+
87+
#[derive(Debug)]
88+
struct Gauge {
89+
gauge: Family<Labels, PcGauge>,
90+
labels: Labels,
91+
}
92+
93+
impl GaugeOps for Gauge {
94+
fn increase(&self, val: u64) {
95+
self.gauge.get_or_create(&self.labels).inc_by(val as _);
96+
}
97+
98+
fn decrease(&self, val: u64) {
99+
self.gauge.get_or_create(&self.labels).dec_by(val as _);
100+
}
101+
102+
fn absolute(&self, val: u64) {
103+
self.gauge.get_or_create(&self.labels).set(val as _);
104+
}
105+
}
106+
107+
#[derive(Debug)]
108+
struct GaugeVec {
109+
gauge: Family<Labels, PcGauge>,
110+
label_names: &'static [&'static str],
111+
}
112+
113+
impl GaugeVecOps for GaugeVec {
114+
fn gauge(&self, labels: &[Cow<'static, str>]) -> BoxedGauge {
115+
Gauge {
116+
gauge: self.gauge.clone(),
117+
labels: Labels {
118+
pairs: self
119+
.label_names
120+
.iter()
121+
.zip_eq(labels.iter())
122+
.map(|(name, label)| (*name, label.clone()))
123+
.collect(),
124+
},
125+
}
126+
.boxed()
127+
}
128+
}
129+
130+
#[derive(Debug)]
131+
struct Histogram {
132+
histogram: Family<Labels, PcHistogram, PcHistogramBuilder>,
133+
labels: Labels,
134+
}
135+
136+
impl HistogramOps for Histogram {
137+
fn record(&self, val: f64) {
138+
self.histogram.get_or_create(&self.labels).observe(val);
139+
}
140+
}
141+
142+
#[derive(Debug)]
143+
struct HistogramVec {
144+
histogram: Family<Labels, PcHistogram, PcHistogramBuilder>,
145+
label_names: &'static [&'static str],
146+
}
147+
148+
impl HistogramVecOps for HistogramVec {
149+
fn histogram(&self, labels: &[Cow<'static, str>]) -> BoxedHistogram {
150+
Histogram {
151+
histogram: self.histogram.clone(),
152+
labels: Labels {
153+
pairs: self
154+
.label_names
155+
.iter()
156+
.zip_eq(labels.iter())
157+
.map(|(name, label)| (*name, label.clone()))
158+
.collect(),
159+
},
160+
}
161+
.boxed()
162+
}
163+
}
164+
165+
#[derive(Debug, Clone)]
166+
/// Prometheus metric registry with lib `prometheus-client`.
167+
pub struct PrometheusClientMetricsRegistry {
168+
registry: Arc<Mutex<Registry>>,
169+
}
170+
171+
impl PrometheusClientMetricsRegistry {
172+
/// Create an Prometheus metrics registry.
173+
pub fn new(registry: Arc<Mutex<Registry>>) -> Self {
174+
Self { registry }
175+
}
176+
}
177+
178+
impl RegistryOps for PrometheusClientMetricsRegistry {
179+
fn register_counter_vec(
180+
&self,
181+
name: Cow<'static, str>,
182+
desc: Cow<'static, str>,
183+
label_names: &'static [&'static str],
184+
) -> BoxedCounterVec {
185+
let counter = Family::<Labels, PcCounter>::default();
186+
self.registry.lock().register(name, desc, counter.clone());
187+
CounterVec { counter, label_names }.boxed()
188+
}
189+
190+
fn register_gauge_vec(
191+
&self,
192+
name: Cow<'static, str>,
193+
desc: Cow<'static, str>,
194+
label_names: &'static [&'static str],
195+
) -> BoxedGaugeVec {
196+
let gauge = Family::<Labels, PcGauge>::default();
197+
self.registry.lock().register(name, desc, gauge.clone());
198+
GaugeVec { gauge, label_names }.boxed()
199+
}
200+
201+
fn register_histogram_vec(
202+
&self,
203+
name: Cow<'static, str>,
204+
desc: Cow<'static, str>,
205+
label_names: &'static [&'static str],
206+
) -> BoxedHistogramVec {
207+
let histogram = Family::<Labels, PcHistogram, PcHistogramBuilder>::new_with_constructor(PcHistogramBuilder {
208+
buckets: vec![0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0],
209+
});
210+
self.registry.lock().register(name, desc, histogram.clone());
211+
HistogramVec { histogram, label_names }.boxed()
212+
}
213+
214+
fn register_histogram_vec_with_buckets(
215+
&self,
216+
name: Cow<'static, str>,
217+
desc: Cow<'static, str>,
218+
label_names: &'static [&'static str],
219+
buckets: Vec<f64>,
220+
) -> BoxedHistogramVec {
221+
let histogram =
222+
Family::<Labels, PcHistogram, PcHistogramBuilder>::new_with_constructor(PcHistogramBuilder { buckets });
223+
self.registry.lock().register(name, desc, histogram.clone());
224+
HistogramVec { histogram, label_names }.boxed()
225+
}
226+
}
227+
228+
#[derive(Debug, Clone)]
229+
struct PcHistogramBuilder {
230+
buckets: Vec<f64>,
231+
}
232+
233+
impl MetricConstructor<PcHistogram> for PcHistogramBuilder {
234+
fn new_metric(&self) -> PcHistogram {
235+
PcHistogram::new(self.buckets.iter().copied())
236+
}
237+
}
238+
239+
#[cfg(test)]
240+
mod tests {
241+
use prometheus_client_0_24::encoding::text::encode;
242+
243+
use super::*;
244+
245+
#[test]
246+
fn test() {
247+
let registry = Arc::new(Mutex::new(Registry::default()));
248+
let pc = PrometheusClientMetricsRegistry::new(registry.clone());
249+
250+
let cv = pc.register_counter_vec("test_counter_1".into(), "test counter 1".into(), &["label1", "label2"]);
251+
let c = cv.counter(&["l1".into(), "l2".into()]);
252+
c.increase(42);
253+
254+
let gv = pc.register_gauge_vec("test_gauge_1".into(), "test gauge 1".into(), &["label1", "label2"]);
255+
let g = gv.gauge(&["l1".into(), "l2".into()]);
256+
g.increase(514);
257+
g.decrease(114);
258+
g.absolute(114514);
259+
260+
let hv = pc.register_histogram_vec(
261+
"test_histogram_1".into(),
262+
"test histogram 1".into(),
263+
&["label1", "label2"],
264+
);
265+
let h = hv.histogram(&["l1".into(), "l2".into()]);
266+
h.record(114.514);
267+
268+
let hv = pc.register_histogram_vec_with_buckets(
269+
"test_histogram_2".into(),
270+
"test histogram 2".into(),
271+
&["label1", "label2"],
272+
vec![1.0, 10.0, 100.0],
273+
);
274+
let h = hv.histogram(&["l1".into(), "l2".into()]);
275+
h.record(114.514);
276+
277+
let mut text = String::new();
278+
encode(&mut text, &registry.lock()).unwrap();
279+
println!("{text}");
280+
}
281+
}

src/test_utils.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub use prometheus_0_13;
2121
pub use prometheus_0_14;
2222
pub use prometheus_client_0_22;
2323
pub use prometheus_client_0_23;
24+
pub use prometheus_client_0_24;
2425

2526
#[macro_export]
2627
macro_rules! test {
@@ -75,6 +76,21 @@ macro_rules! test {
7576
($f)(&registry);
7677
}
7778

79+
#[test]
80+
fn test_prometheus_client_0_24() {
81+
use std::sync::Arc;
82+
83+
use parking_lot::Mutex;
84+
use $crate::{
85+
metrics::BoxedRegistry, registry::prometheus_client_0_24::PrometheusClientMetricsRegistry,
86+
test_utils::prometheus_client_0_24::registry::Registry,
87+
};
88+
let registry: BoxedRegistry = Box::new(PrometheusClientMetricsRegistry::new(Arc::new(Mutex::new(
89+
Registry::default(),
90+
))));
91+
($f)(&registry);
92+
}
93+
7894
#[test]
7995
fn test_opentelemetry_0_26() {
8096
use $crate::{

0 commit comments

Comments
 (0)