Initial commit: SYNC_API project
This commit is contained in:
176
sync_api/routers/health.py
Normal file
176
sync_api/routers/health.py
Normal file
@@ -0,0 +1,176 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from database.connection import db_connection
|
||||
from models.sync_models import HealthCheck, EstadoSincronizacion
|
||||
import time
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["health"])
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Variable global para tracking del tiempo de inicio
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
def get_uptime() -> str:
|
||||
"""Calcula el tiempo de actividad del servicio"""
|
||||
uptime_seconds = time.time() - start_time
|
||||
|
||||
if uptime_seconds < 60:
|
||||
return f"{int(uptime_seconds)} segundos"
|
||||
elif uptime_seconds < 3600:
|
||||
minutes = int(uptime_seconds / 60)
|
||||
seconds = int(uptime_seconds % 60)
|
||||
return f"{minutes}m {seconds}s"
|
||||
elif uptime_seconds < 86400:
|
||||
hours = int(uptime_seconds / 3600)
|
||||
minutes = int((uptime_seconds % 3600) / 60)
|
||||
return f"{hours}h {minutes}m"
|
||||
else:
|
||||
days = int(uptime_seconds / 86400)
|
||||
hours = int((uptime_seconds % 86400) / 3600)
|
||||
return f"{days}d {hours}h"
|
||||
|
||||
|
||||
@router.get("/health",
|
||||
response_model=HealthCheck,
|
||||
summary="Verificar salud del sistema",
|
||||
description="Retorna el estado general de salud del sistema de monitoreo")
|
||||
async def health_check():
|
||||
"""
|
||||
Endpoint de health check que proporciona información sobre:
|
||||
- Estado general del sistema
|
||||
- Conectividad con la base de datos
|
||||
- Estadísticas generales de nodos
|
||||
- Tiempo de actividad del servicio
|
||||
- Versión de la API
|
||||
"""
|
||||
try:
|
||||
logger.info("Ejecutando health check del sistema")
|
||||
|
||||
# Probar conexión a ambas bases de datos
|
||||
main_db_status = "healthy"
|
||||
backup_db_status = "healthy"
|
||||
|
||||
try:
|
||||
main_db_test = db_connection.test_connection("main")
|
||||
if not main_db_test.get("connection_successful", False):
|
||||
main_db_status = "unhealthy"
|
||||
except Exception as e:
|
||||
logger.error(f"Error en conexión a BD principal durante health check: {str(e)}")
|
||||
main_db_status = "unhealthy"
|
||||
|
||||
try:
|
||||
backup_db_test = db_connection.test_connection("backup")
|
||||
if not backup_db_test.get("connection_successful", False):
|
||||
backup_db_status = "unhealthy"
|
||||
except Exception as e:
|
||||
logger.error(f"Error en conexión a BD backup durante health check: {str(e)}")
|
||||
backup_db_status = "unhealthy"
|
||||
|
||||
# Estado general de BD (ambas deben estar healthy)
|
||||
db_status = "healthy" if main_db_status == "healthy" and backup_db_status == "healthy" else "unhealthy"
|
||||
|
||||
# Obtener estadísticas de nodos si la BD está disponible
|
||||
total_nodes = 0
|
||||
nodes_healthy = 0
|
||||
nodes_attention = 0
|
||||
nodes_error = 0
|
||||
|
||||
if db_status == "healthy":
|
||||
try:
|
||||
# Consultar estadísticas de nodos por estado
|
||||
stats_query = """
|
||||
SELECT
|
||||
CASE
|
||||
WHEN Activo = 1 THEN 'Actualizada'
|
||||
ELSE 'Atención'
|
||||
END as EstadoSincronizacion,
|
||||
COUNT(DISTINCT NodoSubNodo) as NodeCount
|
||||
FROM BasesdeDatos
|
||||
GROUP BY Activo
|
||||
"""
|
||||
|
||||
results = db_connection.execute_query(stats_query, server_type="main")
|
||||
|
||||
status_mapping = {
|
||||
EstadoSincronizacion.ACTUALIZADA.value: 'healthy',
|
||||
EstadoSincronizacion.ATENCION.value: 'attention',
|
||||
EstadoSincronizacion.ERROR.value: 'error'
|
||||
}
|
||||
|
||||
for row in results:
|
||||
estado = row.get('EstadoSincronizacion', '')
|
||||
count = row.get('NodeCount', 0)
|
||||
total_nodes += count
|
||||
|
||||
if estado == EstadoSincronizacion.ACTUALIZADA.value:
|
||||
nodes_healthy += count
|
||||
elif estado == EstadoSincronizacion.ATENCION.value:
|
||||
nodes_attention += count
|
||||
elif estado == EstadoSincronizacion.ERROR.value:
|
||||
nodes_error += count
|
||||
|
||||
logger.info(f"Estadísticas de nodos - Total: {total_nodes}, "
|
||||
f"Healthy: {nodes_healthy}, Atención: {nodes_attention}, Error: {nodes_error}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error obteniendo estadísticas de nodos: {str(e)}")
|
||||
# Mantener valores por defecto si hay error
|
||||
|
||||
# Determinar estado general del sistema
|
||||
overall_status = "healthy"
|
||||
|
||||
if db_status != "healthy":
|
||||
overall_status = "unhealthy"
|
||||
elif nodes_error > 0 or (total_nodes > 0 and nodes_healthy / total_nodes < 0.5):
|
||||
overall_status = "degraded"
|
||||
elif nodes_attention > 0:
|
||||
overall_status = "degraded"
|
||||
|
||||
# Crear respuesta del health check
|
||||
health_response = HealthCheck(
|
||||
status=overall_status,
|
||||
database_status=db_status,
|
||||
total_nodes=total_nodes,
|
||||
nodes_healthy=nodes_healthy,
|
||||
nodes_attention=nodes_attention,
|
||||
nodes_error=nodes_error,
|
||||
last_check=datetime.now(),
|
||||
uptime=get_uptime(),
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
logger.info(f"Health check completado - Estado: {overall_status}")
|
||||
return health_response
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error crítico durante health check: {str(e)}")
|
||||
|
||||
# Retornar estado de error crítico
|
||||
return HealthCheck(
|
||||
status="unhealthy",
|
||||
database_status="error",
|
||||
total_nodes=0,
|
||||
nodes_healthy=0,
|
||||
nodes_attention=0,
|
||||
nodes_error=0,
|
||||
last_check=datetime.now(),
|
||||
uptime=get_uptime(),
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/ping",
|
||||
summary="Ping simple",
|
||||
description="Endpoint simple para verificar que la API está respondiendo")
|
||||
async def ping():
|
||||
"""
|
||||
Endpoint simple de ping para verificación básica de disponibilidad.
|
||||
Útil para load balancers y monitoreo básico.
|
||||
"""
|
||||
return {
|
||||
"message": "pong",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"status": "ok"
|
||||
}
|
||||
Reference in New Issue
Block a user