use metrics::{counter, gauge, histogram};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static STARTUP_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
pub mod names {
pub const SIGNING_REQUESTS_TOTAL: &str = "nklave_signing_requests_total";
pub const SIGNING_REFUSALS_TOTAL: &str = "nklave_signing_refusals_total";
pub const SIGNING_LATENCY_SECONDS: &str = "nklave_signing_latency_seconds";
pub const BLOCKS_SIGNED_TOTAL: &str = "nklave_blocks_signed_total";
pub const ATTESTATIONS_SIGNED_TOTAL: &str = "nklave_attestations_signed_total";
pub const VALIDATORS_TOTAL: &str = "nklave_validators_total";
pub const STATE_SEQUENCE: &str = "nklave_state_sequence";
pub const STARTUP_TIMESTAMP_SECONDS: &str = "nklave_startup_timestamp_seconds";
pub const UPTIME_SECONDS: &str = "nklave_uptime_seconds";
pub const CHECKPOINT_AGE_SECONDS: &str = "nklave_checkpoint_age_seconds";
pub const LAST_CHECKPOINT_SEQUENCE: &str = "nklave_last_checkpoint_sequence";
pub const LAST_SIGNED_SLOT: &str = "nklave_last_signed_slot";
pub const LAST_SIGNED_TARGET_EPOCH: &str = "nklave_last_signed_target_epoch";
pub const KEYS_RELOAD_TOTAL: &str = "nklave_keys_reload_total";
pub const KEYS_LOAD_DURATION_SECONDS: &str = "nklave_keys_load_duration_seconds";
pub const REPLICATION_LAG_SEQUENCES: &str = "nklave_replication_lag_sequences";
pub const NODE_ROLE: &str = "nklave_node_role";
}
pub fn record_signing_success(request_type: &str, validator: &str) {
counter!(names::SIGNING_REQUESTS_TOTAL, "type" => request_type.to_string(), "status" => "success", "validator" => validator.to_string())
.increment(1);
}
pub fn record_signing_refusal(request_type: &str, reason: &str, validator: &str) {
counter!(names::SIGNING_REQUESTS_TOTAL, "type" => request_type.to_string(), "status" => "refused", "validator" => validator.to_string())
.increment(1);
counter!(names::SIGNING_REFUSALS_TOTAL, "type" => request_type.to_string(), "reason" => reason.to_string(), "validator" => validator.to_string())
.increment(1);
}
pub fn record_signing_latency(request_type: &str, latency_seconds: f64) {
histogram!(names::SIGNING_LATENCY_SECONDS, "type" => request_type.to_string())
.record(latency_seconds);
}
pub fn set_validators_count(count: usize) {
gauge!(names::VALIDATORS_TOTAL).set(count as f64);
}
pub fn set_state_sequence(sequence: u64) {
gauge!(names::STATE_SEQUENCE).set(sequence as f64);
}
pub fn record_block_signed(validator: &str) {
counter!(names::BLOCKS_SIGNED_TOTAL, "validator" => validator.to_string()).increment(1);
}
pub fn record_attestation_signed(validator: &str) {
counter!(names::ATTESTATIONS_SIGNED_TOTAL, "validator" => validator.to_string()).increment(1);
}
pub fn init_startup_metrics() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
STARTUP_TIMESTAMP.store(now, Ordering::SeqCst);
gauge!(names::STARTUP_TIMESTAMP_SECONDS).set(now as f64);
}
pub fn update_uptime() {
let startup = STARTUP_TIMESTAMP.load(Ordering::SeqCst);
if startup > 0 {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let uptime = now.saturating_sub(startup);
gauge!(names::UPTIME_SECONDS).set(uptime as f64);
}
}
pub fn set_checkpoint_age(age_seconds: u64) {
gauge!(names::CHECKPOINT_AGE_SECONDS).set(age_seconds as f64);
}
pub fn set_last_checkpoint_sequence(sequence: u64) {
gauge!(names::LAST_CHECKPOINT_SEQUENCE).set(sequence as f64);
}
pub fn set_last_signed_slot(validator: &str, slot: u64) {
gauge!(names::LAST_SIGNED_SLOT, "validator" => validator.to_string()).set(slot as f64);
}
pub fn set_last_signed_target_epoch(validator: &str, epoch: u64) {
gauge!(names::LAST_SIGNED_TARGET_EPOCH, "validator" => validator.to_string()).set(epoch as f64);
}
pub fn record_keys_reload(new_keys: usize) {
counter!(names::KEYS_RELOAD_TOTAL).increment(1);
gauge!(names::VALIDATORS_TOTAL).set(new_keys as f64);
}
pub fn record_keys_load_duration(duration_seconds: f64) {
histogram!(names::KEYS_LOAD_DURATION_SECONDS).record(duration_seconds);
}
pub fn set_replication_lag(lag_sequences: u64) {
gauge!(names::REPLICATION_LAG_SEQUENCES).set(lag_sequences as f64);
}
pub fn set_node_role(role: &str) {
gauge!(names::NODE_ROLE, "role" => "primary").set(0.0);
gauge!(names::NODE_ROLE, "role" => "passive").set(0.0);
gauge!(names::NODE_ROLE, "role" => "standalone").set(0.0);
gauge!(names::NODE_ROLE, "role" => "promoting").set(0.0);
gauge!(names::NODE_ROLE, "role" => role.to_string()).set(1.0);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metric_names() {
assert!(names::SIGNING_REQUESTS_TOTAL.starts_with("nklave_"));
assert!(names::SIGNING_REFUSALS_TOTAL.starts_with("nklave_"));
assert!(names::SIGNING_LATENCY_SECONDS.starts_with("nklave_"));
}
}