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
|
// Determine status and description based on latency and health
|
||||||
let (site_status, site_description) = match (latency, is_healthy) {
|
let (site_status, site_description) = match (latency, is_healthy) {
|
||||||
(Some(_ms), true) => (ServiceStatus::Running, None),
|
(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()])),
|
(None, _) => (ServiceStatus::Stopped, Some(vec!["unreachable".to_string()])),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -301,6 +301,10 @@ impl Collector for SystemCollector {
|
|||||||
|
|
||||||
// Get logged-in users (optional)
|
// Get logged-in users (optional)
|
||||||
let logged_in_users = self.get_logged_in_users().await;
|
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!({
|
let mut system_metrics = json!({
|
||||||
"summary": {
|
"summary": {
|
||||||
@ -332,6 +336,14 @@ impl Collector for SystemCollector {
|
|||||||
system_metrics["summary"]["logged_in_users"] = json!(users);
|
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}%",
|
debug!("System metrics collected: CPU load {:.2}, Memory {:.1}%",
|
||||||
cpu_load_5, memory_usage_percent);
|
cpu_load_5, memory_usage_percent);
|
||||||
|
|
||||||
@ -340,4 +352,58 @@ impl Collector for SystemCollector {
|
|||||||
data: system_metrics,
|
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>>,
|
pub cpu_cstate: Option<Vec<String>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub logged_in_users: Option<Vec<String>>,
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@ -109,10 +109,10 @@ fn render_metrics(
|
|||||||
// Add latency information for nginx sites if available
|
// Add latency information for nginx sites if available
|
||||||
let service_name_with_latency = if let Some(parent) = &svc.sub_service {
|
let service_name_with_latency = if let Some(parent) = &svc.sub_service {
|
||||||
if parent == "nginx" {
|
if parent == "nginx" {
|
||||||
match (&svc.latency_ms, &svc.description) {
|
match &svc.latency_ms {
|
||||||
(Some(latency), _) => format!("{} {:.0}ms", svc.name, latency),
|
Some(latency) if *latency >= 5000.0 => format!("{} unreachable", svc.name), // Timeout (5s+)
|
||||||
(None, Some(desc)) if !desc.is_empty() => format!("{} {}", svc.name, desc[0]),
|
Some(latency) => format!("{} {:.0}ms", svc.name, latency),
|
||||||
_ => svc.name.clone(),
|
None => format!("{} unreachable", svc.name), // Connection failed
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
svc.name.clone()
|
svc.name.clone()
|
||||||
|
|||||||
@ -90,6 +90,16 @@ fn render_metrics(
|
|||||||
description_lines.push(user_line);
|
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(
|
system_dataset.add_row(
|
||||||
overall_status.clone(),
|
overall_status.clone(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user