use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use log::{debug, error, info, warn};
use crate::monitoring::blockchain_metrics;
use crate::monitoring::blockchain_alerts;
const DEFAULT_METRICS_INTERVAL_MS: u64 = 10000;
pub struct MetricsService {
interval_ms: u64,
running: Arc<Mutex<bool>>,
last_collection: Arc<Mutex<Instant>>,
}
impl MetricsService {
pub fn new(interval_ms: Option<u64>) -> Self {
Self {
interval_ms: interval_ms.unwrap_or(DEFAULT_METRICS_INTERVAL_MS),
running: Arc::new(Mutex::new(false)),
last_collection: Arc::new(Mutex::new(Instant::now())),
}
}
pub fn start(&self) {
let mut running = self.running.lock().unwrap();
if *running {
warn!("Metrics service is already running");
return;
}
*running = true;
info!("Starting blockchain metrics service with interval of {}ms", self.interval_ms);
let running_clone = Arc::clone(&self.running);
let last_collection_clone = Arc::clone(&self.last_collection);
let interval_ms = self.interval_ms;
thread::spawn(move || {
while *running_clone.lock().unwrap() {
Self::collect_metrics();
blockchain_alerts::check_alerts();
*last_collection_clone.lock().unwrap() = Instant::now();
thread::sleep(Duration::from_millis(interval_ms));
}
info!("Blockchain metrics service stopped");
});
}
pub fn stop(&self) {
let mut running = self.running.lock().unwrap();
*running = false;
info!("Stopping blockchain metrics service");
}
fn collect_metrics() {
debug!("Collecting blockchain metrics...");
Self::collect_simulated_metrics();
}
fn collect_simulated_metrics() {
let segwit_pct = 85.0 + (rand::random::<f64>() - 0.5) * 5.0;
blockchain_metrics::update_segwit_percentage(segwit_pct);
let taproot_pct = 12.5 + (rand::random::<f64>() - 0.5) * 3.0;
blockchain_metrics::update_taproot_percentage(taproot_pct);
let utxo_size = 82_500_000 + (rand::random::<f64>() * 100_000.0) as u64;
blockchain_metrics::update_utxo_set_size(utxo_size);
let fee_rate = 20.0 + (rand::random::<f64>() - 0.5) * 10.0;
blockchain_metrics::update_avg_fee_rate(fee_rate);
let conn_error_rate = if rand::random::<f64>() < 0.95 {
rand::random::<f64>() * 0.02
} else {
0.05 + rand::random::<f64>() * 0.05
};
blockchain_metrics::update_error_rate("connection_failure", conn_error_rate);
let mempool_size = 15_000_000 + (rand::random::<f64>() * 10_000_000.0) as u64;
blockchain_metrics::update_mempool_size(mempool_size);
let avg_block_size = 1_200_000 + (rand::random::<f64>() * 200_000.0) as u64;
blockchain_metrics::update_avg_block_size(avg_block_size);
static mut LAST_BLOCK_HEIGHT: u64 = 750_432;
let new_block = rand::random::<f64>() < 0.1;
unsafe {
if new_block {
LAST_BLOCK_HEIGHT += 1;
let block_hash = format!("000000000000000000{:x}", rand::random::<u32>());
let propagation_time = 200 + (rand::random::<f64>() * 300.0) as u64;
blockchain_metrics::update_block_propagation_time(&block_hash, propagation_time);
}
blockchain_metrics::update_block_height(LAST_BLOCK_HEIGHT);
}
let hashrate = 300.0 + (rand::random::<f64>() - 0.5) * 15.0;
blockchain_metrics::update_network_hashrate(hashrate);
blockchain_metrics::set_bip_compliance("341", true); blockchain_metrics::set_bip_compliance("342", true); blockchain_metrics::set_bip_compliance("174", true); }
}
pub fn get_metrics_interval() -> u64 {
std::env::var("ANYA_METRICS_COLLECTION_INTERVAL_MS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(DEFAULT_METRICS_INTERVAL_MS)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_service() {
let service = MetricsService::new(Some(100));
service.start();
thread::sleep(Duration::from_millis(300));
service.stop();
let metrics = blockchain_metrics::get_metrics_json();
assert!(metrics["segwit_percentage"].as_f64().unwrap() > 0.0);
}
}