Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit baefdcd

Browse files
authoredJul 9, 2024
Merge branch 'master' into update-some-deps
2 parents 1a95902 + a914fc9 commit baefdcd

File tree

6 files changed

+224
-26
lines changed

6 files changed

+224
-26
lines changed
 

‎CHANGELOG.md

+14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.22.3] - unreleased
8+
9+
### Added
10+
11+
- Added `encode_registry` and `encode_eof` functions to `text` module.
12+
See [PR 205].
13+
14+
[PR 205]: https://github.com/prometheus/client_rust/pull/205
15+
16+
- Support all platforms with 32 bit atomics lacking 64 bit atomics.
17+
See [PR 203].
18+
19+
[PR 203]: https://github.com/prometheus/client_rust/pull/203
20+
721
## [0.22.2]
822

923
### Added

‎Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "prometheus-client"
3-
version = "0.22.2"
3+
version = "0.22.3"
44
authors = ["Max Inden <mail@max-inden.de>"]
55
edition = "2021"
66
description = "Open Metrics client library allowing users to natively instrument applications."

‎src/encoding/text.rs

+194-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Open Metrics text format implementation.
22
//!
33
//! ```
4-
//! # use prometheus_client::encoding::text::encode;
4+
//! # use prometheus_client::encoding::text::{encode, encode_registry, encode_eof};
55
//! # use prometheus_client::metrics::counter::Counter;
66
//! # use prometheus_client::registry::Registry;
77
//! #
@@ -15,13 +15,26 @@
1515
//! # );
1616
//! # counter.inc();
1717
//! let mut buffer = String::new();
18+
//!
19+
//! // Encode the complete OpenMetrics exposition into the message buffer
1820
//! encode(&mut buffer, &registry).unwrap();
21+
//! let expected_msg = "# HELP my_counter This is my counter.\n".to_owned() +
22+
//! "# TYPE my_counter counter\n" +
23+
//! "my_counter_total 1\n" +
24+
//! "# EOF\n";
25+
//! assert_eq!(expected_msg, buffer);
26+
//! buffer.clear();
27+
//!
28+
//! // Encode just the registry into the message buffer
29+
//! encode_registry(&mut buffer, &registry).unwrap();
30+
//! let expected_reg = "# HELP my_counter This is my counter.\n".to_owned() +
31+
//! "# TYPE my_counter counter\n" +
32+
//! "my_counter_total 1\n";
33+
//! assert_eq!(expected_reg, buffer);
1934
//!
20-
//! let expected = "# HELP my_counter This is my counter.\n".to_owned() +
21-
//! "# TYPE my_counter counter\n" +
22-
//! "my_counter_total 1\n" +
23-
//! "# EOF\n";
24-
//! assert_eq!(expected, buffer);
35+
//! // Encode EOF marker into message buffer to complete the OpenMetrics exposition
36+
//! encode_eof(&mut buffer).unwrap();
37+
//! assert_eq!(expected_msg, buffer);
2538
//! ```
2639
2740
use crate::encoding::{EncodeExemplarValue, EncodeLabelSet};
@@ -33,15 +46,140 @@ use std::borrow::Cow;
3346
use std::collections::HashMap;
3447
use std::fmt::Write;
3548

49+
/// Encode both the metrics registered with the provided [`Registry`] and the
50+
/// EOF marker into the provided [`Write`]r using the OpenMetrics text format.
51+
///
52+
/// Note: This function encodes the **complete** OpenMetrics exposition.
53+
///
54+
/// Use [`encode_registry`] or [`encode_eof`] if partial encoding is needed.
55+
///
56+
/// See [OpenMetrics exposition format](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#text-format)
57+
/// for additional details.
58+
///
59+
/// # Examples
60+
///
61+
/// ```no_run
62+
/// # use prometheus_client::encoding::text::encode;
63+
/// # use prometheus_client::metrics::counter::Counter;
64+
/// # use prometheus_client::metrics::gauge::Gauge;
65+
/// # use prometheus_client::registry::Registry;
66+
/// #
67+
/// // Initialize registry with metric families
68+
/// let mut registry = Registry::default();
69+
/// let counter: Counter = Counter::default();
70+
/// registry.register(
71+
/// "my_counter",
72+
/// "This is my counter",
73+
/// counter.clone(),
74+
/// );
75+
/// let gauge: Gauge = Gauge::default();
76+
/// registry.register(
77+
/// "my_gauge",
78+
/// "This is my gauge",
79+
/// gauge.clone(),
80+
/// );
81+
///
82+
/// // Encode the complete OpenMetrics exposition into the buffer
83+
/// let mut buffer = String::new();
84+
/// encode(&mut buffer, &registry)?;
85+
/// # Ok::<(), std::fmt::Error>(())
86+
/// ```
87+
pub fn encode<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
88+
where
89+
W: Write,
90+
{
91+
encode_registry(writer, registry)?;
92+
encode_eof(writer)
93+
}
94+
3695
/// Encode the metrics registered with the provided [`Registry`] into the
3796
/// provided [`Write`]r using the OpenMetrics text format.
38-
pub fn encode<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
97+
///
98+
/// Note: The OpenMetrics exposition requires that a complete message must end
99+
/// with an EOF marker.
100+
///
101+
/// This function may be called repeatedly for the HTTP scrape response until
102+
/// [`encode_eof`] signals the end of the response.
103+
///
104+
/// This may also be used to compose a partial message with metrics assembled
105+
/// from multiple registries.
106+
///
107+
/// # Examples
108+
///
109+
/// ```no_run
110+
/// # use prometheus_client::encoding::text::encode_registry;
111+
/// # use prometheus_client::metrics::counter::Counter;
112+
/// # use prometheus_client::metrics::gauge::Gauge;
113+
/// # use prometheus_client::registry::Registry;
114+
/// #
115+
/// // Initialize registry with a counter
116+
/// let mut reg_counter = Registry::default();
117+
/// let counter: Counter = Counter::default();
118+
/// reg_counter.register(
119+
/// "my_counter",
120+
/// "This is my counter",
121+
/// counter.clone(),
122+
/// );
123+
///
124+
/// // Encode the counter registry into the buffer
125+
/// let mut buffer = String::new();
126+
/// encode_registry(&mut buffer, &reg_counter)?;
127+
///
128+
/// // Initialize another registry but with a gauge
129+
/// let mut reg_gauge = Registry::default();
130+
/// let gauge: Gauge = Gauge::default();
131+
/// reg_gauge.register(
132+
/// "my_gauge",
133+
/// "This is my gauge",
134+
/// gauge.clone(),
135+
/// );
136+
///
137+
/// // Encode the gauge registry into the buffer
138+
/// encode_registry(&mut buffer, &reg_gauge)?;
139+
/// # Ok::<(), std::fmt::Error>(())
140+
/// ```
141+
pub fn encode_registry<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
39142
where
40143
W: Write,
41144
{
42-
registry.encode(&mut DescriptorEncoder::new(writer).into())?;
43-
writer.write_str("# EOF\n")?;
44-
Ok(())
145+
registry.encode(&mut DescriptorEncoder::new(writer).into())
146+
}
147+
148+
/// Encode the EOF marker into the provided [`Write`]r using the OpenMetrics
149+
/// text format.
150+
///
151+
/// Note: This function is used to mark/signal the end of the exposition.
152+
///
153+
/// # Examples
154+
///
155+
/// ```no_run
156+
/// # use prometheus_client::encoding::text::{encode_registry, encode_eof};
157+
/// # use prometheus_client::metrics::counter::Counter;
158+
/// # use prometheus_client::metrics::gauge::Gauge;
159+
/// # use prometheus_client::registry::Registry;
160+
/// #
161+
/// // Initialize registry with a counter
162+
/// let mut registry = Registry::default();
163+
/// let counter: Counter = Counter::default();
164+
/// registry.register(
165+
/// "my_counter",
166+
/// "This is my counter",
167+
/// counter.clone(),
168+
/// );
169+
///
170+
/// // Encode registry into the buffer
171+
/// let mut buffer = String::new();
172+
/// encode_registry(&mut buffer, &registry)?;
173+
///
174+
/// // Encode EOF marker to complete the message
175+
/// encode_eof(&mut buffer)?;
176+
/// # Ok::<(), std::fmt::Error>(())
177+
/// ```
178+
pub fn encode_eof<W>(writer: &mut W) -> Result<(), std::fmt::Error>
179+
where
180+
W: Write,
181+
{
182+
writer.write_str("# EOF\n")
45183
}
46184

47185
pub(crate) struct DescriptorEncoder<'a> {
@@ -915,6 +1053,52 @@ mod tests {
9151053
parse_with_python_client(encoded);
9161054
}
9171055

1056+
#[test]
1057+
fn encode_registry_eof() {
1058+
let mut orders_registry = Registry::default();
1059+
1060+
let total_orders: Counter<u64> = Default::default();
1061+
orders_registry.register("orders", "Total orders received", total_orders.clone());
1062+
total_orders.inc();
1063+
1064+
let processing_times = Histogram::new(exponential_buckets(1.0, 2.0, 10));
1065+
orders_registry.register_with_unit(
1066+
"processing_times",
1067+
"Order times",
1068+
Unit::Seconds,
1069+
processing_times.clone(),
1070+
);
1071+
processing_times.observe(2.4);
1072+
1073+
let mut user_auth_registry = Registry::default();
1074+
1075+
let successful_logins: Counter<u64> = Default::default();
1076+
user_auth_registry.register(
1077+
"successful_logins",
1078+
"Total successful logins",
1079+
successful_logins.clone(),
1080+
);
1081+
successful_logins.inc();
1082+
1083+
let failed_logins: Counter<u64> = Default::default();
1084+
user_auth_registry.register(
1085+
"failed_logins",
1086+
"Total failed logins",
1087+
failed_logins.clone(),
1088+
);
1089+
1090+
let mut response = String::new();
1091+
1092+
encode_registry(&mut response, &orders_registry).unwrap();
1093+
assert_eq!(&response[response.len() - 20..], "bucket{le=\"+Inf\"} 1\n");
1094+
1095+
encode_registry(&mut response, &user_auth_registry).unwrap();
1096+
assert_eq!(&response[response.len() - 20..], "iled_logins_total 0\n");
1097+
1098+
encode_eof(&mut response).unwrap();
1099+
assert_eq!(&response[response.len() - 20..], "ogins_total 0\n# EOF\n");
1100+
}
1101+
9181102
fn parse_with_python_client(input: String) {
9191103
pyo3::prepare_freethreaded_python();
9201104

‎src/metrics/counter.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::encoding::{EncodeMetric, MetricEncoder};
66

77
use super::{MetricType, TypedMetric};
88
use std::marker::PhantomData;
9-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
9+
#[cfg(target_has_atomic = "64")]
1010
use std::sync::atomic::AtomicU64;
1111
use std::sync::atomic::{AtomicU32, Ordering};
1212
use std::sync::Arc;
@@ -40,15 +40,15 @@ use std::sync::Arc;
4040
/// counter.inc();
4141
/// let _value: f64 = counter.get();
4242
/// ```
43-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
43+
#[cfg(target_has_atomic = "64")]
4444
#[derive(Debug)]
4545
pub struct Counter<N = u64, A = AtomicU64> {
4646
value: Arc<A>,
4747
phantom: PhantomData<N>,
4848
}
4949

5050
/// Open Metrics [`Counter`] to measure discrete events.
51-
#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
51+
#[cfg(not(target_has_atomic = "64"))]
5252
#[derive(Debug)]
5353
pub struct Counter<N = u32, A = AtomicU32> {
5454
value: Arc<A>,
@@ -114,7 +114,7 @@ pub trait Atomic<N> {
114114
fn get(&self) -> N;
115115
}
116116

117-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
117+
#[cfg(target_has_atomic = "64")]
118118
impl Atomic<u64> for AtomicU64 {
119119
fn inc(&self) -> u64 {
120120
self.inc_by(1)
@@ -143,7 +143,7 @@ impl Atomic<u32> for AtomicU32 {
143143
}
144144
}
145145

146-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
146+
#[cfg(target_has_atomic = "64")]
147147
impl Atomic<f64> for AtomicU64 {
148148
fn inc(&self) -> f64 {
149149
self.inc_by(1.0)
@@ -231,7 +231,7 @@ mod tests {
231231
assert_eq!(1, counter.get());
232232
}
233233

234-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
234+
#[cfg(target_has_atomic = "64")]
235235
#[test]
236236
fn f64_stored_in_atomic_u64() {
237237
fn prop(fs: Vec<f64>) {

‎src/metrics/exemplar.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ use super::histogram::Histogram;
1111
use super::{MetricType, TypedMetric};
1212
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
1313
use std::collections::HashMap;
14-
#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
14+
#[cfg(not(target_has_atomic = "64"))]
1515
use std::sync::atomic::AtomicU32;
16-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
16+
#[cfg(target_has_atomic = "64")]
1717
use std::sync::atomic::AtomicU64;
1818
use std::sync::Arc;
1919

@@ -65,7 +65,7 @@ pub struct Exemplar<S, V> {
6565
/// }),
6666
/// );
6767
/// ```
68-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
68+
#[cfg(target_has_atomic = "64")]
6969
#[derive(Debug)]
7070
pub struct CounterWithExemplar<S, N = u64, A = AtomicU64> {
7171
pub(crate) inner: Arc<RwLock<CounterWithExemplarInner<S, N, A>>>,
@@ -77,7 +77,7 @@ impl<S> TypedMetric for CounterWithExemplar<S> {
7777

7878
/// Open Metrics [`Counter`] with an [`Exemplar`] to both measure discrete
7979
/// events and track references to data outside of the metric set.
80-
#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
80+
#[cfg(not(target_has_atomic = "64"))]
8181
#[derive(Debug)]
8282
pub struct CounterWithExemplar<S, N = u32, A = AtomicU32> {
8383
pub(crate) inner: Arc<RwLock<CounterWithExemplarInner<S, N, A>>>,

‎src/metrics/gauge.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::encoding::{EncodeGaugeValue, EncodeMetric, MetricEncoder};
77
use super::{MetricType, TypedMetric};
88
use std::marker::PhantomData;
99
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
10-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
10+
#[cfg(target_has_atomic = "64")]
1111
use std::sync::atomic::{AtomicI64, AtomicU64};
1212
use std::sync::Arc;
1313

@@ -40,15 +40,15 @@ use std::sync::Arc;
4040
/// gauge.set(42.0);
4141
/// let _value: f64 = gauge.get();
4242
/// ```
43-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
43+
#[cfg(target_has_atomic = "64")]
4444
#[derive(Debug)]
4545
pub struct Gauge<N = i64, A = AtomicI64> {
4646
value: Arc<A>,
4747
phantom: PhantomData<N>,
4848
}
4949

5050
/// Open Metrics [`Gauge`] to record current measurements.
51-
#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
51+
#[cfg(not(target_has_atomic = "64"))]
5252
#[derive(Debug)]
5353
pub struct Gauge<N = i32, A = AtomicI32> {
5454
value: Arc<A>,
@@ -186,7 +186,7 @@ impl Atomic<u32> for AtomicU32 {
186186
}
187187
}
188188

189-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
189+
#[cfg(target_has_atomic = "64")]
190190
impl Atomic<i64> for AtomicI64 {
191191
fn inc(&self) -> i64 {
192192
self.inc_by(1)
@@ -213,7 +213,7 @@ impl Atomic<i64> for AtomicI64 {
213213
}
214214
}
215215

216-
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
216+
#[cfg(target_has_atomic = "64")]
217217
impl Atomic<f64> for AtomicU64 {
218218
fn inc(&self) -> f64 {
219219
self.inc_by(1.0)

0 commit comments

Comments
 (0)
Please sign in to comment.