6bda1a2b59
- Collecte temps réel (1s) : CPU, RAM, charge réseau, top 5 processus - Collecte medium (30min) : disques via sysinfo, températures hwmon, SMART smartctl - Collecte statique (boot) : DMI/BIOS via /sys, interfaces réseau, CPU model - API locale Axum sur :9101 — GET /metrics (réaltime + medium + hardware) - Push backend : /api/v1/metrics (réaltime + medium) et /api/v1/events (hardware, boot) - Architecture modulaire : collectors/realtime, medium, static_info - ROADMAP Phase 3 marquée complète Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
90 lines
3.2 KiB
Rust
90 lines
3.2 KiB
Rust
use std::sync::Arc;
|
|
|
|
use serde_json::json;
|
|
use tokio::{sync::RwLock, time};
|
|
use tracing::{error, info};
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
mod api;
|
|
mod backend;
|
|
mod collectors;
|
|
mod config;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(EnvFilter::from_default_env().add_directive("info".parse()?))
|
|
.init();
|
|
|
|
let config_path = std::env::args().nth(1).unwrap_or_else(|| "config.yaml".into());
|
|
let cfg = config::Config::load(&config_path)?;
|
|
info!("Agent ID : {} | hostname : {}", cfg.agent.id, cfg.agent.hostname);
|
|
|
|
let client = backend::BackendClient::new(&cfg);
|
|
|
|
// Enregistrement initial
|
|
if let Err(e) = client.register(&cfg).await {
|
|
error!("Enregistrement backend : {e}");
|
|
}
|
|
|
|
// Collecte hardware au démarrage
|
|
let hardware = collectors::static_info::collect();
|
|
info!("Hardware : {} — {} cœurs — {} Mo RAM", hardware.cpu_model, hardware.cpu_cores, hardware.ram_total_mb);
|
|
if let Err(e) = client.push_static(&cfg.agent.id, &hardware).await {
|
|
error!("Push hardware : {e}");
|
|
}
|
|
|
|
// Événement boot
|
|
if let Err(e) = backend::push_event(&client, &cfg.agent.id, "boot", json!({ "hostname": cfg.agent.hostname })).await {
|
|
error!("Push événement boot : {e}");
|
|
}
|
|
|
|
// État partagé avec l'API locale
|
|
let shared = Arc::new(RwLock::new(api::AgentState {
|
|
hardware: Some(hardware),
|
|
..Default::default()
|
|
}));
|
|
|
|
// API locale (tâche de fond)
|
|
let api_state = shared.clone();
|
|
let api_listen = cfg.api.listen.clone();
|
|
tokio::spawn(async move { api::serve(api_listen, api_state).await });
|
|
|
|
// Collecteur temps réel
|
|
let mut rt_collector = collectors::realtime::RealtimeCollector::new();
|
|
let realtime_interval = time::Duration::from_millis(cfg.intervals.realtime_ms);
|
|
let medium_interval = time::Duration::from_secs(cfg.intervals.medium_s);
|
|
|
|
// Première collecte medium au démarrage
|
|
let medium = collectors::medium::collect();
|
|
shared.write().await.medium = medium.clone();
|
|
if let Err(e) = client.push_medium(&cfg.agent.id, &medium).await {
|
|
error!("Push medium initial : {e}");
|
|
}
|
|
|
|
// Minuteries
|
|
let mut rt_ticker = time::interval(realtime_interval);
|
|
let mut med_ticker = time::interval(medium_interval);
|
|
med_ticker.tick().await; // consomme le premier tick immédiat
|
|
|
|
loop {
|
|
tokio::select! {
|
|
_ = rt_ticker.tick() => {
|
|
let metrics = rt_collector.collect(cfg.intervals.realtime_ms);
|
|
shared.write().await.realtime = metrics.clone();
|
|
if let Err(e) = client.push_realtime(&cfg.agent.id, &metrics).await {
|
|
error!("Push temps réel : {e}");
|
|
}
|
|
}
|
|
_ = med_ticker.tick() => {
|
|
info!("Collecte medium (disques, températures)…");
|
|
let metrics = collectors::medium::collect();
|
|
shared.write().await.medium = metrics.clone();
|
|
if let Err(e) = client.push_medium(&cfg.agent.id, &metrics).await {
|
|
error!("Push medium : {e}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|