feat: Sistema de monitoreo base con logging y configuración dinámica
- Implementado monitor de procesos Node.js con detección automática - Sistema de logging con niveles (Info, Warning, Error, Critical) - ConfigManager para gestión dinámica de apps monitoreadas - Interfaz web básica con escaneo de procesos - Integración con API central para reportar estados - User-Agent tracking para identificación de agentes - Persistencia de configuración en JSON - Logs almacenados en archivo con rotación - Sistema modular: monitor, interface, logger, config Estructura: - src/main.rs: Orquestador principal - src/monitor.rs: Monitoreo de procesos y envío a API - src/interface.rs: Servidor web Axum con endpoints - src/logger.rs: Sistema de logging a archivo y consola - src/config.rs: Gestión de configuración persistente - web/: Templates HTML para interfaz web - config/: Configuración de apps monitoreadas - logs/: Archivos de log del sistema Features implementadas: ✅ Detección automática de procesos Node.js ✅ Monitoreo de CPU y RAM por proceso ✅ Reportes periódicos a API central (cada 60s) ✅ Interfaz web en puerto 8080 ✅ Logs estructurados con timestamps ✅ Configuración dinámica sin reinicio ✅ Script de despliegue automatizado Próximos pasos: - Integración con systemd para control de procesos - Dashboard mejorado con cards de apps - Logs en tiempo real vía WebSocket - Start/Stop/Restart de aplicaciones
This commit is contained in:
160
src/monitor.rs
160
src/monitor.rs
@@ -0,0 +1,160 @@
|
||||
use sysinfo::System;
|
||||
use serde::Serialize;
|
||||
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
|
||||
use std::time::Duration;
|
||||
use crate::logger::get_logger;
|
||||
use crate::config::get_config_manager;
|
||||
|
||||
// User-Agent dinámico
|
||||
fn generate_user_agent() -> String {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let os = std::env::consts::OS;
|
||||
let arch = std::env::consts::ARCH;
|
||||
|
||||
format!("SIAX-Agent/{} ({}/{}; Rust-Monitor)", version, os, arch)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct AppStatusUpdate {
|
||||
app_name: String,
|
||||
server: String,
|
||||
status: String,
|
||||
port: i32,
|
||||
pid: i32,
|
||||
memory_usage: String,
|
||||
cpu_usage: String,
|
||||
last_check: String,
|
||||
}
|
||||
|
||||
pub async fn run_monitoring(server_name: String, api_key: String, cloud_url: String) {
|
||||
let logger = get_logger();
|
||||
let config_manager = get_config_manager();
|
||||
let mut sys = System::new_all();
|
||||
let user_agent = generate_user_agent();
|
||||
|
||||
logger.info("Monitor", &format!("Vigilando procesos para {} [{}]", server_name, user_agent));
|
||||
println!("🚀 Monitor: Vigilando procesos para {}", server_name);
|
||||
println!("📡 User-Agent: {}", user_agent);
|
||||
|
||||
loop {
|
||||
sys.refresh_all();
|
||||
|
||||
// ✨ LEER APPS DESDE CONFIG (dinámico)
|
||||
let apps_to_monitor = config_manager.get_apps();
|
||||
|
||||
if apps_to_monitor.is_empty() {
|
||||
logger.warning("Monitor", "No hay apps configuradas para monitorear", None);
|
||||
}
|
||||
|
||||
for app in apps_to_monitor {
|
||||
let data = collect_metrics(&sys, &app.name, app.port, &server_name);
|
||||
|
||||
match send_to_cloud(data, &api_key, &cloud_url, &user_agent).await {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
logger.error(
|
||||
"Monitor",
|
||||
&format!("Error enviando {}", app.name),
|
||||
Some(&e.to_string())
|
||||
);
|
||||
eprintln!("❌ Error enviando {}: {}", app.name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_metrics(sys: &System, name: &str, port: i32, server: &str) -> AppStatusUpdate {
|
||||
let mut pid_encontrado = 0;
|
||||
let mut cpu = 0.0;
|
||||
let mut mem = 0.0;
|
||||
let mut status = "stopped".to_string();
|
||||
|
||||
for (pid, process) in sys.processes() {
|
||||
let process_name = process.name();
|
||||
|
||||
if process_name.contains("node") {
|
||||
if let Some(cwd) = process.cwd() {
|
||||
let cwd_str = cwd.to_string_lossy();
|
||||
if cwd_str.contains(name) {
|
||||
pid_encontrado = pid.as_u32() as i32;
|
||||
cpu = process.cpu_usage();
|
||||
mem = process.memory() as f64 / 1024.0 / 1024.0;
|
||||
status = "running".to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let now = chrono::Local::now();
|
||||
let timestamp = now.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||
|
||||
AppStatusUpdate {
|
||||
app_name: name.to_string(),
|
||||
server: server.to_string(),
|
||||
status,
|
||||
port,
|
||||
pid: pid_encontrado,
|
||||
memory_usage: format!("{:.2}MB", mem),
|
||||
cpu_usage: format!("{:.2}%", cpu),
|
||||
last_check: timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_to_cloud(
|
||||
data: AppStatusUpdate,
|
||||
api_key: &str,
|
||||
cloud_url: &str,
|
||||
user_agent: &str
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let logger = get_logger();
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
"x-api-key",
|
||||
HeaderValue::from_str(api_key)?
|
||||
);
|
||||
headers.insert(
|
||||
"Content-Type",
|
||||
HeaderValue::from_static("application/json")
|
||||
);
|
||||
headers.insert(
|
||||
USER_AGENT,
|
||||
HeaderValue::from_str(user_agent)?
|
||||
);
|
||||
|
||||
let response = client
|
||||
.post(cloud_url)
|
||||
.headers(headers)
|
||||
.json(&data)
|
||||
.timeout(Duration::from_secs(10))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
println!("📤 {} -> {} (PID: {}, CPU: {}, RAM: {})",
|
||||
data.app_name,
|
||||
data.status,
|
||||
data.pid,
|
||||
data.cpu_usage,
|
||||
data.memory_usage
|
||||
);
|
||||
Ok(())
|
||||
} else {
|
||||
let status = response.status();
|
||||
let error_text = response.text().await.unwrap_or_else(|_| "Sin respuesta".to_string());
|
||||
|
||||
logger.error(
|
||||
"Monitor",
|
||||
&format!("Error enviando datos de {}", data.app_name),
|
||||
Some(&format!("HTTP {}: {}", status, error_text))
|
||||
);
|
||||
|
||||
eprintln!("⚠️ Error HTTP {}: {}", status, error_text);
|
||||
Err(format!("HTTP {}: {}", status, error_text).into())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user