f0a071e83a
- Migration 002 : table metrics_history (index agent_id/timestamp)
- AppState : broadcast channels (metrics_tx, network_tx) + FromRef<SqlitePool>
- GET /api/v1/history/{agent_id}?hours=N : série temporelle, rétention 7 jours
- POST /api/v1/metrics : écrit dans history + broadcast SSE
- GET /api/v1/stream : Server-Sent Events (events metrics + network)
- GET /metrics : endpoint Prometheus text/plain (CPU, RAM, disque, temp, réseau)
- Bump version API 0.2.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
63 lines
2.5 KiB
Rust
63 lines
2.5 KiB
Rust
use axum::{extract::State, http::header, response::IntoResponse};
|
|
use sqlx::SqlitePool;
|
|
|
|
use crate::error::Result;
|
|
|
|
/// Exposition des métriques au format Prometheus text (scraping Grafana/Prometheus).
|
|
///
|
|
/// Endpoint : GET /metrics
|
|
/// Compatible avec prometheus.yml :
|
|
/// scrape_configs:
|
|
/// - job_name: sentinelmesh
|
|
/// static_configs:
|
|
/// - targets: ['sentinelmesh:8080']
|
|
pub async fn metrics(State(db): State<SqlitePool>) -> Result<impl IntoResponse> {
|
|
let rows = sqlx::query_as::<_, (
|
|
String, String,
|
|
Option<f64>, Option<f64>, Option<f64>, Option<f64>,
|
|
Option<i64>, Option<i64>,
|
|
)>(
|
|
"SELECT a.hostname, m.agent_id,
|
|
m.cpu_percent, m.ram_percent, m.disk_percent, m.temperature_c,
|
|
m.net_rx_bps, m.net_tx_bps
|
|
FROM metrics m JOIN agents a ON a.id = m.agent_id",
|
|
)
|
|
.fetch_all(&db)
|
|
.await?;
|
|
|
|
let mut out = String::with_capacity(2048);
|
|
|
|
macro_rules! gauge {
|
|
($name:expr, $help:expr, $type:expr) => {
|
|
out.push_str(&format!(
|
|
"# HELP sentinelmesh_{} {}\n# TYPE sentinelmesh_{} {}\n",
|
|
$name, $help, $name, $type
|
|
));
|
|
};
|
|
}
|
|
|
|
gauge!("cpu_percent", "Utilisation CPU en pourcentage", "gauge");
|
|
gauge!("ram_percent", "Utilisation RAM en pourcentage", "gauge");
|
|
gauge!("disk_percent", "Utilisation disque en pourcentage", "gauge");
|
|
gauge!("temperature_c", "Température CPU/système en Celsius", "gauge");
|
|
gauge!("net_rx_bps", "Débit réseau entrant en octets/s", "gauge");
|
|
gauge!("net_tx_bps", "Débit réseau sortant en octets/s", "gauge");
|
|
|
|
for (hostname, agent_id, cpu, ram, disk, temp, rx, tx) in &rows {
|
|
let lbl = format!("agent=\"{agent_id}\",hostname=\"{hostname}\"");
|
|
let push = |name: &str, val: f64| format!("sentinelmesh_{name}{{{lbl}}} {val:.2}\n");
|
|
|
|
if let Some(v) = cpu { out.push_str(&push("cpu_percent", *v)); }
|
|
if let Some(v) = ram { out.push_str(&push("ram_percent", *v)); }
|
|
if let Some(v) = disk { out.push_str(&push("disk_percent", *v)); }
|
|
if let Some(v) = temp { out.push_str(&push("temperature_c", *v)); }
|
|
if let Some(v) = rx { out.push_str(&push("net_rx_bps", *v as f64)); }
|
|
if let Some(v) = tx { out.push_str(&push("net_tx_bps", *v as f64)); }
|
|
}
|
|
|
|
Ok((
|
|
[(header::CONTENT_TYPE, "text/plain; version=0.0.4; charset=utf-8")],
|
|
out,
|
|
))
|
|
}
|