use metrics::{counter, gauge, histogram};
use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
use std::sync::OnceLock;
static PROMETHEUS_HANDLE: OnceLock<PrometheusHandle> = OnceLock::new();
pub fn init_metrics() -> Option<PrometheusHandle> {
let handle = match PrometheusBuilder::new().install_recorder() {
Ok(h) => h,
Err(e) => {
tracing::error!(error = %e, "Failed to install Prometheus recorder, metrics disabled");
return None;
}
};
if let Err(existing) = PROMETHEUS_HANDLE.set(handle.clone()) {
tracing::warn!("Metrics already initialized, using existing handle");
return Some(existing);
}
gauge!("cedros_login_up").set(1.0);
tracing::info!("Prometheus metrics initialized");
Some(handle)
}
pub fn get_prometheus_handle() -> Option<&'static PrometheusHandle> {
PROMETHEUS_HANDLE.get()
}
pub fn render_metrics() -> String {
PROMETHEUS_HANDLE
.get()
.map(|h| h.render())
.unwrap_or_else(|| "# Metrics not initialized\n".to_string())
}
pub fn record_auth_success(method: &str) {
counter!("cedros_login_auth_total", "method" => method.to_string(), "result" => "success")
.increment(1);
}
pub fn record_auth_failure(method: &str, reason: &str) {
counter!(
"cedros_login_auth_total",
"method" => method.to_string(),
"result" => "failure",
"reason" => reason.to_string()
)
.increment(1);
}
pub fn record_auth_duration(method: &str, duration_secs: f64) {
histogram!(
"cedros_login_auth_duration_seconds",
"method" => method.to_string()
)
.record(duration_secs);
}
pub fn set_active_sessions(count: u64) {
gauge!("cedros_login_active_sessions").set(count as f64);
}
pub fn record_session_created() {
counter!("cedros_login_sessions_total", "action" => "created").increment(1);
}
pub fn record_session_revoked() {
counter!("cedros_login_sessions_total", "action" => "revoked").increment(1);
}
pub fn set_total_users(count: u64) {
gauge!("cedros_login_users_total").set(count as f64);
}
pub fn record_user_registered() {
counter!("cedros_login_registrations_total").increment(1);
}
pub fn record_deposit(token: &str, amount_usd: f64) {
counter!("cedros_login_deposits_total", "token" => token.to_string()).increment(1);
counter!("cedros_login_deposits_usd_total", "token" => token.to_string())
.increment(amount_usd as u64);
}
pub fn record_credits_spent(amount: u64) {
counter!("cedros_login_credits_spent_total").increment(amount);
}
pub fn record_withdrawal(status: &str) {
counter!("cedros_login_withdrawals_total", "status" => status.to_string()).increment(1);
}
pub fn record_http_request(method: &str, path: &str, status: u16, duration_secs: f64) {
let status_class = match status {
200..=299 => "2xx",
300..=399 => "3xx",
400..=499 => "4xx",
500..=599 => "5xx",
_ => "other",
};
counter!(
"cedros_login_http_requests_total",
"method" => method.to_string(),
"path" => path.to_string(),
"status" => status_class.to_string()
)
.increment(1);
histogram!(
"cedros_login_http_request_duration_seconds",
"method" => method.to_string(),
"path" => path.to_string()
)
.record(duration_secs);
}
pub fn record_rate_limit_hit(key_type: &str) {
counter!("cedros_login_rate_limit_hits_total", "key_type" => key_type.to_string()).increment(1);
}
pub fn record_error(error_type: &str) {
counter!("cedros_login_errors_total", "type" => error_type.to_string()).increment(1);
}
#[cfg(test)]
mod tests {
#[test]
fn test_render_without_init() {
}
}