Lines
90.48 %
Functions
75 %
Branches
100 %
//! Metric names and recorder setup.
//!
//! Names carry the `nomisync_` prefix because the series land in a
//! VictoriaMetrics shared with every other app on the cluster: a bare
//! `http_requests_total` would collide with anything else exporting the obvious
//! name, and an ad-hoc query without a namespace filter would silently mix them.
//! Keeping the names here as constants means the exporter and any test that
//! asserts on them cannot drift apart.
use metrics_exporter_prometheus::{BuildError, Matcher, PrometheusBuilder, PrometheusHandle};
/// Requests served, labelled by method, matched path and status.
pub const HTTP_REQUESTS: &str = "nomisync_http_requests_total";
/// Request latency in seconds, same labels, bucketed by [`LATENCY_BUCKETS`].
pub const HTTP_REQUEST_DURATION: &str = "nomisync_http_requests_duration_seconds";
/// Web-request latencies: sub-10ms to 10s, which spans a cached page render and
/// a slow report query.
const LATENCY_BUCKETS: &[f64] = &[
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
];
/// Install the process-wide Prometheus recorder.
///
/// # Errors
/// [`BuildError`] if the bucket configuration is rejected or a recorder is
/// already installed — both are startup faults, reported rather than panicked.
pub fn install_recorder() -> Result<PrometheusHandle, BuildError> {
PrometheusBuilder::new()
.set_buckets_for_metric(
Matcher::Full(HTTP_REQUEST_DURATION.to_string()),
LATENCY_BUCKETS,
)?
.install_recorder()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn names_carry_the_app_prefix() {
for name in [HTTP_REQUESTS, HTTP_REQUEST_DURATION] {
assert!(
name.starts_with("nomisync_"),
"{name} would collide with other apps in the shared VictoriaMetrics"
);
fn recorder_renders_the_registered_series() {
let Ok(handle) = install_recorder() else {
// A recorder is global and install-once; another test in this binary
// may already hold it. Nothing to assert then.
return;
};
metrics::counter!(HTTP_REQUESTS, &[("method", "GET")]).increment(1);
let rendered = handle.render();
rendered.contains(HTTP_REQUESTS),
"exporter did not render {HTTP_REQUESTS}"