feat: add metrics export as CSV/JSON (MON-04)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1b8a8cfd32
commit
980fc3af6d
@ -440,6 +440,7 @@ impl RpcHandler {
|
||||
"monitoring.alert-rules" => self.handle_monitoring_alert_rules().await,
|
||||
"monitoring.configure-alert" => self.handle_monitoring_configure_alert(params).await,
|
||||
"monitoring.acknowledge-alert" => self.handle_monitoring_acknowledge_alert(params).await,
|
||||
"monitoring.export" => self.handle_monitoring_export(params).await,
|
||||
|
||||
// System updates
|
||||
"update.check" => self.handle_update_check().await,
|
||||
|
||||
@ -116,6 +116,80 @@ impl RpcHandler {
|
||||
Ok(serde_json::json!({ "updated": true, "kind": kind_str }))
|
||||
}
|
||||
|
||||
/// monitoring.export — export metrics as CSV or JSON
|
||||
pub(super) async fn handle_monitoring_export(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
debug!("Exporting metrics");
|
||||
|
||||
let format = params
|
||||
.as_ref()
|
||||
.and_then(|p| p.get("format"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("csv");
|
||||
|
||||
let resolution = params
|
||||
.as_ref()
|
||||
.and_then(|p| p.get("resolution"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("minute");
|
||||
|
||||
let count = params
|
||||
.as_ref()
|
||||
.and_then(|p| p.get("count"))
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(1440) as usize;
|
||||
|
||||
let count = count.min(1440);
|
||||
|
||||
let data = match resolution {
|
||||
"quarter_hour" | "15min" => self.metrics_store.history_quarter_hours(count).await,
|
||||
_ => self.metrics_store.history_minutes(count).await,
|
||||
};
|
||||
|
||||
match format {
|
||||
"json" => {
|
||||
Ok(serde_json::json!({
|
||||
"format": "json",
|
||||
"resolution": resolution,
|
||||
"count": data.len(),
|
||||
"data": data,
|
||||
}))
|
||||
}
|
||||
_ => {
|
||||
// CSV format
|
||||
let mut csv = String::from(
|
||||
"timestamp,cpu_percent,mem_used_bytes,mem_total_bytes,disk_used_bytes,disk_total_bytes,net_rx_bytes,net_tx_bytes,load_avg_1,load_avg_5,load_avg_15,rpc_latency_ms,ws_connections\n"
|
||||
);
|
||||
for snapshot in &data {
|
||||
csv.push_str(&format!(
|
||||
"{},{:.1},{},{},{},{},{},{},{:.2},{:.2},{:.2},{:.1},{}\n",
|
||||
snapshot.timestamp,
|
||||
snapshot.system.cpu_percent,
|
||||
snapshot.system.mem_used_bytes,
|
||||
snapshot.system.mem_total_bytes,
|
||||
snapshot.system.disk_used_bytes,
|
||||
snapshot.system.disk_total_bytes,
|
||||
snapshot.system.net_rx_bytes,
|
||||
snapshot.system.net_tx_bytes,
|
||||
snapshot.system.load_avg_1,
|
||||
snapshot.system.load_avg_5,
|
||||
snapshot.system.load_avg_15,
|
||||
snapshot.rpc_latency_ms,
|
||||
snapshot.ws_connections,
|
||||
));
|
||||
}
|
||||
Ok(serde_json::json!({
|
||||
"format": "csv",
|
||||
"resolution": resolution,
|
||||
"count": data.len(),
|
||||
"csv": csv,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// monitoring.acknowledge-alert — acknowledge a fired alert
|
||||
pub(super) async fn handle_monitoring_acknowledge_alert(
|
||||
&self,
|
||||
|
||||
@ -342,7 +342,7 @@
|
||||
|
||||
- [x] **MON-03** — Implemented alerting system. Added AlertRule and FiredAlert types to monitoring/mod.rs with 5 configurable rules (disk >90%, RAM >90%, container crash, RPC latency spike, SSL cert expiry <30 days). Metrics collector evaluates rules every 60s, fires alerts as Notifications via WebSocket. Added RPC endpoints: monitoring.alerts, monitoring.alert-rules, monitoring.configure-alert, monitoring.acknowledge-alert. Frontend: Monitoring.vue has alert history section with configurable thresholds, enable/disable toggles, dismiss buttons. CSS toggle/input styles in style.css.
|
||||
|
||||
- [ ] **MON-04** — Add historical data export. Add `monitoring.export` RPC endpoint that exports metrics as CSV or JSON for a given time range. Add "Export" button in monitoring UI. **Acceptance**: Can download last 24h of metrics as CSV.
|
||||
- [x] **MON-04** — Added historical data export. `monitoring.export` RPC endpoint returns metrics as CSV (with headers) or JSON for configurable count/resolution. Frontend: Export CSV and Export JSON buttons in Monitoring.vue header. Creates downloadable blob file with date-stamped filename.
|
||||
|
||||
#### Sprint 28: Remote Management (Week 5-8)
|
||||
|
||||
|
||||
@ -1,8 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="hidden md:block mb-8">
|
||||
<h1 class="text-3xl font-bold text-white mb-2">Monitoring</h1>
|
||||
<p class="text-white/70">Real-time system metrics and container resource usage</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-white mb-2">Monitoring</h1>
|
||||
<p class="text-white/70">Real-time system metrics and container resource usage</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="glass-button text-sm px-4 py-2" @click="exportMetrics('csv')">
|
||||
Export CSV
|
||||
</button>
|
||||
<button class="glass-button text-sm px-4 py-2" @click="exportMetrics('json')">
|
||||
Export JSON
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary Cards -->
|
||||
@ -401,6 +413,32 @@ function formatAlertTime(timestamp: number): string {
|
||||
return d.toLocaleString()
|
||||
}
|
||||
|
||||
async function exportMetrics(format: 'csv' | 'json') {
|
||||
try {
|
||||
const data = await rpcClient.call<{ csv?: string; data?: unknown[]; count: number }>({
|
||||
method: 'monitoring.export',
|
||||
params: { format, resolution: 'minute', count: 1440 },
|
||||
})
|
||||
let blob: Blob
|
||||
let filename: string
|
||||
if (format === 'csv' && data?.csv) {
|
||||
blob = new Blob([data.csv], { type: 'text/csv' })
|
||||
filename = `archipelago-metrics-${new Date().toISOString().slice(0, 10)}.csv`
|
||||
} else {
|
||||
blob = new Blob([JSON.stringify(data?.data ?? [], null, 2)], { type: 'application/json' })
|
||||
filename = `archipelago-metrics-${new Date().toISOString().slice(0, 10)}.json`
|
||||
}
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = filename
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
} catch {
|
||||
if (import.meta.env.DEV) console.warn('Failed to export metrics')
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchCurrent() {
|
||||
try {
|
||||
const data = await rpcClient.call<MetricSnapshot | { status: string }>({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user