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
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:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cm-dashboard-agent"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -74,7 +74,7 @@ impl Agent {
|
||||
// Separate intervals for collection and transmission
|
||||
let mut collection_interval =
|
||||
interval(Duration::from_secs(self.config.collection_interval_seconds));
|
||||
let mut transmission_interval = interval(Duration::from_secs(1)); // ZMQ broadcast every 1 second
|
||||
let mut transmission_interval = interval(Duration::from_secs(self.config.zmq.transmission_interval_seconds));
|
||||
let mut notification_interval = interval(Duration::from_secs(self.config.status_aggregation.notification_interval_seconds));
|
||||
|
||||
loop {
|
||||
|
||||
@@ -121,25 +121,25 @@ impl Collector for NixOSCollector {
|
||||
let mut metrics = Vec::new();
|
||||
let timestamp = chrono::Utc::now().timestamp() as u64;
|
||||
|
||||
// Collect NixOS build information
|
||||
match self.get_nixos_build_info() {
|
||||
Ok(build_info) => {
|
||||
// Collect NixOS build information (config hash)
|
||||
match self.get_config_hash() {
|
||||
Ok(config_hash) => {
|
||||
metrics.push(Metric {
|
||||
name: "system_nixos_build".to_string(),
|
||||
value: MetricValue::String(build_info),
|
||||
value: MetricValue::String(config_hash),
|
||||
unit: None,
|
||||
description: Some("NixOS build information".to_string()),
|
||||
description: Some("NixOS deployed configuration hash".to_string()),
|
||||
status: Status::Ok,
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to get NixOS build info: {}", e);
|
||||
debug!("Failed to get config hash: {}", e);
|
||||
metrics.push(Metric {
|
||||
name: "system_nixos_build".to_string(),
|
||||
value: MetricValue::String("unknown".to_string()),
|
||||
unit: None,
|
||||
description: Some("NixOS build (failed to detect)".to_string()),
|
||||
description: Some("NixOS config hash (failed to detect)".to_string()),
|
||||
status: Status::Unknown,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ struct ServiceCacheState {
|
||||
nginx_site_metrics: Vec<Metric>,
|
||||
/// Last time nginx sites were checked
|
||||
last_nginx_check_time: Option<Instant>,
|
||||
/// How often to check nginx site latency (30 seconds)
|
||||
/// How often to check nginx site latency (configurable)
|
||||
nginx_check_interval_seconds: u64,
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl SystemdCollector {
|
||||
discovery_interval_seconds: config.interval_seconds,
|
||||
nginx_site_metrics: Vec::new(),
|
||||
last_nginx_check_time: None,
|
||||
nginx_check_interval_seconds: 30, // 30 seconds for nginx sites
|
||||
nginx_check_interval_seconds: config.nginx_check_interval_seconds,
|
||||
}),
|
||||
config,
|
||||
}
|
||||
@@ -615,10 +615,10 @@ impl SystemdCollector {
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
// Create HTTP client with timeouts (similar to legacy implementation)
|
||||
// Create HTTP client with timeouts from configuration
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(Duration::from_secs(10))
|
||||
.connect_timeout(Duration::from_secs(10))
|
||||
.timeout(Duration::from_secs(self.config.http_timeout_seconds))
|
||||
.connect_timeout(Duration::from_secs(self.config.http_connect_timeout_seconds))
|
||||
.redirect(reqwest::redirect::Policy::limited(10))
|
||||
.build()?;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ pub struct ZmqConfig {
|
||||
pub bind_address: String,
|
||||
pub timeout_ms: u64,
|
||||
pub heartbeat_interval_ms: u64,
|
||||
pub transmission_interval_seconds: u64,
|
||||
}
|
||||
|
||||
/// Collector configuration
|
||||
@@ -104,6 +105,9 @@ pub struct SystemdConfig {
|
||||
pub memory_critical_mb: f32,
|
||||
pub service_directories: std::collections::HashMap<String, Vec<String>>,
|
||||
pub host_user_mapping: String,
|
||||
pub nginx_check_interval_seconds: u64,
|
||||
pub http_timeout_seconds: u64,
|
||||
pub http_connect_timeout_seconds: u64,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user