from __future__ import annotations from datetime import datetime, timedelta from typing import Optional from fastapi import APIRouter, Depends, Query, Response from prometheus_client import CONTENT_TYPE_LATEST, generate_latest from sqlalchemy.orm import Session from config.database import get_db from services.metrics import MetricsService router = APIRouter(prefix="/metrics", tags=["monitoring"]) @router.get("", summary="Prometheus metrics endpoint", include_in_schema=False) async def metrics_endpoint() -> Response: payload = generate_latest() return Response(content=payload, media_type=CONTENT_TYPE_LATEST) @router.get("/performance", summary="Get performance metrics") async def get_performance_metrics( metric_name: Optional[str] = Query( None, description="Filter by metric name"), hours: int = Query(24, description="Hours back to look"), db: Session = Depends(get_db), ) -> dict: """Get aggregated performance metrics.""" service = MetricsService(db) start_time = datetime.utcnow() - timedelta(hours=hours) if metric_name: metrics = service.get_metrics( metric_name=metric_name, start_time=start_time) aggregated = service.get_aggregated_metrics( metric_name, start_time=start_time) return { "metric_name": metric_name, "period_hours": hours, "aggregated": aggregated, "recent_samples": [ { "timestamp": m.timestamp.isoformat(), "value": m.value, "labels": m.labels, "endpoint": m.endpoint, "method": m.method, "status_code": m.status_code, "duration_seconds": m.duration_seconds, } for m in metrics[:50] # Last 50 samples ], } # Return summary for all metrics all_metrics = service.get_metrics(start_time=start_time, limit=1000) metric_types = {} for m in all_metrics: if m.metric_name not in metric_types: metric_types[m.metric_name] = [] metric_types[m.metric_name].append(m.value) summary = {} for name, values in metric_types.items(): summary[name] = { "count": len(values), "avg": sum(values) / len(values) if values else 0, "min": min(values) if values else 0, "max": max(values) if values else 0, } return { "period_hours": hours, "summary": summary, } @router.get("/health", summary="Detailed health check with metrics") async def detailed_health(db: Session = Depends(get_db)) -> dict: """Get detailed health status with recent metrics.""" service = MetricsService(db) last_hour = datetime.utcnow() - timedelta(hours=1) # Get request metrics from last hour request_metrics = service.get_metrics( metric_name="http_request", start_time=last_hour ) if request_metrics: durations = [] error_count = 0 for m in request_metrics: if m.duration_seconds is not None: durations.append(m.duration_seconds) if m.status_code is not None: if m.status_code >= 400: error_count += 1 total_requests = len(request_metrics) avg_duration = sum(durations) / len(durations) if durations else 0 error_rate = error_count / total_requests if total_requests > 0 else 0 else: avg_duration = 0 error_rate = 0 total_requests = 0 return { "status": "ok", "timestamp": datetime.utcnow().isoformat(), "metrics": { "requests_last_hour": total_requests, "avg_response_time_seconds": avg_duration, "error_rate": error_rate, }, }