Skip to content

Commit

Permalink
feat: metrics NG (#22)
Browse files Browse the repository at this point in the history
xDarksome authored Jul 8, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 3d85923 commit af0464e
Showing 24 changed files with 2,667 additions and 893 deletions.
803 changes: 657 additions & 146 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 3 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -30,8 +30,7 @@ collections = ["dep:collections"]
future = ["dep:future"]
geoblock = ["geoip/middleware"]
geoip = ["dep:geoip"]
metrics = ["dep:metrics", "future/metrics", "alloc/metrics"]
future_metrics = ["dep:future_metrics"]
metrics = ["dep:metrics", "alloc/metrics"]
alloc_metrics = ["alloc/metrics"]
profiler = ["alloc/profiler"]
rate_limit = ["dep:rate_limit"]
@@ -45,8 +44,7 @@ analytics = { path = "./crates/analytics", optional = true }
collections = { path = "./crates/collections", optional = true }
future = { path = "./crates/future", optional = true }
geoip = { path = "./crates/geoip", optional = true }
metrics = { path = "./crates/metrics", optional = true }
future_metrics = { path = "./crates/future_metrics", optional = true }
metrics = { package = "wc_metrics", path = "./crates/metrics", optional = true }
rate_limit = { path = "./crates/rate_limit", optional = true }

[dev-dependencies]
@@ -56,6 +54,7 @@ tokio = { version = "1", features = ["full"] }
hyper = { version = "1.2.0", features = ["full"] }
tower = { version = "0.4", features = ["util", "filter"] }
axum = "0.7.5"
metrics-exporter-prometheus = { version = "0.15", default-features = false }

[[example]]
name = "alloc_profiler"
@@ -65,10 +64,6 @@ required-features = ["alloc", "profiler"]
name = "alloc_stats"
required-features = ["alloc", "metrics"]

[[example]]
name = "metrics"
required-features = ["metrics", "future"]

[[example]]
name = "geoblock"
required-features = ["geoblock"]
2 changes: 1 addition & 1 deletion crates/alloc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ profiler = ["dep:dhat", "dep:tokio"]
metrics = ["dep:metrics"]

[dependencies]
metrics = { version = "0.23", optional = true }
metrics = { package = "wc_metrics", path = "../metrics", optional = true }
tikv-jemallocator = { version = "0.5", features = ["stats"] }
tikv-jemalloc-ctl = { version = "0.5", features = ["use_std"] }
serde = { version = "1", features = ["derive"] }
2 changes: 1 addition & 1 deletion crates/alloc/src/stats.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ pub fn collect_jemalloc_stats() -> Result<JemallocStats, Error> {

#[cfg(feature = "metrics")]
pub fn update_jemalloc_metrics() -> Result<(), Error> {
use metrics::gauge;
use metrics::backend::gauge;

let stats = collect_jemalloc_stats()?;
let total = &stats.total;
3 changes: 0 additions & 3 deletions crates/future/Cargo.toml
Original file line number Diff line number Diff line change
@@ -5,11 +5,8 @@ edition = "2021"

[features]
default = []
full = ["metrics"]
metrics = ["dep:metrics"]

[dependencies]
metrics = { path = "../metrics", optional = true }
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "time", "macros"] }
tokio-util = { version = "0.7", default-features = false }
pin-project = "1"
67 changes: 8 additions & 59 deletions crates/future/src/lib.rs
Original file line number Diff line number Diff line change
@@ -202,34 +202,6 @@ pub trait FutureExt {
self,
token: CancellationToken,
) -> CancellationFuture<Self::Future, Ready<()>>;

/// Consumes the future, returning a new future that records the metrics of
/// the inner future's async task execution.
///
/// # Example
///
/// ```rust
/// use {future::FutureExt, metrics::OtelTaskMetricsRecorder, std::time::Duration};
///
/// # async fn example() {
/// let recorder = OtelTaskMetricsRecorder::new("custom_task").with_name("specific_task_name");
///
/// async {
/// tokio::time::sleep(Duration::from_millis(500)).await;
/// }
/// .with_metrics(recorder)
/// .await
/// # }
///
/// # #[tokio::main]
/// # async fn main() {
/// # example().await;
/// # }
/// ```
#[cfg(feature = "metrics")]
fn with_metrics<R>(self, recorder: R) -> metrics::TaskMetricsFuture<Self::Future, R>
where
R: metrics::TaskMetricsRecorder;
}

pub trait StaticFutureExt {
@@ -248,7 +220,7 @@ pub trait StaticFutureExt {
/// tokio::time::sleep(Duration::from_millis(500)).await;
/// 42
/// }
/// .spawn("");
/// .spawn();
///
/// assert!(matches!(join_handle.await, Ok(42)));
/// # }
@@ -258,15 +230,7 @@ pub trait StaticFutureExt {
/// # example().await;
/// # }
/// ```
#[cfg(feature = "metrics")]
fn spawn(self, name: &'static str) -> JoinHandle<<Self::Future as Future>::Output>;

/// Same as [`StaticFutureExt::spawn`], but it won't monitor long running
/// futures.
///
/// Use this only if your future is expected to be long running (ex.
/// singleton).
fn spawn_and_forget(self) -> JoinHandle<<Self::Future as Future>::Output>;
fn spawn(self) -> JoinHandle<<Self::Future as Future>::Output>;
}

impl<T> FutureExt for T
@@ -292,14 +256,6 @@ where
on_cancel: ready(()),
}
}

#[cfg(feature = "metrics")]
fn with_metrics<R>(self, recorder: R) -> metrics::TaskMetricsFuture<Self::Future, R>
where
R: metrics::TaskMetricsRecorder,
{
metrics::TaskMetricsFuture::new(self, recorder)
}
}

impl<T> StaticFutureExt for T
@@ -309,19 +265,12 @@ where
{
type Future = T;

#[cfg(feature = "metrics")]
fn spawn(self, name: &'static str) -> JoinHandle<<Self::Future as Future>::Output> {
static METRICS: metrics::TaskMetrics = metrics::TaskMetrics::new("spawned_task");

tokio::spawn(self.with_metrics(METRICS.with_name(name)))
}

fn spawn_and_forget(self) -> JoinHandle<<Self::Future as Future>::Output> {
fn spawn(self) -> JoinHandle<<Self::Future as Future>::Output> {
tokio::spawn(self)
}
}

#[cfg(all(test, feature = "metrics"))]
#[cfg(test)]
mod test {
use {
super::*,
@@ -356,7 +305,7 @@ mod test {
tokio::time::sleep(Duration::from_millis(100)).await;
b.fetch_add(1, Ordering::SeqCst);
})
.spawn("")
.spawn()
};

tokio::time::sleep(Duration::from_millis(200)).await;
@@ -385,7 +334,7 @@ mod test {
tokio::time::sleep(Duration::from_millis(100)).await;
b.fetch_add(1, Ordering::Relaxed);
})
.spawn("")
.spawn()
};

tokio::time::sleep(Duration::from_millis(200)).await;
@@ -416,7 +365,7 @@ mod test {
tokio::time::sleep(Duration::from_millis(100)).await;
b.fetch_add(1, Ordering::Relaxed);
})
.spawn("")
.spawn()
};

assert_eq!(handle.await.unwrap(), Err(Error::Timeout));
@@ -441,7 +390,7 @@ mod test {
tokio::time::sleep(Duration::from_millis(100)).await;
b.fetch_add(1, Ordering::Relaxed);
})
.spawn("")
.spawn()
};

assert_eq!(handle.await.unwrap(), Ok(42));
8 changes: 0 additions & 8 deletions crates/future_metrics/Cargo.toml

This file was deleted.

215 changes: 0 additions & 215 deletions crates/future_metrics/src/lib.rs

This file was deleted.

28 changes: 20 additions & 8 deletions crates/metrics/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
[package]
name = "metrics"
name = "wc_metrics"
version = "0.1.0"
edition = "2021"

[features]
default = ["future"]
future = ["dep:pin-project"]

[dependencies]
pin-project = "1"
prometheus = "0.13"
opentelemetry = { version = "0.22.0", features = ["metrics"] }
opentelemetry_sdk = { version = "0.22.1", features = ["metrics", "rt-tokio"] }
opentelemetry-prometheus = "0.15"
once_cell = "1.17"
smallvec = "1.11"
metrics = "0.23"
smallvec = "1"
parking_lot = "0.12"
enum-ordinalize = "4.3"
arc-swap = "1.7"
pin-project = { version = "1", optional = true }
futures = "0.3"

[dev-dependencies]
wc_metrics = { path = "./" }
metrics-exporter-prometheus = "0.15"
prometheus-parse = "0.2"
tikv-jemalloc-ctl = { version = "0.5", features = ["use_std"] }
tikv-jemallocator = "0.5"
smol = "2"
104 changes: 104 additions & 0 deletions crates/metrics/src/examples/macros_counter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use wc_metrics::{
counter,
enum_ordinalize::Ordinalize,
BoolLabel,
EnumLabel,
OptionalBoolLabel,
OptionalEnumLabel,
OptionalStringLabel,
StringLabel,
};

#[derive(Clone, Copy, Debug, Ordinalize)]
enum MyEnum {
A,
B,
}

impl wc_metrics::Enum for MyEnum {
fn as_str(&self) -> &'static str {
match self {
Self::A => "a",
Self::B => "b",
}
}
}

pub fn counters(v: u64) {
let s = "a";
let b = true;
let u = 42;
let e = MyEnum::A;

counter!("counter1").increment(v);

counter!("counter2", EnumLabel<"e", MyEnum> => e).increment(v);

counter!("counter3", BoolLabel<"b"> => b).increment(v);

counter!("counter4", StringLabel<"s"> => s).increment(v);

counter!("counter5", StringLabel<"s", u8> => &u).increment(v);

counter!("counter6",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b
)
.increment(v);

counter!("counter7", "st" => "1").increment(v);

counter!("counter8", "st1" => "1", "st2" => "2").increment(v);

counter!("counter9", StringLabel<"s", u8> => &u, "st" => "2").increment(v);

counter!("counter10",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b,
"st1" => "1",
"st2" => "2"
)
.increment(v);

counter!("counter11", "description11").increment(v);

counter!("counter12", "description12", EnumLabel<"e", MyEnum> => e).increment(v);

counter!("counter13", "description13", BoolLabel<"b"> => b).increment(v);

counter!("counter14", "description14", StringLabel<"s"> => s).increment(v);

counter!("counter15", "description15", StringLabel<"s", u8> => &u).increment(v);

counter!("counter16", "description16",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b
)
.increment(v);

counter!("counter17", "description17", "st" => "1").increment(v);

counter!("counter18", "description18", "st1" => "1", "st2" => "2").increment(v);

counter!("counter19", "description19", StringLabel<"s", u8> => &u, "st" => "2").increment(v);

counter!("counter20", "description20",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b,
OptionalEnumLabel<"oe", MyEnum> => Some(e),
OptionalStringLabel<"os1"> => Some(s),
OptionalStringLabel<"os2", u8> => Some(&u),
OptionalBoolLabel<"ob"> => Some(b),
"st1" => "1",
"st2" => "2"
)
.increment(v);
}
89 changes: 89 additions & 0 deletions crates/metrics/src/examples/macros_future_metrics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use wc_metrics::{
enum_ordinalize::Ordinalize,
future_metrics,
BoolLabel,
EnumLabel,
FutureExt,
OptionalBoolLabel,
OptionalEnumLabel,
OptionalStringLabel,
StringLabel,
};

#[derive(Clone, Copy, Debug, Ordinalize)]
enum MyEnum {
A,
B,
}

impl wc_metrics::Enum for MyEnum {
fn as_str(&self) -> &'static str {
match self {
Self::A => "a",
Self::B => "b",
}
}
}

pub async fn future_metrics() {
let s = "a";
let b = true;
let u = 42;
let e = MyEnum::A;

async {}
.with_metrics(future_metrics!("future_metrics1"))
.await;

async {}
.with_metrics(future_metrics!("future_metrics2", EnumLabel<"e", MyEnum> => e))
.await;

async {}
.with_metrics(future_metrics!("future_metrics3", BoolLabel<"b"> => b))
.await;

async {}
.with_metrics(future_metrics!("future_metrics4", StringLabel<"s"> => s))
.await;

async {}
.with_metrics(future_metrics!("future_metrics5", StringLabel<"s", u8> => &u))
.await;

async {}
.with_metrics(future_metrics!("future_metrics6",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b
))
.await;

async {}
.with_metrics(future_metrics!("future_metrics7", "st" => "1"))
.await;

async {}
.with_metrics(future_metrics!("future_metrics8", "st1" => "1", "st2" => "2"))
.await;

async {}
.with_metrics(future_metrics!("future_metrics9", StringLabel<"s", u8> => &u, "st" => "2"))
.await;

async {}
.with_metrics(future_metrics!("future_metrics10",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b,
OptionalEnumLabel<"oe", MyEnum> => Some(e),
OptionalStringLabel<"os1"> => Some(s),
OptionalStringLabel<"os2", u8> => Some(&u),
OptionalBoolLabel<"ob"> => Some(b),
"st1" => "1",
"st2" => "2"
))
.await;
}
104 changes: 104 additions & 0 deletions crates/metrics/src/examples/macros_gauge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use wc_metrics::{
enum_ordinalize::Ordinalize,
gauge,
BoolLabel,
EnumLabel,
OptionalBoolLabel,
OptionalEnumLabel,
OptionalStringLabel,
StringLabel,
};

#[derive(Clone, Copy, Debug, Ordinalize)]
enum MyEnum {
A,
B,
}

impl wc_metrics::Enum for MyEnum {
fn as_str(&self) -> &'static str {
match self {
Self::A => "a",
Self::B => "b",
}
}
}

pub fn gauges(v: f64) {
let s = "a";
let b = true;
let u = 42;
let e = MyEnum::A;

gauge!("gauge1").set(v);

gauge!("gauge2", EnumLabel<"e", MyEnum> => e).set(v);

gauge!("gauge3", BoolLabel<"b"> => b).set(v);

gauge!("gauge4", StringLabel<"s"> => s).set(v);

gauge!("gauge5", StringLabel<"s", u8> => &u).set(v);

gauge!("gauge6",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b
)
.set(v);

gauge!("gauge7", "st" => "1").set(v);

gauge!("gauge8", "st1" => "1", "st2" => "2").set(v);

gauge!("gauge9", StringLabel<"s", u8> => &u, "st" => "2").set(v);

gauge!("gauge10",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b,
"st1" => "1",
"st2" => "2"
)
.set(v);

gauge!("gauge11", "description11").set(v);

gauge!("gauge12", "description12", EnumLabel<"e", MyEnum> => e).set(v);

gauge!("gauge13", "description13", BoolLabel<"b"> => b).set(v);

gauge!("gauge14", "description14", StringLabel<"s"> => s).set(v);

gauge!("gauge15", "description15", StringLabel<"s", u8> => &u).set(v);

gauge!("gauge16", "description16",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b
)
.set(v);

gauge!("gauge17", "description17", "st" => "1").set(v);

gauge!("gauge18", "description18", "st1" => "1", "st2" => "2").set(v);

gauge!("gauge19", "description19", StringLabel<"s", u8> => &u, "st" => "2").set(v);

gauge!("gauge20", "description20",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b,
OptionalEnumLabel<"oe", MyEnum> => Some(e),
OptionalStringLabel<"os1"> => Some(s),
OptionalStringLabel<"os2", u8> => Some(&u),
OptionalBoolLabel<"ob"> => Some(b),
"st1" => "1",
"st2" => "2"
)
.set(v);
}
104 changes: 104 additions & 0 deletions crates/metrics/src/examples/macros_histogram.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use wc_metrics::{
enum_ordinalize::Ordinalize,
histogram,
BoolLabel,
EnumLabel,
OptionalBoolLabel,
OptionalEnumLabel,
OptionalStringLabel,
StringLabel,
};

#[derive(Clone, Copy, Debug, Ordinalize)]
enum MyEnum {
A,
B,
}

impl wc_metrics::Enum for MyEnum {
fn as_str(&self) -> &'static str {
match self {
Self::A => "a",
Self::B => "b",
}
}
}

pub fn histograms(v: f64) {
let s = "a";
let b = true;
let u = 42;
let e = MyEnum::A;

histogram!("histogram1").record(v);

histogram!("histogram2", EnumLabel<"e", MyEnum> => e).record(v);

histogram!("histogram3", BoolLabel<"b"> => b).record(v);

histogram!("histogram4", StringLabel<"s"> => s).record(v);

histogram!("histogram5", StringLabel<"s", u8> => &u).record(v);

histogram!("histogram6",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b
)
.record(v);

histogram!("histogram7", "st" => "1").record(v);

histogram!("histogram8", "st1" => "1", "st2" => "2").record(v);

histogram!("histogram9", StringLabel<"s", u8> => &u, "st" => "2").record(v);

histogram!("histogram10",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b,
"st1" => "1",
"st2" => "2"
)
.record(v);

histogram!("histogram11", "description11").record(v);

histogram!("histogram12", "description12", EnumLabel<"e", MyEnum> => e).record(v);

histogram!("histogram13", "description13", BoolLabel<"b"> => b).record(v);

histogram!("histogram14", "description14", StringLabel<"s"> => s).record(v);

histogram!("histogram15", "description15", StringLabel<"s", u8> => &u).record(v);

histogram!("histogram16", "description16",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b
)
.record(v);

histogram!("histogram17", "description17", "st" => "1").record(v);

histogram!("histogram18", "description18", "st1" => "1", "st2" => "2").record(v);

histogram!("histogram19", "description19", StringLabel<"s", u8> => &u, "st" => "2").record(v);

histogram!("histogram20", "description20",
EnumLabel<"e", MyEnum> => e,
StringLabel<"s1"> => s,
StringLabel<"s2", u8> => &u,
BoolLabel<"b"> => b,
OptionalEnumLabel<"oe", MyEnum> => Some(e),
OptionalStringLabel<"os1"> => Some(s),
OptionalStringLabel<"os2", u8> => Some(&u),
OptionalBoolLabel<"ob"> => Some(b),
"st1" => "1",
"st2" => "2"
)
.record(v);
}
4 changes: 4 additions & 0 deletions crates/metrics/src/examples/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod macros_counter;
pub mod macros_future_metrics;
pub mod macros_gauge;
pub mod macros_histogram;
260 changes: 169 additions & 91 deletions crates/metrics/src/future.rs
Original file line number Diff line number Diff line change
@@ -1,129 +1,207 @@
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
time::{Duration, Instant},
//! Instrumentation machinery for collecting [`Future`] metrics.
//!
//! Usage:
//!
//! ```
//! use wc_metrics::{
//! self as metrics,
//! label_name,
//! BoolLabel,
//! FutureExt,
//! FutureMetrics,
//! LabeledFutureMetrics2,
//! Lazy,
//! };
//!
//! type MyBoolLabelA = BoolLabel<{ label_name("my_bool_label_a") }>;
//! type MyBoolLabelB = BoolLabel<{ label_name("my_bool_label_b") }>;
//!
//! static FUTURE_METRICS_A: Lazy<FutureMetrics> = metrics::new("my_future_a");
//! static FUTURE_METRICS_B: Lazy<LabeledFutureMetrics2<MyBoolLabelA, MyBoolLabelB>> =
//! metrics::builder("my_future_b")
//! .with_static_labels(&[("labelA", "valueA"), ("labelA", "valueA")])
//! .build();
//!
//! let fut_a = async {}.with_metrics(&FUTURE_METRICS_A);
//! let fut_b = async {}.with_metrics(
//! FUTURE_METRICS_B.resolve_labels((MyBoolLabelA::new(false), MyBoolLabelB::new(true))),
//! );
//! ```
use {
crate::{
sealed::{Attrs, Metric},
Lazy,
},
futures::future::FusedFuture,
metrics::{counter, gauge, histogram, Counter, Gauge, Histogram, Label},
std::{
future::Future,
pin::Pin,
task::{Context, Poll},
time::{Duration, Instant},
},
};

/// Trait for tracking task execution related metrics with
/// [`TaskMetricsFuture`].
///
/// Most of the time [`OtelTaskMetricsRecorder`] should be used instead of
/// manual implementations of this trait, unless we want to support multiple
/// metrics tracking APIs.
pub trait TaskMetricsRecorder: Send + Sync + 'static {
fn record_task_started(&self) {}

fn record_task_finished(
&self,
_total_duration: Duration,
_poll_duration: Duration,
_poll_entries: u64,
_completed: bool,
) {
}
}
/// Metric names used by this module.
pub mod name {
pub const FUTURE_DURATION: &str = "future_duration";
pub const FUTURE_CANCELLED_DURATION: &str = "future_cancelled_duration";

/// Trait that implements task name tagging using a static string.
pub trait AsTaskName: Send + Sync + 'static {
fn as_task_name(&self) -> &'static str;
}
pub const FUTURES_CREATED: &str = "futures_created_count";
pub const FUTURES_STARTED: &str = "futures_started_count";
pub const FUTURES_FINISHED: &str = "futures_finished_count";
pub const FUTURES_CANCELLED: &str = "futures_cancelled_count";

impl AsTaskName for () {
fn as_task_name(&self) -> &'static str {
""
}
pub const FUTURE_POLL_DURATION: &str = "future_poll_duration";
pub const FUTURE_POLL_DURATION_MAX: &str = "future_poll_duration_max";
pub const FUTURE_POLLS: &str = "future_polls_count";
}

impl AsTaskName for &'static str {
fn as_task_name(&self) -> &'static str {
self
}
}
/// Metrics collected during a [`Future`] execution.
pub struct Metrics {
duration: Histogram,
cancelled_duration: Histogram,

created: Counter,
started: Counter,
finished: Counter,
cancelled: Counter,

struct Stats<R: TaskMetricsRecorder> {
started: Instant,
completed: bool,
poll_duration: Duration,
poll_entries: u64,
recorder: R,
poll_duration: Histogram,
poll_duration_max: Gauge,
polls: Counter,
}

impl<R> Stats<R>
where
R: TaskMetricsRecorder,
{
fn new(recorder: R) -> Self {
recorder.record_task_started();
impl Metric for Metrics {
fn register(attrs: &Attrs) -> Self {
let mut labels = attrs.labels();
let name = Label::from_static_parts("future_name", attrs.name());
labels.push(name);

Self {
started: Instant::now(),
completed: false,
poll_duration: Duration::from_secs(0),
poll_entries: 0,
recorder,
duration: histogram!(name::FUTURE_DURATION, labels.iter()),
cancelled_duration: histogram!(name::FUTURE_CANCELLED_DURATION, labels.iter()),
created: counter!(name::FUTURES_CREATED, labels.iter()),
started: counter!(name::FUTURES_STARTED, labels.iter()),
finished: counter!(name::FUTURES_FINISHED, labels.iter()),
cancelled: counter!(name::FUTURES_CANCELLED, labels.iter()),
poll_duration: histogram!(name::FUTURE_POLL_DURATION, labels.iter()),
poll_duration_max: gauge!(name::FUTURE_POLL_DURATION_MAX, labels.iter()),
polls: counter!(name::FUTURE_POLLS, labels.iter()),
}
}
}

impl<R> Drop for Stats<R>
where
R: TaskMetricsRecorder,
{
fn drop(&mut self) {
self.recorder.record_task_finished(
self.started.elapsed(),
self.poll_duration,
self.poll_entries,
self.completed,
);
/// Convienience extension `trait` for creating [`Metered`] [`Future`]s.
pub trait FutureExt: Sized {
/// Consumes the future, returning a new future that records the executiion
/// metrics of the inner future.
fn with_metrics(self, metrics: impl Into<&'static Metrics>) -> Metered<Self> {
Metered::new(self, metrics)
}
}

impl<F> FutureExt for F where F: Future {}

/// [`Future`] wrapper collecting [`Metrics`] of inner [`Future`] `F`.
#[pin_project::pin_project]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct TaskMetricsFuture<F, R>
where
F: Future,
R: TaskMetricsRecorder,
{
pub struct Metered<F> {
#[pin]
inner: F,
stats: Stats<R>,
future: F,
state: State,
}

impl<F, R> TaskMetricsFuture<F, R>
where
F: Future,
R: TaskMetricsRecorder,
{
pub fn new(inner: F, recorder: R) -> Self {
struct State {
started_at: Option<Instant>,
is_finished: bool,

poll_duration_sum: Duration,
poll_duration_max: Duration,
polls_count: usize,

metrics: &'static Metrics,
}

impl<F> Metered<F> {
fn new(future: F, metrics: impl Into<&'static Metrics>) -> Self {
let metrics = metrics.into();

metrics.created.increment(1);

Self {
inner,
stats: Stats::new(recorder),
future,
state: State {
started_at: None,
is_finished: false,
poll_duration_sum: Duration::from_secs(0),
poll_duration_max: Duration::from_secs(0),
polls_count: 0,
metrics,
},
}
}
}

impl<F, R> Future for TaskMetricsFuture<F, R>
where
F: Future,
R: TaskMetricsRecorder,
{
impl From<&'static Lazy<Metrics>> for &'static Metrics {
fn from(lazy: &'static Lazy<Metrics>) -> Self {
lazy.get_or_register()
}
}

impl<F: Future> Future for Metered<F> {
type Output = F::Output;

fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let poll_start = Instant::now();
let this = self.project();
let result = this.inner.poll(cx);
let mut this = self.project();
let state = &mut this.state;

if result.is_ready() {
this.stats.completed = true;
if state.started_at.is_none() {
state.started_at = Some(Instant::now());
state.metrics.started.increment(1);
}

this.stats.poll_entries += 1;
this.stats.poll_duration += poll_start.elapsed();
let poll_started_at = Instant::now();
let result = this.future.poll(cx);
let poll_duration = poll_started_at.elapsed();

state.poll_duration_sum += poll_duration;
state.poll_duration_max = state.poll_duration_max.max(poll_duration);
state.polls_count += 1;

if result.is_ready() {
state.is_finished = true;

state.metrics.finished.increment(1);

if let Some(started_at) = state.started_at {
state.metrics.duration.record(started_at.elapsed())
}
}

result
}
}

impl Drop for State {
fn drop(&mut self) {
if !self.is_finished {
self.metrics.cancelled.increment(1);

if let Some(started_at) = self.started_at {
self.metrics.cancelled_duration.record(started_at.elapsed())
}
}

self.metrics.poll_duration.record(self.poll_duration_sum);
self.metrics.poll_duration_max.set(self.poll_duration_max);
self.metrics.polls.increment(self.polls_count as u64);
}
}

impl<F: Future> FusedFuture for Metered<F> {
fn is_terminated(&self) -> bool {
self.state.is_finished
}
}
545 changes: 545 additions & 0 deletions crates/metrics/src/label.rs

Large diffs are not rendered by default.

134 changes: 134 additions & 0 deletions crates/metrics/src/lazy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use {
crate::{
label::{DynamicLabel, ResolveLabels, WithLabel},
sealed::{Decrement, Execute, Increment, Record, Set},
Attrs,
Metric,
StaticAttrs,
},
metrics::{Counter, Gauge, Histogram, IntoF64},
std::sync::OnceLock,
};

/// Lazily initialized metric.
///
/// Can only be used if assigned to a `static` variable.
///
/// Use [`Builder`](crate::Builder) to specify metric attributes known at the
/// complile time and to build [`Lazy`] metrics.
pub struct Lazy<M> {
metric: OnceLock<M>,
attrs: StaticAttrs,
}

impl<M: Metric> Lazy<M> {
pub(super) const fn new(attrs: StaticAttrs) -> Self {
Self {
metric: OnceLock::new(),
attrs,
}
}

pub(crate) fn get_or_register(&self) -> &M {
if let Some(m) = self.metric.get() {
return m;
};

let attrs = Attrs {
static_: self.attrs,
dynamic: Default::default(),
};

self.metric.get_or_init(|| M::register(&attrs))
}
}

impl Lazy<Counter> {
/// See [`Counter::increment`].
pub fn increment(&'static self, value: u64) {
self.get_or_register().increment(value)
}
}

impl Lazy<Gauge> {
/// See [`Gauge::increment`].
pub fn increment<T: IntoF64>(&'static self, value: T) {
self.get_or_register().increment(value)
}

/// See [`Gauge::decrement`].
pub fn decrement<T: IntoF64>(&'static self, value: T) {
self.get_or_register().decrement(value)
}

/// See [`Gauge::set`].
pub fn set<T: IntoF64>(&'static self, value: T) {
self.get_or_register().set(value)
}
}

impl Lazy<Histogram> {
/// See [`Histogram::record`].
pub fn record<T: IntoF64>(&'static self, value: T) {
self.get_or_register().record(value)
}
}

impl<L, M> Lazy<WithLabel<L, M>>
where
L: DynamicLabel<M>,
{
/// See [`WithLabel::resolve_label`].
pub fn resolve_label<T>(&'static self, label: T) -> &M
where
WithLabel<L, M>: Metric + ResolveLabels<(T,), Target = M>,
{
self.get_or_register().resolve_label(label)
}

/// See [`WithLabel::resolve_labels`].
pub fn resolve_labels<LS>(
&'static self,
labels: LS,
) -> &<WithLabel<L, M> as ResolveLabels<LS>>::Target
where
WithLabel<L, M>: Metric + ResolveLabels<LS>,
{
self.get_or_register().resolve_labels(labels)
}

/// Calls [`Counter::increment`] or [`Gauge::increment`] on the metric built
/// using the provided labels.
pub fn increment<T, Labels>(&'static self, value: T, labels: Labels)
where
WithLabel<L, M>: Metric + Execute<Increment<T>, Labels>,
{
self.get_or_register().execute(Increment(value), labels);
}

/// Calls [`Gauge::decrement`] on the metric built using the provided
/// labels.
pub fn decrement<T, Labels>(&'static self, value: T, labels: Labels)
where
WithLabel<L, M>: Metric + Execute<Decrement<T>, Labels>,
{
self.get_or_register().execute(Decrement(value), labels);
}

/// Calls [`Gauge::set`] on the metric built using the provided labels.
pub fn set<T, Labels>(&'static self, value: T, labels: Labels)
where
WithLabel<L, M>: Metric + Execute<Set<T>, Labels>,
{
self.get_or_register().execute(Set(value), labels);
}

/// Calls [`Histogram::record`] on the metric built using the provided
/// labels.
pub fn record<T, Labels>(&'static self, value: T, labels: Labels)
where
WithLabel<L, M>: Metric + Execute<Record<T>, Labels>,
{
self.get_or_register().execute(Record(value), labels);
}
}
402 changes: 288 additions & 114 deletions crates/metrics/src/lib.rs

Large diffs are not rendered by default.

206 changes: 158 additions & 48 deletions crates/metrics/src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,64 +1,174 @@
/// Define a local static
/// [`ObservableGauge`](opentelemetry::metrics::ObservableGauge) and return a
/// reference to it, or immediately observe a value.
/// Similar to [`metrics::counter`](crate::backend::counter), but expects
/// dynamic labels to be type-annotated and provides an option to specify
/// metric descriptions.
///
/// Uses the machinery of this crate to create appropriately-typed `static`
/// metric and to resolve dynamic labels.
///
/// Using this macro with the same arguments multilpe times is not recommended
/// as each time it creates a separate `static` variable.
/// If your metric needs to be modified from multiple places either store it
/// inside your own types, or use the vanilla machinery of this crate to define
/// your own `static` metric and use it instead.
///
/// Usage:
/// ```
#[doc = include_str!("examples/macros_counter.rs")]
/// ```
#[macro_export]
macro_rules! gauge {
($name:expr) => {{
static METRIC: $crate::Lazy<$crate::otel::metrics::ObservableGauge<u64>> =
$crate::Lazy::new(|| {
$crate::ServiceMetrics::meter()
.u64_observable_gauge($name)
.init()
});
macro_rules! counter {
($($tail:tt)*) => {
$crate::metric!($crate::backend::Counter, $($tail)*)
};
}

&METRIC
}};
/// Similar to [`metrics::gauge`](crate::backend::gauge), but expects
/// dynamic labels to be type-annotated and provides an option to specify
/// metric descriptions.
///
/// Uses the machinery of this crate to create appropriately-typed `static`
/// metric and to resolve dynamic labels.
///
/// Using this macro with the same arguments multilpe times is not recommended
/// as each time it creates a separate `static` variable.
/// If your metric needs to be modified from multiple places either store it
/// inside your own types, or use the vanilla machinery of this crate to define
/// your own `static` metric and use it instead.
///
/// Usage:
/// ```
#[doc = include_str!("examples/macros_gauge.rs")]
/// ```
#[macro_export]
macro_rules! gauge {
($($tail:tt)*) => {
$crate::metric!($crate::backend::Gauge, $($tail)*)
};
}

($name:expr, $value:expr) => {{
$crate::gauge!($name, $value, &[]);
}};
/// Similar to [`metrics::histogram`](crate::backend::histogram), but expects
/// dynamic labels to be type-annotated and provides an option to specify
/// metric descriptions.
///
/// Uses the machinery of this crate to create appropriately-typed `static`
/// metric and to resolve dynamic labels.
///
/// Using this macro with the same arguments multilpe times is not recommended
/// as each time it creates a separate `static` variable.
/// If your metric needs to be modified from multiple places either store it
/// inside your own types, or use the vanilla machinery of this crate to define
/// your own `static` metric and use it instead.
///
/// Usage:
/// ```
#[doc = include_str!("examples/macros_histogram.rs")]
/// ```
#[macro_export]
macro_rules! histogram {
($($tail:tt)*) => {
$crate::metric!($crate::backend::Histogram, $($tail)*)
};
}

($name:expr, $value:expr, $tags:expr) => {{
$crate::gauge!($name).observe($value as u64, $tags);
}};
/// Similar to [`counter`], [`gauge`] and [`histogram`], but operates with
/// [`FutureMetrics`](crate::FutureMetrics) instead.
///
/// Usage:
/// ```
#[doc = include_str!("examples/macros_future_metrics.rs")]
/// ```
#[cfg(feature = "future")]
#[macro_export]
macro_rules! future_metrics {
($($tail:tt)*) => {
$crate::metric!($crate::FutureMetrics, $($tail)*)
};
}

/// Define a local static [`Histogram`](opentelemetry::metrics::Histogram) and
/// return a reference to it, or immediately record a value.
#[doc(hidden)]
#[macro_export]
macro_rules! histogram {
($name:expr) => {{
static METRIC: $crate::Lazy<$crate::otel::metrics::Histogram<f64>> =
$crate::Lazy::new(|| $crate::ServiceMetrics::meter().f64_histogram($name).init());
macro_rules! metric {
( $type:ty, $name:literal) => {
{
static METRIC: $crate::Lazy<$type> = $crate::new($name);
&METRIC
}
};

( $type:ty, $name:literal, $description:literal) => {
{
static METRIC: $crate::Lazy<$type> = $crate::builder($name)
.with_description($description)
.build();
&METRIC
}
};

&METRIC
}};
( $type:ty, $name:literal, $description:literal, $($tail:tt)*) => {
{
static METRIC: $crate::Lazy<$crate::metric_type!($type, $($tail)*)> = $crate::builder($name)
.with_description($description)
.with_static_labels($crate::static_labels!($($tail)*))
.build();

($name:expr, $value:expr) => {{
$crate::histogram!($name, $value, &[]);
}};
let m = &METRIC;
$crate::resolve_labels!(m, $($tail)*);
m
}
};
( $type:ty, $name:literal, $($tail:tt)*) => {
{
static METRIC: $crate::Lazy<$crate::metric_type!($type, $($tail)*)> = $crate::builder($name)
.with_static_labels($crate::static_labels!($($tail)*))
.build();

($name:expr, $value:expr, $tags:expr) => {{
$crate::histogram!($name).record($value as f64, $tags);
}};
let m = &METRIC;
$crate::resolve_labels!(m, $($tail)*);
m
}
};
}

/// Define a local static [`Counter`](opentelemetry::metrics::Counter) and
/// return a reference to it, or immediately add a value.
#[doc(hidden)]
#[macro_export]
macro_rules! counter {
($name:expr) => {{
static METRIC: $crate::Lazy<$crate::otel::metrics::Counter<u64>> =
$crate::Lazy::new(|| $crate::ServiceMetrics::meter().u64_counter($name).init());

&METRIC
}};
macro_rules! metric_type {
( $type:ty, $( $_:literal => $__:literal ),+ )=> {
$type
};
( $type:ty, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $( $_:literal => $__:literal ),+ )=> {
$crate::WithLabel<$label_type_name<{ $crate::label_name($label_name) }$(,$inner_ty)?>, $type>
};
( $type:ty, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $($tail:tt)*) => {
$crate::WithLabel<$label_type_name<{ $crate::label_name($label_name) }$(,$inner_ty)?>, $crate::metric_type!($type, $($tail)*)>
};
( $type:ty, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr )=> {
$crate::WithLabel<$label_type_name<{ $crate::label_name($label_name) }$(,$inner_ty)?>, $type>
};
}

($name:expr, $value:expr) => {{
$crate::counter!($name, $value, &[]);
}};
#[doc(hidden)]
#[macro_export]
macro_rules! static_labels {
( $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr) => {
&[]
};
( $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $($tail:tt)*) => {
$crate::static_labels!($($tail)*)
};
( $( $label_name:literal => $label_value:literal ),+ ) => {
&[$(($label_name, $label_value),)*]
};
}

($name:expr, $value:expr, $tags:expr) => {{
$crate::counter!($name).add($value as u64, $tags);
}};
#[doc(hidden)]
#[macro_export]
macro_rules! resolve_labels {
( $var:ident, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $($tail:tt)*) => {
let $var = $var.resolve_label($label_type_name::<{ $crate::label_name($label_name) }$(,$inner_ty)?>::new($label_value));
$crate::resolve_labels!($var, $($tail)*)
};
( $var:ident, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr )=> {
let $var = $var.resolve_label($label_type_name::<{ $crate::label_name($label_name) }$(,$inner_ty)?>::new($label_value));
};
( $var:ident, $( $label_name:literal => $label_value:literal ),+ ) => {};
}
149 changes: 0 additions & 149 deletions crates/metrics/src/task.rs

This file was deleted.

273 changes: 273 additions & 0 deletions crates/metrics/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
use {
metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle},
prometheus_parse::{HistogramCount, Value},
std::collections::HashMap,
tikv_jemalloc_ctl as alloc,
};

#[global_allocator]
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;

#[test]
fn suite() {
use crate::examples::{
macros_counter::counters,
macros_future_metrics::future_metrics,
macros_gauge::gauges,
macros_histogram::histograms,
};

let mut metrics = Metrics::new();

let allocated1 = measure_heap_allocation();

counters(1);
gauges(1.0);
histograms(1.0);
#[cfg(feature = "future")]
smol::block_on(future_metrics());

let allocated2 = measure_heap_allocation();

assert!(allocated2 > allocated1);
assert!(
allocated2 - allocated1 < 1024 * 1024,
"before: {allocated1}, after: {allocated2}"
);

metrics.scrape();

metrics.assert_counters(1);
metrics.assert_gauges(1.0);
metrics.assert_histograms(1.0);
#[cfg(feature = "future")]
metrics.assert_future_metrics(1.0);

const ITERATIONS: usize = 1_000_000;

// Histograms are not included here, because their current implementation is
// leaky in `metrics` itself. It needs to be consumed consistently by the
// `prometheus` exporter to not leak.
//
// This crate adds identical abstractions on top of every `metrics` metric, so
// we can be pretty sure that at least our own code doesn't leak by just
// checking the counters.
let allocated1 = measure_heap_allocation();
for n in 0..ITERATIONS {
counters(1);
gauges(n as f64);
}
let allocated2 = measure_heap_allocation();

assert_eq!(allocated1, allocated2,);

metrics.scrape();

metrics.assert_counters(ITERATIONS as u64 + 1);
metrics.assert_gauges(ITERATIONS as f64 - 1.0);
}

struct Metrics {
prometheus: PrometheusHandle,
scrape: Option<prometheus_parse::Scrape>,
}

impl Metrics {
fn new() -> Self {
Self {
prometheus: PrometheusBuilder::new()
.set_buckets_for_metric(Matcher::Prefix("histogram".into()), &[0.0])
.unwrap()
.set_buckets_for_metric(Matcher::Prefix("future".into()), &[0.0])
.unwrap()
.install_recorder()
.unwrap(),
scrape: None,
}
}

fn scrape(&mut self) {
let rendered = self.prometheus.render();
print!("{rendered}");

self.scrape = Some(
prometheus_parse::Scrape::parse(rendered.lines().map(ToString::to_string).map(Ok))
.unwrap(),
);
}

fn assert_counters(&mut self, value: u64) {
self.assert_metrics("counter", Value::Counter(value as f64))
}

fn assert_gauges(&mut self, value: f64) {
self.assert_metrics("gauge", Value::Gauge(value))
}

fn assert_histograms(&mut self, count: f64) {
self.assert_metrics("histogram", expected_histogram(count))
}

fn assert_metrics(&mut self, ty: &'static str, value: Value) {
let name = |n| format!("{ty}{n}");

self.assert_metric(&name(1), None, &[], &value);
self.assert_metric(&name(2), None, &[("e", "a")], &value);
self.assert_metric(&name(3), None, &[("b", "true")], &value);
self.assert_metric(&name(4), None, &[("s", "a")], &value);
self.assert_metric(&name(5), None, &[("s", "42")], &value);

let labels = &[("e", "a"), ("s1", "a"), ("s2", "42"), ("b", "true")];
self.assert_metric(&name(6), None, labels, &value);

self.assert_metric(&name(7), None, &[("st", "1")], &value);

let labels = &[("st1", "1"), ("st2", "2")];
self.assert_metric(&name(8), None, labels, &value);

let labels = &[("s", "42"), ("st", "2")];
self.assert_metric(&name(9), None, labels, &value);

let labels = &[
("e", "a"),
("s1", "a"),
("s2", "42"),
("b", "true"),
("st1", "1"),
("st2", "2"),
];
self.assert_metric(&name(10), None, labels, &value);

self.assert_metric(&name(11), Some("description11"), &[], &value);
self.assert_metric(&name(12), Some("description12"), &[("e", "a")], &value);
self.assert_metric(&name(13), Some("description13"), &[("b", "true")], &value);
self.assert_metric(&name(14), Some("description14"), &[("s", "a")], &value);
self.assert_metric(&name(15), Some("description15"), &[("s", "42")], &value);

let labels = &[("e", "a"), ("s1", "a"), ("s2", "42"), ("b", "true")];
self.assert_metric(&name(16), Some("description16"), labels, &value);

self.assert_metric(&name(17), Some("description17"), &[("st", "1")], &value);

let labels = &[("st1", "1"), ("st2", "2")];
self.assert_metric(&name(18), Some("description18"), labels, &value);

let labels = &[("s", "42"), ("st", "2")];
self.assert_metric(&name(19), Some("description19"), labels, &value);

let labels = &[
("e", "a"),
("s1", "a"),
("s2", "42"),
("b", "true"),
("oe", "a"),
("os1", "a"),
("os2", "42"),
("ob", "true"),
("st1", "1"),
("st2", "2"),
];
self.assert_metric(&name(20), Some("description20"), labels, &value);
}

fn assert_metric(
&mut self,
name: &str,
description: Option<&str>,
labels: &[(&str, &str)],
value: &Value,
) {
let labels: HashMap<_, _> = labels
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();

let scrape = self.scrape.as_mut().unwrap();

let samples = &mut scrape.samples;
let idx = samples
.iter()
.position(|m| m.metric == name && *m.labels == labels)
.unwrap_or_else(|| panic!("{name} {labels:?} missing"));
let metric = samples.remove(idx);
assert_eq!(&metric.value, value);

let doc = scrape.docs.remove(name);
assert_eq!(doc, description.map(ToString::to_string))
}

#[cfg(feature = "future")]
fn assert_future_metrics(&mut self, count: f64) {
use crate::future::name;

self.assert_future_metrics_(name::FUTURES_CREATED, Value::Counter(count));
self.assert_future_metrics_(name::FUTURES_STARTED, Value::Counter(count));
self.assert_future_metrics_(name::FUTURES_FINISHED, Value::Counter(count));
self.assert_future_metrics_(name::FUTURE_POLLS, Value::Counter(count));

self.assert_future_metrics_(name::FUTURE_DURATION, expected_histogram(count));
self.assert_future_metrics_(name::FUTURE_POLL_DURATION, expected_histogram(count));
}

#[cfg(feature = "future")]
fn assert_future_metrics_(&mut self, name: &str, value: Value) {
let future_names: Vec<String> = (1..=10).map(|n| format!("future_metrics{n}")).collect();
let future_name = |n: usize| ("future_name", future_names[n].as_str());

self.assert_metric(name, None, &[future_name(0)], &value);
self.assert_metric(name, None, &[future_name(1), ("e", "a")], &value);
self.assert_metric(name, None, &[future_name(2), ("b", "true")], &value);
self.assert_metric(name, None, &[future_name(3), ("s", "a")], &value);
self.assert_metric(name, None, &[future_name(4), ("s", "42")], &value);

let labels = &[
future_name(5),
("e", "a"),
("s1", "a"),
("s2", "42"),
("b", "true"),
];
self.assert_metric(name, None, labels, &value);

self.assert_metric(name, None, &[future_name(6), ("st", "1")], &value);

let labels = &[future_name(7), ("st1", "1"), ("st2", "2")];
self.assert_metric(name, None, labels, &value);

let labels = &[future_name(8), ("s", "42"), ("st", "2")];
self.assert_metric(name, None, labels, &value);

let labels = &[
future_name(9),
("e", "a"),
("s1", "a"),
("s2", "42"),
("b", "true"),
("oe", "a"),
("os1", "a"),
("os2", "42"),
("ob", "true"),
("st1", "1"),
("st2", "2"),
];
self.assert_metric(name, None, labels, &value);
}
}

fn measure_heap_allocation() -> usize {
alloc::epoch::advance().unwrap();
alloc::stats::allocated::read().unwrap()
}

fn expected_histogram(count: f64) -> Value {
Value::Histogram(vec![
HistogramCount {
less_than: 0.0,
count: 0.0,
},
HistogramCount {
less_than: f64::INFINITY,
count,
},
])
}
8 changes: 5 additions & 3 deletions examples/alloc_stats.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use wc::metrics::ServiceMetrics;
use metrics_exporter_prometheus::PrometheusBuilder;

#[global_allocator]
static ALLOCATOR: wc::alloc::Jemalloc = wc::alloc::Jemalloc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
ServiceMetrics::init_with_name("metrics_example");
let prometheus = PrometheusBuilder::new()
.install_recorder()
.expect("install prometheus recorder");

// Collect allocation stats from Jemalloc and update the metrics.
wc::alloc::stats::update_jemalloc_metrics().unwrap();

println!("{}", ServiceMetrics::export().unwrap());
println!("{}", prometheus.render());

Ok(())
}
35 changes: 0 additions & 35 deletions examples/metrics.rs

This file was deleted.

4 changes: 0 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -7,12 +7,8 @@ pub use analytics;
pub use collections;
#[cfg(feature = "future")]
pub use future;
#[cfg(feature = "future_metrics")]
pub use future_metrics;
#[cfg(feature = "geoip")]
pub use geoip;
#[cfg(feature = "http")]
pub use http;
#[cfg(feature = "metrics")]
pub use metrics;
#[cfg(feature = "rate_limit")]

0 comments on commit af0464e

Please sign in to comment.