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:
2026-01-11 23:14:09 -05:00
parent bc1953fce1
commit 3595e55a1e
20 changed files with 3465 additions and 0 deletions

246
web/logs.html Normal file
View File

@@ -0,0 +1,246 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Logs del Sistema - SIAX</title>
<style>
body {
background: #0f172a;
color: white;
font-family: 'Courier New', monospace;
padding: 40px;
margin: 0;
}
h1 {
color: #3b82f6;
font-family: sans-serif;
}
.controls {
margin: 20px 0;
display: flex;
gap: 10px;
align-items: center;
}
.btn {
padding: 10px 20px;
border: none;
color: white;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background: #3b82f6;
}
.btn-primary:hover {
background: #2563eb;
}
.btn-danger {
background: #ef4444;
}
.btn-danger:hover {
background: #dc2626;
}
.btn-secondary {
background: #64748b;
}
.btn-secondary:hover {
background: #475569;
}
.stats {
background: #1e293b;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
display: flex;
gap: 20px;
}
.stat-item {
flex: 1;
text-align: center;
padding: 15px;
border-radius: 6px;
}
.stat-info {
background: rgba(59, 130, 246, 0.1);
border: 1px solid #3b82f6;
}
.stat-warning {
background: rgba(245, 158, 11, 0.1);
border: 1px solid #f59e0b;
}
.stat-error {
background: rgba(239, 68, 68, 0.1);
border: 1px solid #ef4444;
}
.stat-critical {
background: rgba(220, 38, 38, 0.1);
border: 1px solid #dc2626;
}
.stat-number {
font-size: 32px;
font-weight: bold;
margin-bottom: 5px;
}
.stat-label {
font-size: 12px;
text-transform: uppercase;
opacity: 0.7;
}
.log-entry {
background: #1e293b;
padding: 15px;
margin: 10px 0;
border-radius: 8px;
border-left: 4px solid;
font-size: 13px;
line-height: 1.6;
}
.log-info {
border-left-color: #3b82f6;
}
.log-warning {
border-left-color: #f59e0b;
}
.log-error {
border-left-color: #ef4444;
}
.log-critical {
border-left-color: #dc2626;
background: rgba(220, 38, 38, 0.1);
}
.log-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-weight: bold;
}
.log-timestamp {
color: #94a3b8;
font-size: 11px;
}
.log-level {
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: bold;
}
.log-module {
color: #60a5fa;
}
.log-message {
margin: 8px 0;
}
.log-details {
background: #0f172a;
padding: 10px;
border-radius: 4px;
margin-top: 8px;
color: #94a3b8;
font-size: 12px;
}
.no-logs {
background: #1e293b;
padding: 40px;
text-align: center;
border-radius: 8px;
color: #94a3b8;
}
.filter-bar {
background: #1e293b;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.filter-checkbox {
display: flex;
align-items: center;
gap: 5px;
}
.filter-checkbox input {
width: 18px;
height: 18px;
cursor: pointer;
}
.filter-checkbox label {
cursor: pointer;
user-select: none;
}
</style>
</head>
<body>
<h1>📋 Logs del Sistema SIAX</h1>
<div class="controls">
<button onclick="location.reload()" class="btn btn-primary">🔄 Refrescar</button>
<button onclick="clearLogs()" class="btn btn-danger">🗑️ Limpiar Logs</button>
<a href="/" class="btn btn-secondary">← Volver al Panel</a>
</div>
<div class="stats">
{{STATS}}
</div>
<div class="filter-bar">
<span style="color: #60a5fa; font-weight: bold;">Filtrar por nivel:</span>
<div class="filter-checkbox">
<input type="checkbox" id="filter-info" checked onchange="filterLogs()">
<label for="filter-info"> Info</label>
</div>
<div class="filter-checkbox">
<input type="checkbox" id="filter-warning" checked onchange="filterLogs()">
<label for="filter-warning">⚠️ Warning</label>
</div>
<div class="filter-checkbox">
<input type="checkbox" id="filter-error" checked onchange="filterLogs()">
<label for="filter-error">❌ Error</label>
</div>
<div class="filter-checkbox">
<input type="checkbox" id="filter-critical" checked onchange="filterLogs()">
<label for="filter-critical">🔥 Critical</label>
</div>
</div>
<div id="logs-container">
{{LOGS}}
</div>
<script>
function clearLogs() {
if (confirm('¿Estás seguro de que quieres eliminar todos los logs?')) {
fetch('/clear-logs', { method: 'POST' })
.then(() => location.reload())
.catch(err => alert('Error al limpiar logs: ' + err));
}
}
function filterLogs() {
const showInfo = document.getElementById('filter-info').checked;
const showWarning = document.getElementById('filter-warning').checked;
const showError = document.getElementById('filter-error').checked;
const showCritical = document.getElementById('filter-critical').checked;
const logs = document.querySelectorAll('.log-entry');
logs.forEach(log => {
const level = log.dataset.level;
let show = false;
if (level === 'info' && showInfo) show = true;
if (level === 'warning' && showWarning) show = true;
if (level === 'error' && showError) show = true;
if (level === 'critical' && showCritical) show = true;
log.style.display = show ? 'block' : 'none';
});
}
// Auto-refresh cada 30 segundos
setTimeout(() => location.reload(), 30000);
</script>
</body>
</html>