use super::types::{BeaconProcessMetrics, ValidatorProcessMetrics}; use lazy_static::lazy_static; use lighthouse_metrics::{MetricFamily, MetricType}; use serde_json::json; use std::collections::HashMap; use std::path::Path; /// Represents a metric that needs to be fetched from lighthouse metrics registry /// and sent to the remote monitoring service. #[derive(Debug, Clone)] pub struct JsonMetric { /// Name of the metric as used in Lighthouse metrics. lighthouse_metric_name: &'static str, /// Json key for the metric that we send to the remote monitoring endpoint. json_output_key: &'static str, /// Type of the json value to be sent to the remote monitoring endpoint ty: JsonType, } impl JsonMetric { const fn new( lighthouse_metric_name: &'static str, json_output_key: &'static str, ty: JsonType, ) -> Self { Self { lighthouse_metric_name, json_output_key, ty, } } /// Return a json value given given the metric type. fn get_typed_value(&self, value: i64) -> serde_json::Value { match self.ty { JsonType::Integer => json!(value), JsonType::Boolean => { if value > 0 { json!(true) } else { json!(false) } } } } /// Return a default json value given given the metric type. fn get_typed_value_default(&self) -> serde_json::Value { match self.ty { JsonType::Integer => json!(0), JsonType::Boolean => { json!(false) } } } } /// The required metrics for the beacon and validator processes. const BEACON_PROCESS_METRICS: &[JsonMetric] = &[ JsonMetric::new( "sync_eth1_fallback_configured", "sync_eth1_fallback_configured", JsonType::Boolean, ), JsonMetric::new( "sync_eth1_fallback_connected", "sync_eth1_fallback_connected", JsonType::Boolean, ), JsonMetric::new( "sync_eth1_connected", "sync_eth1_connected", JsonType::Boolean, ), JsonMetric::new( "store_disk_db_size", "disk_beaconchain_bytes_total", JsonType::Integer, ), JsonMetric::new("libp2p_peers", "network_peers_connected", JsonType::Integer), JsonMetric::new( "libp2p_outbound_bytes", "network_libp2p_bytes_total_transmit", JsonType::Integer, ), JsonMetric::new( "libp2p_inbound_bytes", "network_libp2p_bytes_total_receive", JsonType::Integer, ), JsonMetric::new( "notifier_head_slot", "sync_beacon_head_slot", JsonType::Integer, ), JsonMetric::new("sync_eth2_synced", "sync_eth2_synced", JsonType::Boolean), ]; const VALIDATOR_PROCESS_METRICS: &[JsonMetric] = &[ JsonMetric::new( "vc_validators_enabled_count", "validator_active", JsonType::Integer, ), JsonMetric::new( "vc_validators_total_count", "validator_total", JsonType::Integer, ), JsonMetric::new( "sync_eth2_fallback_configured", "sync_eth2_fallback_configured", JsonType::Boolean, ), JsonMetric::new( "sync_eth2_fallback_connected", "sync_eth2_fallback_connected", JsonType::Boolean, ), ]; /// Represents the type for the JSON output. #[derive(Debug, Clone)] pub enum JsonType { Integer, Boolean, } lazy_static! { /// HashMap representing the `BEACON_PROCESS_METRICS`. pub static ref BEACON_METRICS_MAP: HashMap = BEACON_PROCESS_METRICS .iter() .map(|metric| (metric.lighthouse_metric_name.to_string(), metric.clone())) .collect(); /// HashMap representing the `VALIDATOR_PROCESS_METRICS`. pub static ref VALIDATOR_METRICS_MAP: HashMap = VALIDATOR_PROCESS_METRICS .iter() .map(|metric| (metric.lighthouse_metric_name.to_string(), metric.clone())) .collect(); } /// Returns the value from a Counter/Gauge `MetricType` assuming that it has no associated labels /// else it returns `None`. fn get_value(mf: &MetricFamily) -> Option { let metric = mf.get_metric().first()?; match mf.get_field_type() { MetricType::COUNTER => Some(metric.get_counter().get_value() as i64), MetricType::GAUGE => Some(metric.get_gauge().get_value() as i64), _ => None, } } /// Collects all metrics and returns a `serde_json::Value` object with the required metrics /// from the metrics hashmap. pub fn gather_metrics(metrics_map: &HashMap) -> Option { let metric_families = lighthouse_metrics::gather(); let mut res = serde_json::Map::with_capacity(metrics_map.len()); for mf in metric_families.iter() { let metric_name = mf.get_name(); if metrics_map.contains_key(metric_name) { let value = get_value(mf).unwrap_or_default(); let metric = metrics_map.get(metric_name)?; let value = metric.get_typed_value(value); let _ = res.insert(metric.json_output_key.to_string(), value); }; } // Insert default metrics for all monitoring service metrics that do not // exist as lighthouse metrics. for json_metric in metrics_map.values() { if !res.contains_key(json_metric.json_output_key) { let _ = res.insert( json_metric.json_output_key.to_string(), json_metric.get_typed_value_default(), ); } } Some(serde_json::Value::Object(res)) } /// Gathers and returns the lighthouse beacon metrics. pub fn gather_beacon_metrics( db_path: &Path, freezer_db_path: &Path, ) -> Result { // Update db size metrics store::metrics::scrape_for_metrics(db_path, freezer_db_path); let beacon_metrics = gather_metrics(&BEACON_METRICS_MAP) .ok_or_else(|| "Failed to gather beacon metrics".to_string())?; let process = eth2::lighthouse::ProcessHealth::observe()?.into(); Ok(BeaconProcessMetrics { beacon: beacon_metrics, common: process, }) } /// Gathers and returns the lighthouse validator metrics. pub fn gather_validator_metrics() -> Result { let validator_metrics = gather_metrics(&VALIDATOR_METRICS_MAP) .ok_or_else(|| "Failed to gather validator metrics".to_string())?; let process = eth2::lighthouse::ProcessHealth::observe()?.into(); Ok(ValidatorProcessMetrics { validator: validator_metrics, common: process, }) }