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 const RATE_LIMIT_THROTTLED_TOTAL: &str = "s4_rate_limit_throttled_total";
pub const COMPLIANCE_MODE_ACTIVE: &str = "s4_compliance_mode_active";
pub const NOTIFICATIONS_DROPPED_TOTAL: &str = "s4_notifications_dropped_total";
pub const LIFECYCLE_ACTIONS_TOTAL: &str = "s4_lifecycle_actions_total";
pub const REPLICATION_DROPPED_TOTAL: &str = "s4_replication_dropped_total";
pub const REPLICATION_REPLICATED_TOTAL: &str = "s4_replication_replicated_total";
pub const MFA_DELETE_DENIALS_TOTAL: &str = "s4_mfa_delete_denials_total";
}
pub fn record_mfa_delete_denial(bucket: &str) {
metrics::counter!(
names::MFA_DELETE_DENIALS_TOTAL,
"bucket" => bucket.to_owned(),
)
.increment(1);
}
pub fn record_replication_drop(bucket: &str) {
metrics::counter!(
names::REPLICATION_DROPPED_TOTAL,
"bucket" => bucket.to_owned(),
)
.increment(1);
}
pub fn record_replication_replicated(bucket: &str, dest: &str) {
metrics::counter!(
names::REPLICATION_REPLICATED_TOTAL,
"bucket" => bucket.to_owned(),
"dest" => dest.to_owned(),
)
.increment(1);
}
pub fn record_lifecycle_action(bucket: &str, action: &'static str) {
metrics::counter!(
names::LIFECYCLE_ACTIONS_TOTAL,
"bucket" => bucket.to_owned(),
"action" => action,
)
.increment(1);
}
pub fn record_notification_drop(dest_type: &'static str) {
metrics::counter!(
names::NOTIFICATIONS_DROPPED_TOTAL,
"dest" => dest_type,
)
.increment(1);
}
pub fn record_compliance_mode_active(mode: &'static str) {
metrics::gauge!(names::COMPLIANCE_MODE_ACTIVE, "mode" => mode).set(1.0);
}
pub fn record_rate_limit_throttle(principal: &str, bucket: &str) {
metrics::counter!(
names::RATE_LIMIT_THROTTLED_TOTAL,
"principal" => principal.to_owned(),
"bucket" => bucket.to_owned(),
)
.increment(1);
}
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\""));
}
}