Update to v0.1.18 with per-collector intervals and tmux check
All checks were successful
Build and Release / build-and-release (push) Successful in 2m7s

- Implement per-collector interval timing respecting NixOS config
- Remove all hardcoded timeout/interval values and make configurable
- Add tmux session requirement check for TUI mode (bypassed for headless)
- Update agent to send config hash in Build field instead of nixos version
- Add nginx check interval, HTTP timeouts, and ZMQ transmission interval configs
- Update NixOS configuration with new configurable values

Breaking changes:
- Build field now shows nix store config hash (8 chars) instead of nixos version
- All intervals now follow individual collector configuration instead of global

New configuration fields:
- systemd.nginx_check_interval_seconds
- systemd.http_timeout_seconds
- systemd.http_connect_timeout_seconds
- zmq.transmission_interval_seconds
This commit is contained in:
2025-10-28 10:08:25 +01:00
parent b1bff4857b
commit 627c533724
15 changed files with 414 additions and 76 deletions

View File

@@ -1,6 +1,7 @@
use anyhow::Result;
use cm_dashboard_shared::{Metric, StatusTracker};
use tracing::{error, info};
use std::time::{Duration, Instant};
use tracing::{debug, error, info};
use crate::collectors::{
backup::BackupCollector, cpu::CpuCollector, disk::DiskCollector, memory::MemoryCollector,
@@ -8,15 +9,23 @@ use crate::collectors::{
};
use crate::config::{AgentConfig, CollectorConfig};
/// Manages all metric collectors
/// Collector with timing information
struct TimedCollector {
collector: Box<dyn Collector>,
interval: Duration,
last_collection: Option<Instant>,
name: String,
}
/// Manages all metric collectors with individual intervals
pub struct MetricCollectionManager {
collectors: Vec<Box<dyn Collector>>,
collectors: Vec<TimedCollector>,
status_tracker: StatusTracker,
}
impl MetricCollectionManager {
pub async fn new(config: &CollectorConfig, _agent_config: &AgentConfig) -> Result<Self> {
let mut collectors: Vec<Box<dyn Collector>> = Vec::new();
let mut collectors: Vec<TimedCollector> = Vec::new();
// Benchmark mode - only enable specific collector based on env var
let benchmark_mode = std::env::var("BENCHMARK_COLLECTOR").ok();
@@ -26,7 +35,12 @@ impl MetricCollectionManager {
// CPU collector only
if config.cpu.enabled {
let cpu_collector = CpuCollector::new(config.cpu.clone());
collectors.push(Box::new(cpu_collector));
collectors.push(TimedCollector {
collector: Box::new(cpu_collector),
interval: Duration::from_secs(config.cpu.interval_seconds),
last_collection: None,
name: "CPU".to_string(),
});
info!("BENCHMARK: CPU collector only");
}
}
@@ -34,20 +48,35 @@ impl MetricCollectionManager {
// Memory collector only
if config.memory.enabled {
let memory_collector = MemoryCollector::new(config.memory.clone());
collectors.push(Box::new(memory_collector));
collectors.push(TimedCollector {
collector: Box::new(memory_collector),
interval: Duration::from_secs(config.memory.interval_seconds),
last_collection: None,
name: "Memory".to_string(),
});
info!("BENCHMARK: Memory collector only");
}
}
Some("disk") => {
// Disk collector only
let disk_collector = DiskCollector::new(config.disk.clone());
collectors.push(Box::new(disk_collector));
collectors.push(TimedCollector {
collector: Box::new(disk_collector),
interval: Duration::from_secs(config.disk.interval_seconds),
last_collection: None,
name: "Disk".to_string(),
});
info!("BENCHMARK: Disk collector only");
}
Some("systemd") => {
// Systemd collector only
let systemd_collector = SystemdCollector::new(config.systemd.clone());
collectors.push(Box::new(systemd_collector));
collectors.push(TimedCollector {
collector: Box::new(systemd_collector),
interval: Duration::from_secs(config.systemd.interval_seconds),
last_collection: None,
name: "Systemd".to_string(),
});
info!("BENCHMARK: Systemd collector only");
}
Some("backup") => {
@@ -57,7 +86,12 @@ impl MetricCollectionManager {
config.backup.backup_paths.first().cloned(),
config.backup.max_age_hours,
);
collectors.push(Box::new(backup_collector));
collectors.push(TimedCollector {
collector: Box::new(backup_collector),
interval: Duration::from_secs(config.backup.interval_seconds),
last_collection: None,
name: "Backup".to_string(),
});
info!("BENCHMARK: Backup collector only");
}
}
@@ -69,37 +103,67 @@ impl MetricCollectionManager {
// Normal mode - all collectors
if config.cpu.enabled {
let cpu_collector = CpuCollector::new(config.cpu.clone());
collectors.push(Box::new(cpu_collector));
info!("CPU collector initialized");
collectors.push(TimedCollector {
collector: Box::new(cpu_collector),
interval: Duration::from_secs(config.cpu.interval_seconds),
last_collection: None,
name: "CPU".to_string(),
});
info!("CPU collector initialized with {}s interval", config.cpu.interval_seconds);
}
if config.memory.enabled {
let memory_collector = MemoryCollector::new(config.memory.clone());
collectors.push(Box::new(memory_collector));
info!("Memory collector initialized");
collectors.push(TimedCollector {
collector: Box::new(memory_collector),
interval: Duration::from_secs(config.memory.interval_seconds),
last_collection: None,
name: "Memory".to_string(),
});
info!("Memory collector initialized with {}s interval", config.memory.interval_seconds);
}
let disk_collector = DiskCollector::new(config.disk.clone());
collectors.push(Box::new(disk_collector));
info!("Disk collector initialized");
collectors.push(TimedCollector {
collector: Box::new(disk_collector),
interval: Duration::from_secs(config.disk.interval_seconds),
last_collection: None,
name: "Disk".to_string(),
});
info!("Disk collector initialized with {}s interval", config.disk.interval_seconds);
let systemd_collector = SystemdCollector::new(config.systemd.clone());
collectors.push(Box::new(systemd_collector));
info!("Systemd collector initialized");
collectors.push(TimedCollector {
collector: Box::new(systemd_collector),
interval: Duration::from_secs(config.systemd.interval_seconds),
last_collection: None,
name: "Systemd".to_string(),
});
info!("Systemd collector initialized with {}s interval", config.systemd.interval_seconds);
if config.backup.enabled {
let backup_collector = BackupCollector::new(
config.backup.backup_paths.first().cloned(),
config.backup.max_age_hours,
);
collectors.push(Box::new(backup_collector));
info!("Backup collector initialized");
collectors.push(TimedCollector {
collector: Box::new(backup_collector),
interval: Duration::from_secs(config.backup.interval_seconds),
last_collection: None,
name: "Backup".to_string(),
});
info!("Backup collector initialized with {}s interval", config.backup.interval_seconds);
}
if config.nixos.enabled {
let nixos_collector = NixOSCollector::new(config.nixos.clone());
collectors.push(Box::new(nixos_collector));
info!("NixOS collector initialized");
collectors.push(TimedCollector {
collector: Box::new(nixos_collector),
interval: Duration::from_secs(config.nixos.interval_seconds),
last_collection: None,
name: "NixOS".to_string(),
});
info!("NixOS collector initialized with {}s interval", config.nixos.interval_seconds);
}
}
@@ -118,24 +182,61 @@ impl MetricCollectionManager {
/// Force collection from ALL collectors immediately (used at startup)
pub async fn collect_all_metrics_force(&mut self) -> Result<Vec<Metric>> {
self.collect_all_metrics().await
}
/// Collect metrics from all collectors
pub async fn collect_all_metrics(&mut self) -> Result<Vec<Metric>> {
let mut all_metrics = Vec::new();
let now = Instant::now();
for collector in &self.collectors {
match collector.collect(&mut self.status_tracker).await {
for timed_collector in &mut self.collectors {
match timed_collector.collector.collect(&mut self.status_tracker).await {
Ok(metrics) => {
let metric_count = metrics.len();
all_metrics.extend(metrics);
timed_collector.last_collection = Some(now);
debug!("Force collected {} metrics from {}", metric_count, timed_collector.name);
}
Err(e) => {
error!("Collector failed: {}", e);
error!("Collector {} failed: {}", timed_collector.name, e);
}
}
}
Ok(all_metrics)
}
/// Collect metrics from collectors whose intervals have elapsed
pub async fn collect_metrics_timed(&mut self) -> Result<Vec<Metric>> {
let mut all_metrics = Vec::new();
let now = Instant::now();
for timed_collector in &mut self.collectors {
let should_collect = match timed_collector.last_collection {
None => true, // First collection
Some(last_time) => now.duration_since(last_time) >= timed_collector.interval,
};
if should_collect {
match timed_collector.collector.collect(&mut self.status_tracker).await {
Ok(metrics) => {
let metric_count = metrics.len();
all_metrics.extend(metrics);
timed_collector.last_collection = Some(now);
debug!(
"Collected {} metrics from {} ({}s interval)",
metric_count,
timed_collector.name,
timed_collector.interval.as_secs()
);
}
Err(e) => {
error!("Collector {} failed: {}", timed_collector.name, e);
}
}
}
}
Ok(all_metrics)
}
/// Collect metrics from all collectors (legacy method for compatibility)
pub async fn collect_all_metrics(&mut self) -> Result<Vec<Metric>> {
self.collect_metrics_timed().await
}
}