Implement real-time process monitoring and fix UI hardcoded data

This commit addresses several key issues identified during development:

Major Changes:
- Replace hardcoded top CPU/RAM process display with real system data
- Add intelligent process monitoring to CpuCollector using ps command
- Fix disk metrics permission issues in systemd collector
- Optimize service collection to focus on status, memory, and disk only
- Update dashboard widgets to display live process information

Process Monitoring Implementation:
- Added collect_top_cpu_process() and collect_top_ram_process() methods
- Implemented ps-based monitoring with accurate CPU percentages
- Added filtering to prevent self-monitoring artifacts (ps commands)
- Enhanced error handling and validation for process data
- Dashboard now shows realistic values like "claude (PID 2974) 11.0%"

Service Collection Optimization:
- Removed CPU monitoring from systemd collector for efficiency
- Enhanced service directory permission error logging
- Simplified services widget to show essential metrics only
- Fixed service-to-directory mapping accuracy

UI and Dashboard Improvements:
- Reorganized dashboard layout with btop-inspired multi-panel design
- Updated system panel to include real top CPU/RAM process display
- Enhanced widget formatting and data presentation
- Removed placeholder/hardcoded data throughout the interface

Technical Details:
- Updated agent/src/collectors/cpu.rs with process monitoring
- Modified dashboard/src/ui/mod.rs for real-time process display
- Enhanced systemd collector error handling and disk metrics
- Updated CLAUDE.md documentation with implementation details
This commit is contained in:
2025-10-16 23:55:05 +02:00
parent 7a664ef0fb
commit 8a36472a3d
81 changed files with 7702 additions and 9608 deletions

View File

@@ -0,0 +1,142 @@
use cm_dashboard_shared::{Metric, Status};
use std::collections::HashMap;
use std::time::{Duration, Instant};
use tracing::{debug, info};
pub mod store;
pub mod subscription;
pub use store::MetricStore;
pub use subscription::SubscriptionManager;
/// Widget types that can subscribe to metrics
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum WidgetType {
Cpu,
Memory,
Storage,
Services,
Backup,
Hosts,
Alerts,
}
/// Metric subscription entry
#[derive(Debug, Clone)]
pub struct MetricSubscription {
pub widget_type: WidgetType,
pub metric_names: Vec<String>,
}
/// Historical metric data point
#[derive(Debug, Clone)]
pub struct MetricDataPoint {
pub metric: Metric,
pub received_at: Instant,
}
/// Metric filtering and selection utilities
pub mod filter {
use super::*;
/// Filter metrics by widget type subscription
pub fn filter_metrics_for_widget<'a>(
metrics: &'a [Metric],
subscriptions: &[String],
) -> Vec<&'a Metric> {
metrics
.iter()
.filter(|metric| subscriptions.contains(&metric.name))
.collect()
}
/// Get metrics by pattern matching
pub fn filter_metrics_by_pattern<'a>(
metrics: &'a [Metric],
pattern: &str,
) -> Vec<&'a Metric> {
if pattern.is_empty() {
return metrics.iter().collect();
}
metrics
.iter()
.filter(|metric| metric.name.contains(pattern))
.collect()
}
/// Aggregate status from multiple metrics
pub fn aggregate_widget_status(metrics: &[&Metric]) -> Status {
if metrics.is_empty() {
return Status::Unknown;
}
let statuses: Vec<Status> = metrics.iter().map(|m| m.status).collect();
Status::aggregate(&statuses)
}
}
/// Widget metric subscription definitions
pub mod subscriptions {
/// CPU widget metric subscriptions
pub const CPU_WIDGET_METRICS: &[&str] = &[
"cpu_load_1min",
"cpu_load_5min",
"cpu_load_15min",
"cpu_temperature_celsius",
"cpu_frequency_mhz",
];
/// Memory widget metric subscriptions
pub const MEMORY_WIDGET_METRICS: &[&str] = &[
"memory_usage_percent",
"memory_total_gb",
"memory_used_gb",
"memory_available_gb",
"memory_swap_total_gb",
"memory_swap_used_gb",
"disk_tmp_size_mb",
"disk_tmp_total_mb",
"disk_tmp_usage_percent",
];
/// Storage widget metric subscriptions
pub const STORAGE_WIDGET_METRICS: &[&str] = &[
"disk_nvme0_temperature_celsius",
"disk_nvme0_wear_percent",
"disk_nvme0_spare_percent",
"disk_nvme0_hours",
"disk_nvme0_capacity_gb",
"disk_nvme0_usage_gb",
"disk_nvme0_usage_percent",
];
/// Services widget metric subscriptions
/// Note: Individual service metrics are dynamically discovered
/// Pattern: "service_{name}_status" and "service_{name}_memory_mb"
pub const SERVICES_WIDGET_METRICS: &[&str] = &[
// Individual service metrics will be matched by pattern in the widget
// e.g., "service_sshd_status", "service_nginx_status", etc.
];
/// Backup widget metric subscriptions
pub const BACKUP_WIDGET_METRICS: &[&str] = &[
"backup_status",
"backup_last_run_timestamp",
"backup_size_gb",
"backup_duration_minutes",
];
/// Get all metric subscriptions for a widget type
pub fn get_widget_subscriptions(widget_type: super::WidgetType) -> &'static [&'static str] {
match widget_type {
super::WidgetType::Cpu => CPU_WIDGET_METRICS,
super::WidgetType::Memory => MEMORY_WIDGET_METRICS,
super::WidgetType::Storage => STORAGE_WIDGET_METRICS,
super::WidgetType::Services => SERVICES_WIDGET_METRICS,
super::WidgetType::Backup => BACKUP_WIDGET_METRICS,
super::WidgetType::Hosts => &[], // Hosts widget doesn't subscribe to specific metrics
super::WidgetType::Alerts => &[], // Alerts widget aggregates from all metrics
}
}
}