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

161
shared/src/metrics.rs Normal file
View File

@@ -0,0 +1,161 @@
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
/// Individual metric with value, status, and metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metric {
pub name: String,
pub value: MetricValue,
pub status: Status,
pub timestamp: u64,
pub description: Option<String>,
pub unit: Option<String>,
}
impl Metric {
pub fn new(name: String, value: MetricValue, status: Status) -> Self {
Self {
name,
value,
status,
timestamp: Utc::now().timestamp() as u64,
description: None,
unit: None,
}
}
pub fn with_description(mut self, description: String) -> Self {
self.description = Some(description);
self
}
pub fn with_unit(mut self, unit: String) -> Self {
self.unit = Some(unit);
self
}
}
/// Typed metric values
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MetricValue {
Float(f32),
Integer(i64),
String(String),
Boolean(bool),
}
impl MetricValue {
pub fn as_f32(&self) -> Option<f32> {
match self {
MetricValue::Float(f) => Some(*f),
MetricValue::Integer(i) => Some(*i as f32),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
MetricValue::Integer(i) => Some(*i),
MetricValue::Float(f) => Some(*f as i64),
_ => None,
}
}
pub fn as_string(&self) -> String {
match self {
MetricValue::String(s) => s.clone(),
MetricValue::Float(f) => f.to_string(),
MetricValue::Integer(i) => i.to_string(),
MetricValue::Boolean(b) => b.to_string(),
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
MetricValue::Boolean(b) => Some(*b),
_ => None,
}
}
}
/// Health status for metrics
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum Status {
Ok,
Warning,
Critical,
Unknown,
}
impl Status {
/// Aggregate multiple statuses - returns the worst status
pub fn aggregate(statuses: &[Status]) -> Status {
statuses.iter().max().copied().unwrap_or(Status::Unknown)
}
}
impl Default for Status {
fn default() -> Self {
Status::Unknown
}
}
/// Metric name registry - constants for all metric names
pub mod registry {
// CPU metrics
pub const CPU_LOAD_1MIN: &str = "cpu_load_1min";
pub const CPU_LOAD_5MIN: &str = "cpu_load_5min";
pub const CPU_LOAD_15MIN: &str = "cpu_load_15min";
pub const CPU_TEMPERATURE_CELSIUS: &str = "cpu_temperature_celsius";
pub const CPU_FREQUENCY_MHZ: &str = "cpu_frequency_mhz";
pub const CPU_USAGE_PERCENT: &str = "cpu_usage_percent";
// Memory metrics
pub const MEMORY_USAGE_PERCENT: &str = "memory_usage_percent";
pub const MEMORY_TOTAL_GB: &str = "memory_total_gb";
pub const MEMORY_USED_GB: &str = "memory_used_gb";
pub const MEMORY_AVAILABLE_GB: &str = "memory_available_gb";
pub const MEMORY_SWAP_TOTAL_GB: &str = "memory_swap_total_gb";
pub const MEMORY_SWAP_USED_GB: &str = "memory_swap_used_gb";
// Disk metrics (template - actual names include device)
pub const DISK_USAGE_PERCENT_TEMPLATE: &str = "disk_{device}_usage_percent";
pub const DISK_TEMPERATURE_CELSIUS_TEMPLATE: &str = "disk_{device}_temperature_celsius";
pub const DISK_WEAR_PERCENT_TEMPLATE: &str = "disk_{device}_wear_percent";
pub const DISK_SPARE_PERCENT_TEMPLATE: &str = "disk_{device}_spare_percent";
pub const DISK_HOURS_TEMPLATE: &str = "disk_{device}_hours";
pub const DISK_CAPACITY_GB_TEMPLATE: &str = "disk_{device}_capacity_gb";
// Service metrics (template - actual names include service)
pub const SERVICE_STATUS_TEMPLATE: &str = "service_{name}_status";
pub const SERVICE_MEMORY_MB_TEMPLATE: &str = "service_{name}_memory_mb";
pub const SERVICE_CPU_PERCENT_TEMPLATE: &str = "service_{name}_cpu_percent";
// Backup metrics
pub const BACKUP_STATUS: &str = "backup_status";
pub const BACKUP_LAST_RUN_TIMESTAMP: &str = "backup_last_run_timestamp";
pub const BACKUP_SIZE_GB: &str = "backup_size_gb";
pub const BACKUP_DURATION_MINUTES: &str = "backup_duration_minutes";
pub const BACKUP_NEXT_SCHEDULED_TIMESTAMP: &str = "backup_next_scheduled_timestamp";
// Network metrics (template - actual names include interface)
pub const NETWORK_RX_BYTES_TEMPLATE: &str = "network_{interface}_rx_bytes";
pub const NETWORK_TX_BYTES_TEMPLATE: &str = "network_{interface}_tx_bytes";
pub const NETWORK_RX_PACKETS_TEMPLATE: &str = "network_{interface}_rx_packets";
pub const NETWORK_TX_PACKETS_TEMPLATE: &str = "network_{interface}_tx_packets";
/// Generate disk metric name from template
pub fn disk_metric(template: &str, device: &str) -> String {
template.replace("{device}", device)
}
/// Generate service metric name from template
pub fn service_metric(template: &str, name: &str) -> String {
template.replace("{name}", name)
}
/// Generate network metric name from template
pub fn network_metric(template: &str, interface: &str) -> String {
template.replace("{interface}", interface)
}
}