Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/modules/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
pub mod cpu;
pub mod memory;
pub mod disk;
pub mod memory;
pub mod network;
pub mod process;
pub mod system;
60 changes: 60 additions & 0 deletions src/modules/network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crate::core::{MetricCollector, MetricData, MetricValue};
use sysinfo::Networks;
use std::collections::HashMap;

pub struct NetworkCollector;

impl NetworkCollector {
pub fn new() -> Self {
NetworkCollector
}
}

impl MetricCollector for NetworkCollector {
fn collect(&self) -> Result<MetricData, Box<dyn std::error::Error>> {
let networks = Networks::new_with_refreshed_list();

let mut total_rx: u64 = 0;
let mut total_tx: u64 = 0;
let mut iface_count: i64 = 0;
let mut iface_details: Vec<MetricValue> = Vec::new();

for (name, data) in networks.iter() {
let rx = data.total_received();
let tx = data.total_transmitted();
total_rx += rx;
total_tx += tx;
iface_count += 1;
iface_details.push(MetricValue::String(
format!("{}: rx={} tx={}", name, format_bytes(rx), format_bytes(tx)),
));
}

let mut metrics = HashMap::new();
metrics.insert("total_received_bytes".to_string(), MetricValue::Integer(total_rx as i64));
metrics.insert("total_transmitted_bytes".to_string(), MetricValue::Integer(total_tx as i64));
metrics.insert("interface_count".to_string(), MetricValue::Integer(iface_count));
metrics.insert("interfaces".to_string(), MetricValue::List(iface_details));

Ok(MetricData {
timestamp: std::time::SystemTime::now(),
metrics,
})
}

fn name(&self) -> &'static str {
"network"
}
}

fn format_bytes(bytes: u64) -> String {
if bytes >= 1_073_741_824 {
format!("{:.2}GB", bytes as f64 / 1_073_741_824.0)
} else if bytes >= 1_048_576 {
format!("{:.2}MB", bytes as f64 / 1_048_576.0)
} else if bytes >= 1024 {
format!("{:.2}KB", bytes as f64 / 1024.0)
} else {
format!("{}B", bytes)
}
}
68 changes: 68 additions & 0 deletions src/modules/process.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::core::{MetricCollector, MetricData, MetricValue};
use sysinfo::{System, RefreshKind, ProcessRefreshKind};
use std::collections::HashMap;

pub struct ProcessCollector;

impl ProcessCollector {
pub fn new() -> Self {
ProcessCollector
}
}

impl MetricCollector for ProcessCollector {
fn collect(&self) -> Result<MetricData, Box<dyn std::error::Error>> {
let sys = System::new_with_specifics(
RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()),
);

let processes = sys.processes();
let total = processes.len() as i64;

let mut by_memory: Vec<_> = processes.values().collect();
by_memory.sort_by(|a, b| b.memory().cmp(&a.memory()));

let top_mem: Vec<MetricValue> = by_memory
.iter()
.take(5)
.map(|p| {
MetricValue::String(format!(
"{} (pid {}) — {:.1}MB",
p.name().to_string_lossy(),
p.pid(),
p.memory() as f64 / 1_048_576.0,
))
})
.collect();

let mut by_cpu: Vec<_> = processes.values().collect();
by_cpu.sort_by(|a, b| b.cpu_usage().partial_cmp(&a.cpu_usage()).unwrap_or(std::cmp::Ordering::Equal));

let top_cpu: Vec<MetricValue> = by_cpu
.iter()
.take(5)
.map(|p| {
MetricValue::String(format!(
"{} (pid {}) — {:.1}%",
p.name().to_string_lossy(),
p.pid(),
p.cpu_usage(),
))
})
.collect();

let mut metrics = HashMap::new();
metrics.insert("total_processes".to_string(), MetricValue::Integer(total));
metrics.insert("top_by_memory".to_string(), MetricValue::List(top_mem));
metrics.insert("top_by_cpu".to_string(), MetricValue::List(top_cpu));

Ok(MetricData {
timestamp: std::time::SystemTime::now(),
metrics,
})
}

fn name(&self) -> &'static str {
"process"
}
}
65 changes: 65 additions & 0 deletions src/modules/system.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::core::{MetricCollector, MetricData, MetricValue};
use sysinfo::System;
use std::collections::HashMap;

pub struct SystemCollector;

impl SystemCollector {
pub fn new() -> Self {
SystemCollector
}
}

impl MetricCollector for SystemCollector {
fn collect(&self) -> Result<MetricData, Box<dyn std::error::Error>> {
let mut metrics = HashMap::new();

if let Some(name) = System::name() {
metrics.insert("os_name".to_string(), MetricValue::String(name));
}
if let Some(version) = System::os_version() {
metrics.insert("os_version".to_string(), MetricValue::String(version));
}
if let Some(kernel) = System::kernel_version() {
metrics.insert("kernel_version".to_string(), MetricValue::String(kernel));
}
if let Some(host) = System::host_name() {
metrics.insert("hostname".to_string(), MetricValue::String(host));
}
if let Some(arch) = System::cpu_arch() {
metrics.insert("arch".to_string(), MetricValue::String(arch));
}

let uptime_secs = System::uptime();
metrics.insert("uptime_seconds".to_string(), MetricValue::Integer(uptime_secs as i64));
metrics.insert("uptime_human".to_string(), MetricValue::String(format_uptime(uptime_secs)));

let load = System::load_average();
metrics.insert("load_1m".to_string(), MetricValue::Float(load.one));
metrics.insert("load_5m".to_string(), MetricValue::Float(load.five));
metrics.insert("load_15m".to_string(), MetricValue::Float(load.fifteen));

Ok(MetricData {
timestamp: std::time::SystemTime::now(),
metrics,
})
}

fn name(&self) -> &'static str {
"system"
}
}

fn format_uptime(secs: u64) -> String {
let days = secs / 86400;
let hours = (secs % 86400) / 3600;
let minutes = (secs % 3600) / 60;

if days > 0 {
format!("{}d {}h {}m", days, hours, minutes)
} else if hours > 0 {
format!("{}h {}m", hours, minutes)
} else {
format!("{}m", minutes)
}
}
Loading