Implement hysteresis for metric status changes to prevent flapping

Add comprehensive hysteresis support to prevent status oscillation near
threshold boundaries while maintaining responsive alerting.

Key Features:
- HysteresisThresholds with configurable upper/lower limits
- StatusTracker for per-metric status history
- Default gaps: CPU load 10%, memory 5%, disk temp 5°C

Updated Components:
- CPU load collector (5-minute average with hysteresis)
- Memory usage collector (percentage-based thresholds)
- Disk temperature collector (SMART data monitoring)
- All collectors updated to support StatusTracker interface

Cache Interval Adjustments:
- Service status: 60s → 10s (faster response)
- Disk usage: 300s → 60s (more frequent checks)
- Backup status: 900s → 60s (quicker updates)
- SMART data: moved to 600s tier (10 minutes)

Architecture:
- Individual metric status calculation in collectors
- Centralized StatusTracker in MetricCollectionManager
- Status aggregation preserved in dashboard widgets
This commit is contained in:
2025-10-20 18:45:41 +02:00
parent e998679901
commit 00a8ed3da2
34 changed files with 1037 additions and 770 deletions

View File

@@ -1,14 +1,14 @@
use anyhow::Result;
use clap::Parser;
use tracing::{info, error};
use tracing::{error, info};
use tracing_subscriber::EnvFilter;
mod app;
mod config;
mod communication;
mod config;
mod hosts;
mod metrics;
mod ui;
mod hosts;
mod utils;
use app::Dashboard;
@@ -21,11 +21,11 @@ struct Cli {
/// Increase logging verbosity (-v, -vv)
#[arg(short, long, action = clap::ArgAction::Count)]
verbose: u8,
/// Configuration file path
#[arg(short, long)]
config: Option<String>,
/// Run in headless mode (no TUI, just logging)
#[arg(long)]
headless: bool,
@@ -34,16 +34,16 @@ struct Cli {
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
// Setup logging - only if headless or verbose
if cli.headless || cli.verbose > 0 {
let log_level = match cli.verbose {
0 => "warn", // Only warnings and errors when not verbose
0 => "warn", // Only warnings and errors when not verbose
1 => "info",
2 => "debug",
2 => "debug",
_ => "trace",
};
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env().add_directive(log_level.parse()?))
.init();
@@ -53,21 +53,21 @@ async fn main() -> Result<()> {
.with_env_filter(EnvFilter::from_default_env().add_directive("off".parse()?))
.init();
}
if cli.headless || cli.verbose > 0 {
info!("CM Dashboard starting with individual metrics architecture...");
}
// Create and run dashboard
let mut dashboard = Dashboard::new(cli.config, cli.headless).await?;
// Setup graceful shutdown
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
// Run dashboard with graceful shutdown
tokio::select! {
result = dashboard.run() => {
@@ -80,9 +80,9 @@ async fn main() -> Result<()> {
info!("Shutdown signal received");
}
}
if cli.headless || cli.verbose > 0 {
info!("Dashboard shutdown complete");
}
Ok(())
}
}