Skip to content

Commit

Permalink
Merge branch 'master' into update-some-deps
Browse files Browse the repository at this point in the history
  • Loading branch information
koushiro authored Jul 9, 2024
2 parents 1a95902 + a914fc9 commit baefdcd
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 26 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ 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.22.3] - unreleased

### Added

- Added `encode_registry` and `encode_eof` functions to `text` module.
See [PR 205].

[PR 205]: https://github.com/prometheus/client_rust/pull/205

- Support all platforms with 32 bit atomics lacking 64 bit atomics.
See [PR 203].

[PR 203]: https://github.com/prometheus/client_rust/pull/203

## [0.22.2]

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "prometheus-client"
version = "0.22.2"
version = "0.22.3"
authors = ["Max Inden <[email protected]>"]
edition = "2021"
description = "Open Metrics client library allowing users to natively instrument applications."
Expand Down
204 changes: 194 additions & 10 deletions src/encoding/text.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Open Metrics text format implementation.
//!
//! ```
//! # use prometheus_client::encoding::text::encode;
//! # use prometheus_client::encoding::text::{encode, encode_registry, encode_eof};
//! # use prometheus_client::metrics::counter::Counter;
//! # use prometheus_client::registry::Registry;
//! #
Expand All @@ -15,13 +15,26 @@
//! # );
//! # counter.inc();
//! let mut buffer = String::new();
//!
//! // Encode the complete OpenMetrics exposition into the message buffer
//! encode(&mut buffer, &registry).unwrap();
//! let expected_msg = "# HELP my_counter This is my counter.\n".to_owned() +
//! "# TYPE my_counter counter\n" +
//! "my_counter_total 1\n" +
//! "# EOF\n";
//! assert_eq!(expected_msg, buffer);
//! buffer.clear();
//!
//! // Encode just the registry into the message buffer
//! encode_registry(&mut buffer, &registry).unwrap();
//! let expected_reg = "# HELP my_counter This is my counter.\n".to_owned() +
//! "# TYPE my_counter counter\n" +
//! "my_counter_total 1\n";
//! assert_eq!(expected_reg, buffer);
//!
//! let expected = "# HELP my_counter This is my counter.\n".to_owned() +
//! "# TYPE my_counter counter\n" +
//! "my_counter_total 1\n" +
//! "# EOF\n";
//! assert_eq!(expected, buffer);
//! // Encode EOF marker into message buffer to complete the OpenMetrics exposition
//! encode_eof(&mut buffer).unwrap();
//! assert_eq!(expected_msg, buffer);
//! ```
use crate::encoding::{EncodeExemplarValue, EncodeLabelSet};
Expand All @@ -33,15 +46,140 @@ use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;

/// Encode both the metrics registered with the provided [`Registry`] and the
/// EOF marker into the provided [`Write`]r using the OpenMetrics text format.
///
/// Note: This function encodes the **complete** OpenMetrics exposition.
///
/// 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)
/// for additional details.
///
/// # Examples
///
/// ```no_run
/// # use prometheus_client::encoding::text::encode;
/// # use prometheus_client::metrics::counter::Counter;
/// # use prometheus_client::metrics::gauge::Gauge;
/// # use prometheus_client::registry::Registry;
/// #
/// // Initialize registry with metric families
/// let mut registry = Registry::default();
/// let counter: Counter = Counter::default();
/// registry.register(
/// "my_counter",
/// "This is my counter",
/// counter.clone(),
/// );
/// let gauge: Gauge = Gauge::default();
/// registry.register(
/// "my_gauge",
/// "This is my gauge",
/// gauge.clone(),
/// );
///
/// // Encode the complete OpenMetrics exposition into the buffer
/// let mut buffer = String::new();
/// encode(&mut buffer, &registry)?;
/// # Ok::<(), std::fmt::Error>(())
/// ```
pub fn encode<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
where
W: Write,
{
encode_registry(writer, registry)?;
encode_eof(writer)
}

/// Encode the metrics registered with the provided [`Registry`] into the
/// provided [`Write`]r using the OpenMetrics text format.
pub fn encode<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
///
/// Note: The OpenMetrics exposition requires that a complete message must end
/// with an EOF marker.
///
/// This function may be called repeatedly for the HTTP scrape response until
/// [`encode_eof`] signals the end of the response.
///
/// This may also be used to compose a partial message with metrics assembled
/// from multiple registries.
///
/// # Examples
///
/// ```no_run
/// # use prometheus_client::encoding::text::encode_registry;
/// # use prometheus_client::metrics::counter::Counter;
/// # use prometheus_client::metrics::gauge::Gauge;
/// # use prometheus_client::registry::Registry;
/// #
/// // Initialize registry with a counter
/// let mut reg_counter = Registry::default();
/// let counter: Counter = Counter::default();
/// reg_counter.register(
/// "my_counter",
/// "This is my counter",
/// counter.clone(),
/// );
///
/// // Encode the counter registry into the buffer
/// let mut buffer = String::new();
/// encode_registry(&mut buffer, &reg_counter)?;
///
/// // Initialize another registry but with a gauge
/// let mut reg_gauge = Registry::default();
/// let gauge: Gauge = Gauge::default();
/// reg_gauge.register(
/// "my_gauge",
/// "This is my gauge",
/// gauge.clone(),
/// );
///
/// // Encode the gauge registry into the buffer
/// encode_registry(&mut buffer, &reg_gauge)?;
/// # Ok::<(), std::fmt::Error>(())
/// ```
pub fn encode_registry<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
where
W: Write,
{
registry.encode(&mut DescriptorEncoder::new(writer).into())?;
writer.write_str("# EOF\n")?;
Ok(())
registry.encode(&mut DescriptorEncoder::new(writer).into())
}

/// Encode the EOF marker into the provided [`Write`]r using the OpenMetrics
/// text format.
///
/// Note: This function is used to mark/signal the end of the exposition.
///
/// # Examples
///
/// ```no_run
/// # use prometheus_client::encoding::text::{encode_registry, encode_eof};
/// # use prometheus_client::metrics::counter::Counter;
/// # use prometheus_client::metrics::gauge::Gauge;
/// # use prometheus_client::registry::Registry;
/// #
/// // Initialize registry with a counter
/// let mut registry = Registry::default();
/// let counter: Counter = Counter::default();
/// registry.register(
/// "my_counter",
/// "This is my counter",
/// counter.clone(),
/// );
///
/// // Encode registry into the buffer
/// let mut buffer = String::new();
/// encode_registry(&mut buffer, &registry)?;
///
/// // Encode EOF marker to complete the message
/// encode_eof(&mut buffer)?;
/// # Ok::<(), std::fmt::Error>(())
/// ```
pub fn encode_eof<W>(writer: &mut W) -> Result<(), std::fmt::Error>
where
W: Write,
{
writer.write_str("# EOF\n")
}

pub(crate) struct DescriptorEncoder<'a> {
Expand Down Expand Up @@ -915,6 +1053,52 @@ mod tests {
parse_with_python_client(encoded);
}

#[test]
fn encode_registry_eof() {
let mut orders_registry = Registry::default();

let total_orders: Counter<u64> = Default::default();
orders_registry.register("orders", "Total orders received", total_orders.clone());
total_orders.inc();

let processing_times = Histogram::new(exponential_buckets(1.0, 2.0, 10));
orders_registry.register_with_unit(
"processing_times",
"Order times",
Unit::Seconds,
processing_times.clone(),
);
processing_times.observe(2.4);

let mut user_auth_registry = Registry::default();

let successful_logins: Counter<u64> = Default::default();
user_auth_registry.register(
"successful_logins",
"Total successful logins",
successful_logins.clone(),
);
successful_logins.inc();

let failed_logins: Counter<u64> = Default::default();
user_auth_registry.register(
"failed_logins",
"Total failed logins",
failed_logins.clone(),
);

let mut response = String::new();

encode_registry(&mut response, &orders_registry).unwrap();
assert_eq!(&response[response.len() - 20..], "bucket{le=\"+Inf\"} 1\n");

encode_registry(&mut response, &user_auth_registry).unwrap();
assert_eq!(&response[response.len() - 20..], "iled_logins_total 0\n");

encode_eof(&mut response).unwrap();
assert_eq!(&response[response.len() - 20..], "ogins_total 0\n# EOF\n");
}

fn parse_with_python_client(input: String) {
pyo3::prepare_freethreaded_python();

Expand Down
12 changes: 6 additions & 6 deletions src/metrics/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::encoding::{EncodeMetric, MetricEncoder};

use super::{MetricType, TypedMetric};
use std::marker::PhantomData;
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::AtomicU64;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
Expand Down Expand Up @@ -40,15 +40,15 @@ use std::sync::Arc;
/// counter.inc();
/// let _value: f64 = counter.get();
/// ```
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
#[cfg(target_has_atomic = "64")]
#[derive(Debug)]
pub struct Counter<N = u64, A = AtomicU64> {
value: Arc<A>,
phantom: PhantomData<N>,
}

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

#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
#[cfg(target_has_atomic = "64")]
impl Atomic<u64> for AtomicU64 {
fn inc(&self) -> u64 {
self.inc_by(1)
Expand Down Expand Up @@ -143,7 +143,7 @@ impl Atomic<u32> for AtomicU32 {
}
}

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

#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
#[cfg(target_has_atomic = "64")]
#[test]
fn f64_stored_in_atomic_u64() {
fn prop(fs: Vec<f64>) {
Expand Down
8 changes: 4 additions & 4 deletions src/metrics/exemplar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use super::histogram::Histogram;
use super::{MetricType, TypedMetric};
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use std::collections::HashMap;
#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
#[cfg(not(target_has_atomic = "64"))]
use std::sync::atomic::AtomicU32;
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::AtomicU64;
use std::sync::Arc;

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

/// Open Metrics [`Counter`] with an [`Exemplar`] to both measure discrete
/// events and track references to data outside of the metric set.
#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
#[cfg(not(target_has_atomic = "64"))]
#[derive(Debug)]
pub struct CounterWithExemplar<S, N = u32, A = AtomicU32> {
pub(crate) inner: Arc<RwLock<CounterWithExemplarInner<S, N, A>>>,
Expand Down
10 changes: 5 additions & 5 deletions src/metrics/gauge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::encoding::{EncodeGaugeValue, EncodeMetric, MetricEncoder};
use super::{MetricType, TypedMetric};
use std::marker::PhantomData;
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
#[cfg(not(any(target_arch = "mips", target_arch = "powerpc")))]
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::{AtomicI64, AtomicU64};
use std::sync::Arc;

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

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

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

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

0 comments on commit baefdcd

Please sign in to comment.