""" Monitoring API Routes for NFOGuard Provides health checks, metrics, and system status endpoints """ from fastapi import APIRouter, Response, HTTPException from typing import Dict, Any, Optional import time from monitoring.health import health_checker, HealthStatus from monitoring.metrics import metrics try: from config.validator import get_configuration_summary except ImportError: def get_configuration_summary(): return {"status": "Configuration validator not available"} router = APIRouter(prefix="/api/v1", tags=["monitoring"]) @router.get("/health") async def get_health_status(): """ Get comprehensive health status Returns detailed health information including: - Overall system health - Individual component health checks - Performance metrics - Error status """ try: health_status = await health_checker.get_full_health_status() # Set appropriate HTTP status code if health_status.status == HealthStatus.HEALTHY: status_code = 200 elif health_status.status == HealthStatus.DEGRADED: status_code = 200 # Still operational else: # UNHEALTHY status_code = 503 # Service unavailable return Response( content=health_status.to_dict(), status_code=status_code, media_type="application/json" ) except Exception as e: # Return unhealthy status if health check itself fails return Response( content={ "status": "unhealthy", "message": f"Health check failed: {e}", "timestamp": time.time() }, status_code=500, media_type="application/json" ) @router.get("/health/ready") async def get_readiness_status(): """ Kubernetes readiness probe endpoint Returns 200 if service is ready to accept traffic Returns 503 if service is not ready """ try: readiness = await health_checker.get_readiness_status() status_code = 200 if readiness["ready"] else 503 return Response( content=readiness, status_code=status_code, media_type="application/json" ) except Exception as e: return Response( content={ "ready": False, "message": f"Readiness check failed: {e}", "timestamp": time.time() }, status_code=503, media_type="application/json" ) @router.get("/health/live") async def get_liveness_status(): """ Kubernetes liveness probe endpoint Returns 200 if service is alive and responsive Returns 500 if service should be restarted """ try: liveness = await health_checker.get_liveness_status() status_code = 200 if liveness["alive"] else 500 return Response( content=liveness, status_code=status_code, media_type="application/json" ) except Exception as e: return Response( content={ "alive": False, "message": f"Liveness check failed: {e}", "timestamp": time.time() }, status_code=500, media_type="application/json" ) @router.get("/metrics") async def get_prometheus_metrics(): """ Prometheus-compatible metrics endpoint Returns metrics in Prometheus text format for scraping """ try: prometheus_metrics = metrics.get_prometheus_metrics() return Response( content=prometheus_metrics, media_type="text/plain; charset=utf-8" ) except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to generate metrics: {e}" ) @router.get("/metrics/json") async def get_metrics_json(): """ Get all metrics in JSON format Returns structured metrics data including: - System metrics (CPU, memory, disk) - Processing metrics (rates, durations) - Error metrics (counts, recent errors) """ try: all_metrics = metrics.get_all_metrics() return all_metrics except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get metrics: {e}" ) @router.get("/status") async def get_system_status(): """ Get comprehensive system status Returns detailed system information including: - Health status - Configuration summary - Performance metrics - Recent activity """ try: # Get health status health_status = await health_checker.get_full_health_status() # Get metrics all_metrics = metrics.get_all_metrics() # Get configuration summary config_summary = get_configuration_summary() # Combine into comprehensive status status_response = { "overall_status": health_status.status.value, "timestamp": time.time(), "uptime_seconds": health_status.uptime_seconds, "version": health_status.version, "health": health_status.to_dict(), "metrics": all_metrics, "configuration": config_summary, "summary": { "service_healthy": health_status.status in [HealthStatus.HEALTHY, HealthStatus.DEGRADED], "total_webhooks_processed": all_metrics["processing"]["total_webhooks"], "total_nfo_files_created": all_metrics["processing"]["total_nfo_created"], "total_errors": all_metrics["processing"]["total_errors"], "current_processing_rate": all_metrics["processing"]["webhooks_received_per_minute"], "average_processing_time": all_metrics["processing"]["average_processing_time_seconds"] } } return status_response except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get system status: {e}" ) @router.get("/status/brief") async def get_brief_status(): """ Get brief system status for quick monitoring Returns essential status information without detailed metrics """ try: # Get basic health liveness = await health_checker.get_liveness_status() readiness = await health_checker.get_readiness_status() # Get basic metrics processing_metrics = metrics.get_processing_metrics() system_metrics = metrics.get_system_metrics() return { "status": "healthy" if liveness["alive"] and readiness["ready"] else "unhealthy", "alive": liveness["alive"], "ready": readiness["ready"], "uptime_seconds": liveness["uptime_seconds"], "webhooks_per_minute": processing_metrics["webhooks_received_per_minute"], "active_operations": processing_metrics["active_operations"], "total_errors": processing_metrics["total_errors"], "cpu_percent": system_metrics.get("cpu_percent", 0), "memory_percent": system_metrics.get("memory_percent", 0), "timestamp": time.time() } except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get brief status: {e}" ) @router.get("/metrics/processing") async def get_processing_metrics(): """ Get processing-specific metrics Returns metrics focused on NFO processing performance """ try: processing_metrics = metrics.get_processing_metrics() return processing_metrics except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get processing metrics: {e}" ) @router.get("/metrics/errors") async def get_error_metrics(): """ Get error-specific metrics Returns error counts, types, and recent error information """ try: error_metrics = metrics.get_error_metrics() return error_metrics except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get error metrics: {e}" ) @router.get("/metrics/system") async def get_system_metrics(): """ Get system resource metrics Returns CPU, memory, disk, and process information """ try: system_metrics = metrics.get_system_metrics() return system_metrics except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get system metrics: {e}" ) @router.post("/metrics/reset") async def reset_metrics(metric_types: Optional[str] = None): """ Reset specific metric types Parameters: - metric_types: Comma-separated list of metric types to reset (counters, histograms, errors, timeseries) If not specified, resets all metrics """ try: reset_types = None if metric_types: reset_types = [t.strip() for t in metric_types.split(",")] metrics.reset_metrics(reset_types) return { "message": "Metrics reset successfully", "reset_types": reset_types or "all", "timestamp": time.time() } except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to reset metrics: {e}" ) # Legacy endpoints for backwards compatibility @router.get("/health-check") async def legacy_health_check(): """Legacy health check endpoint (redirects to /health)""" return await get_brief_status() @router.get("/ping") async def ping(): """Simple ping endpoint for basic connectivity testing""" return { "message": "pong", "timestamp": time.time(), "service": "nfoguard", "version": "2.0.0" }