use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex, RwLock};
use super::{BenchmarkResult, MetricType, Runtime};
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct RealTimeMonitor {
pub metrics_collector: Arc<RwLock<MetricsCollector>>,
pub config: MonitorConfig,
}
impl RealTimeMonitor {
pub fn new(config: MonitorConfig) -> Self {
Self {
metrics_collector: Arc::new(Mutex::new(MetricsCollector::new()))
config,
}
}
pub async fn start(&self) -> Result<(), super::BenchmarkError> {
let metrics_collector: _ = self.metrics_collector.clone();
let interval: _ = self.config.collection_interval;
let mut interval_timer = tokio::time::interval(interval);
tokio::spawn(async move {
loop {
interval_timer.tick().await;
let mut collector = metrics_collector.write().await;
collector.collect_system_metrics().await;
}
});
Ok(())
}
pub async fn record_benchmark_result(&self, result: &BenchmarkResult) {
let mut collector = self.metrics_collector.write().await;
collector.record_benchmark_result(result).await;
}
pub async fn get_current_metrics(&self) -> CurrentMetrics {
let collector: _ = self.metrics_collector.read().await;
collector.get_current_metrics().await
}
}
#[derive(Debug, Clone)]
pub struct MonitorConfig {
pub collection_interval: Duration,
pub max_history_size: usize,
pub enable_detailed_metrics: bool,
}
impl Default for MonitorConfig {
fn default() -> Self {
Self {
collection_interval: Duration::from_millis(100),
max_history_size: 1000,
enable_detailed_metrics: true,
}
}
}
impl MonitorConfig {
pub fn new() -> Self {
Self::default()
}
pub fn collection_interval(mut self, interval: Duration) -> Self {
self.collection_interval = interval;
self
}
pub fn max_history_size(mut self, size: usize) -> Self {
self.max_history_size = size;
self
}
pub fn enable_detailed_metrics(mut self, enable: bool) -> Self {
self.enable_detailed_metrics = enable;
self
}
}
#[derive(Debug, Default)]
pub struct MetricsCollector {
pub system_metrics: SystemMetrics,
pub benchmark_metrics: Vec<BenchmarkMetrics>,
pub history: Vec<HistoricalMetrics>,
pub last_collection: Instant,
}
impl MetricsCollector {
pub fn new() -> Self {
Self::default()
}
pub async fn collect_system_metrics(&mut self) {
self.system_metrics.cpu_usage = self.get_cpu_usage().await;
self.system_metrics.memory_usage = self.get_memory_usage().await;
self.system_metrics.disk_io = self.get_disk_io().await;
self.system_metrics.network_io = self.get_network_io().await;
self.last_collection = Instant::now();
}
pub async fn record_benchmark_result(&mut self, result: &BenchmarkResult) {
let metrics: _ = BenchmarkMetrics {
test_name: result.name.clone(),
runtime: result.runtime,
execution_time: result.average_duration(),
throughput: result.throughput(),
memory_usage: result.metrics.memory_usage_bytes,
cpu_usage: result.metrics.cpu_usage_percent,
timestamp: Instant::now(),
};
self.benchmark_metrics.push(metrics);
if self.benchmark_metrics.len() > 1000 {
self.benchmark_metrics.remove(0);
}
}
pub async fn get_current_metrics(&self) -> CurrentMetrics {
CurrentMetrics {
system: self.system_metrics.clone(),
latest_benchmarks: self.benchmark_metrics
.iter()
.rev()
.take(10)
.cloned()
.collect(),
collection_time: self.last_collection.elapsed(),
}
}
async fn get_cpu_usage(&self) -> f64 {
50.0 }
async fn get_memory_usage(&self) -> MemoryUsage {
MemoryUsage {
total: 8 * 1024 * 1024 * 1024, used: 4 * 1024 * 1024 * 1024, available: 4 * 1024 * 1024 * 1024, }
}
async fn get_disk_io(&self) -> DiskIo {
DiskIo {
read_bytes: 1024 * 1024,
write_bytes: 512 * 1024,
read_ops: 100,
write_ops: 50,
}
}
async fn get_network_io(&self) -> NetworkIo {
NetworkIo {
bytes_sent: 1024 * 1024,
bytes_received: 2 * 1024 * 1024,
packets_sent: 1000,
packets_received: 2000,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CurrentMetrics {
pub system: SystemMetrics,
pub latest_benchmarks: Vec<BenchmarkMetrics>,
pub collection_time: Duration,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SystemMetrics {
pub cpu_usage: f64,
pub memory_usage: MemoryUsage,
pub disk_io: DiskIo,
pub network_io: NetworkIo,
pub load_average: (f64, f64, f64),
pub process_count: u32,
pub thread_count: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryUsage {
pub total: u64,
pub used: u64,
pub available: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiskIo {
pub read_bytes: u64,
pub write_bytes: u64,
pub read_ops: u64,
pub write_ops: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkIo {
pub bytes_sent: u64,
pub bytes_received: u64,
pub packets_sent: u64,
pub packets_received: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkMetrics {
pub test_name: String,
pub runtime: Runtime,
pub execution_time: Duration,
pub throughput: f64,
pub memory_usage: u64,
pub cpu_usage: f64,
pub timestamp: Instant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoricalMetrics {
pub timestamp: Instant,
pub system_metrics: SystemMetrics,
pub benchmark_metrics: Vec<BenchmarkMetrics>,
}
#[derive(Debug)]
pub struct PerformanceDashboard {
pub monitor: RealTimeMonitor,
}
impl PerformanceDashboard {
pub fn new(config: MonitorConfig) -> Self {
Self {
monitor: RealTimeMonitor::new(config),
}
}
pub async fn start(&self) -> Result<(), super::BenchmarkError> {
self.monitor.start().await?;
Ok(())
}
pub async fn generate_html_report(&self) -> Result<String, super::BenchmarkError> {
let metrics: _ = self.monitor.get_current_metrics().await;
let html: _ = format!(r#"
<!DOCTYPE html>
<html>
<head>
<title>Amber Performance Dashboard</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
.metric {{ margin: 10px 0; padding: 10px; border: 1px solid #ccc; }}
.metric h3 {{ margin: 0 0 10px 0; }}
.value {{ font-size: 1.2em; font-weight: bold; }}
</style>
</head>
<body>
<h1>Amber Performance Dashboard</h1>
<div class="metric">
<h3>CPU Usage</h3>
<div class="value">{:.2}%</div>
</div>
<div class="metric">
<h3>Memory Usage</h3>
<div class="value">{} / {}</div>
</div>
<div class="metric">
<h3>Collection Time</h3>
<div class="value">{{:?}}</div>
</div>
<h2>Latest Benchmarks</h2>
<table>
<tr>
<th>Test Name</th>
<th>Runtime</th>
<th>Execution Time</th>
<th>Throughput</th>
</tr>
{}
</table>
</body>
</html>
"#,
metrics.system.cpu_usage,
format_bytes(metrics.system.memory_usage.used),
format_bytes(metrics.system.memory_usage.total),
metrics.collection_time,
metrics.latest_benchmarks
.iter()
.map(|bm| format!(
"<tr><td>{}</td><td>{}</td><td>{{:?}}</td><td>{:.2}</td></tr>",
bm.test_name,
bm.runtime,
bm.execution_time,
bm.throughput
))
.collect::<Vec<_>()
.join("\n")
);
Ok(html)
}
}
fn format_bytes(bytes: u64) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
let mut size = bytes as f64;
let mut unit_index = 0;
while size >= 1024.0 && unit_index < UNITS.len() - 1 {
size /= 1024.0;
unit_index += 1;
}
format!("{:.2} {}", size, UNITS[unit_index])
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_real_time_monitor() {
let config: _ = MonitorConfig::new();
let monitor: _ = RealTimeMonitor::new(config);
let mut result = super::super::result::BenchmarkResult::new("test", Runtime::Amber);
result.add_iteration(Duration::from_millis(100));
result.finish();
monitor.record_benchmark_result(&result).await;
let metrics: _ = monitor.get_current_metrics().await;
println!("Metrics: {:?}", metrics);
}
#[tokio::test]
async fn test_performance_dashboard() {
let config: _ = MonitorConfig::new();
let dashboard: _ = PerformanceDashboard::new(config);
let html: _ = dashboard.generate_html_report().await.unwrap();
println!("HTML Report:\n{}", html);
}
}