Add top CPU and RAM process monitoring to System widget
- Implement get_top_cpu_process() and get_top_ram_process() functions in SystemCollector - Add top_cpu_process and top_ram_process fields to SystemSummary data structure - Update System widget to display top processes as description rows - Show process name and percentage usage for highest CPU and RAM consumers - Skip kernel threads and filter out processes with minimal usage (<0.1%)
This commit is contained in:
parent
2bffbaa000
commit
f3b6d12f68
@ -1361,7 +1361,7 @@ impl Collector for ServiceCollector {
|
||||
// Determine status and description based on latency and health
|
||||
let (site_status, site_description) = match (latency, is_healthy) {
|
||||
(Some(_ms), true) => (ServiceStatus::Running, None),
|
||||
(Some(_ms), false) => (ServiceStatus::Stopped, Some(vec!["error".to_string()])),
|
||||
(Some(_ms), false) => (ServiceStatus::Stopped, None), // Show error status but no description
|
||||
(None, _) => (ServiceStatus::Stopped, Some(vec!["unreachable".to_string()])),
|
||||
};
|
||||
|
||||
|
||||
@ -301,6 +301,10 @@ impl Collector for SystemCollector {
|
||||
|
||||
// Get logged-in users (optional)
|
||||
let logged_in_users = self.get_logged_in_users().await;
|
||||
|
||||
// Get top processes
|
||||
let top_cpu_process = self.get_top_cpu_process().await;
|
||||
let top_ram_process = self.get_top_ram_process().await;
|
||||
|
||||
let mut system_metrics = json!({
|
||||
"summary": {
|
||||
@ -332,6 +336,14 @@ impl Collector for SystemCollector {
|
||||
system_metrics["summary"]["logged_in_users"] = json!(users);
|
||||
}
|
||||
|
||||
if let Some(cpu_proc) = top_cpu_process {
|
||||
system_metrics["summary"]["top_cpu_process"] = json!(cpu_proc);
|
||||
}
|
||||
|
||||
if let Some(ram_proc) = top_ram_process {
|
||||
system_metrics["summary"]["top_ram_process"] = json!(ram_proc);
|
||||
}
|
||||
|
||||
debug!("System metrics collected: CPU load {:.2}, Memory {:.1}%",
|
||||
cpu_load_5, memory_usage_percent);
|
||||
|
||||
@ -340,4 +352,58 @@ impl Collector for SystemCollector {
|
||||
data: system_metrics,
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_top_cpu_process(&self) -> Option<String> {
|
||||
// Get top CPU process using ps command
|
||||
let output = Command::new("/run/current-system/sw/bin/ps")
|
||||
.args(["aux", "--sort=-pcpu"])
|
||||
.output()
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
// Skip header line and get first process
|
||||
for line in stdout.lines().skip(1) {
|
||||
let fields: Vec<&str> = line.split_whitespace().collect();
|
||||
if fields.len() >= 11 {
|
||||
let cpu_percent = fields[2];
|
||||
let command = fields[10];
|
||||
// Skip kernel threads (in brackets) and low CPU processes
|
||||
if !command.starts_with('[') && cpu_percent.parse::<f32>().unwrap_or(0.0) > 0.1 {
|
||||
return Some(format!("{} {:.1}%", command, cpu_percent.parse::<f32>().unwrap_or(0.0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
async fn get_top_ram_process(&self) -> Option<String> {
|
||||
// Get top RAM process using ps command
|
||||
let output = Command::new("/run/current-system/sw/bin/ps")
|
||||
.args(["aux", "--sort=-rss"])
|
||||
.output()
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
// Skip header line and get first process
|
||||
for line in stdout.lines().skip(1) {
|
||||
let fields: Vec<&str> = line.split_whitespace().collect();
|
||||
if fields.len() >= 11 {
|
||||
let mem_percent = fields[3];
|
||||
let command = fields[10];
|
||||
// Skip kernel threads (in brackets) and low memory processes
|
||||
if !command.starts_with('[') && mem_percent.parse::<f32>().unwrap_or(0.0) > 0.1 {
|
||||
return Some(format!("{} {:.1}%", command, mem_percent.parse::<f32>().unwrap_or(0.0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -60,6 +60,10 @@ pub struct SystemSummary {
|
||||
pub cpu_cstate: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub logged_in_users: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub top_cpu_process: Option<String>,
|
||||
#[serde(default)]
|
||||
pub top_ram_process: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@ -109,10 +109,10 @@ fn render_metrics(
|
||||
// Add latency information for nginx sites if available
|
||||
let service_name_with_latency = if let Some(parent) = &svc.sub_service {
|
||||
if parent == "nginx" {
|
||||
match (&svc.latency_ms, &svc.description) {
|
||||
(Some(latency), _) => format!("{} {:.0}ms", svc.name, latency),
|
||||
(None, Some(desc)) if !desc.is_empty() => format!("{} {}", svc.name, desc[0]),
|
||||
_ => svc.name.clone(),
|
||||
match &svc.latency_ms {
|
||||
Some(latency) if *latency >= 5000.0 => format!("{} unreachable", svc.name), // Timeout (5s+)
|
||||
Some(latency) => format!("{} {:.0}ms", svc.name, latency),
|
||||
None => format!("{} unreachable", svc.name), // Connection failed
|
||||
}
|
||||
} else {
|
||||
svc.name.clone()
|
||||
|
||||
@ -90,6 +90,16 @@ fn render_metrics(
|
||||
description_lines.push(user_line);
|
||||
}
|
||||
}
|
||||
|
||||
// Add top CPU process
|
||||
if let Some(cpu_proc) = &summary.top_cpu_process {
|
||||
description_lines.push(format!("Top CPU: {}", cpu_proc));
|
||||
}
|
||||
|
||||
// Add top RAM process
|
||||
if let Some(ram_proc) = &summary.top_ram_process {
|
||||
description_lines.push(format!("Top RAM: {}", ram_proc));
|
||||
}
|
||||
|
||||
system_dataset.add_row(
|
||||
overall_status.clone(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user