-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
feat: metrics NG (#22)
Showing
24 changed files
with
2,667 additions
and
893 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ),+ ) => {}; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}, | ||
]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters