use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
pub fn install() -> PrometheusHandle {
PrometheusBuilder::new()
.install_recorder()
.expect("metrics recorder install (must be called once at startup)")
}
pub mod names {
pub const REQUESTS_TOTAL: &str = "s4_requests_total";
pub const BYTES_IN_TOTAL: &str = "s4_bytes_in_total";
pub const BYTES_OUT_TOTAL: &str = "s4_bytes_out_total";
pub const REQUEST_LATENCY_SECONDS: &str = "s4_request_latency_seconds";
pub const POLICY_DENIALS_TOTAL: &str = "s4_policy_denials_total";
pub const TLS_CERT_RELOAD_TOTAL: &str = "s4_tls_cert_reload_total";
pub const ACME_RENEWAL_TOTAL: &str = "s4_acme_renewal_total";
pub const ACME_CERT_EXPIRY_SECONDS: &str = "s4_acme_cert_expiry_seconds";
}
pub fn record_tls_cert_reload(ok: bool) {
let result = if ok { "ok" } else { "err" };
metrics::counter!(names::TLS_CERT_RELOAD_TOTAL, "result" => result).increment(1);
}
pub fn record_acme_renewal(ok: bool) {
let result = if ok { "ok" } else { "err" };
metrics::counter!(names::ACME_RENEWAL_TOTAL, "result" => result).increment(1);
}
pub fn record_acme_cert_expiry(seconds_until_expiry: f64) {
metrics::gauge!(names::ACME_CERT_EXPIRY_SECONDS).set(seconds_until_expiry);
}
pub fn record_policy_denial(action: &'static str, bucket: &str) {
metrics::counter!(
names::POLICY_DENIALS_TOTAL,
"action" => action,
"bucket" => bucket.to_owned(),
)
.increment(1);
}
pub fn record_put(codec: &'static str, bytes_in: u64, bytes_out: u64, latency_secs: f64, ok: bool) {
let result = if ok { "ok" } else { "err" };
metrics::counter!(names::REQUESTS_TOTAL, "op" => "put", "codec" => codec, "result" => result)
.increment(1);
metrics::counter!(names::BYTES_IN_TOTAL, "op" => "put", "codec" => codec).increment(bytes_in);
metrics::counter!(names::BYTES_OUT_TOTAL, "op" => "put", "codec" => codec).increment(bytes_out);
metrics::histogram!(names::REQUEST_LATENCY_SECONDS, "op" => "put", "codec" => codec)
.record(latency_secs);
}
pub fn record_get(codec: &'static str, bytes_in: u64, bytes_out: u64, latency_secs: f64, ok: bool) {
let result = if ok { "ok" } else { "err" };
metrics::counter!(names::REQUESTS_TOTAL, "op" => "get", "codec" => codec, "result" => result)
.increment(1);
metrics::counter!(names::BYTES_IN_TOTAL, "op" => "get", "codec" => codec).increment(bytes_in);
metrics::counter!(names::BYTES_OUT_TOTAL, "op" => "get", "codec" => codec).increment(bytes_out);
metrics::histogram!(names::REQUEST_LATENCY_SECONDS, "op" => "get", "codec" => codec)
.record(latency_secs);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn install_and_render_basic_counters() {
let handle = install();
record_put("cpu-zstd", 1000, 100, 0.05, true);
record_get("cpu-zstd", 100, 1000, 0.02, true);
let rendered = handle.render();
assert!(rendered.contains("s4_requests_total"));
assert!(rendered.contains("s4_bytes_in_total"));
assert!(rendered.contains("s4_bytes_out_total"));
assert!(rendered.contains("s4_request_latency_seconds"));
assert!(rendered.contains("op=\"put\""));
assert!(rendered.contains("op=\"get\""));
assert!(rendered.contains("codec=\"cpu-zstd\""));
}
}