01ammu's picture
Sync from GitHub via hub-sync
fee2f22 verified
Raw History Blame Contribute Delete
3.45 kB
"""FastAPI application entrypoint."""
from __future__ import annotations
from contextlib import asynccontextmanager
import json
import logging
from datetime import datetime, timezone
from typing import Any
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import Response
from sqlalchemy import create_engine
from backend.config import settings
from backend.database import init_db
from backend.monitoring.metrics import export_metrics
from backend.monitoring.middleware import MetricsMiddleware
from backend.routes.datasets import router as datasets_router
from backend.routes.inference import router as inference_router
from backend.routes.jobs import router as jobs_router
class JsonFormatter(logging.Formatter):
"""Format logs as JSON for production-friendly ingestion."""
def format(self, record: logging.LogRecord) -> str:
payload: dict[str, Any] = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
"environment": settings.environment,
}
if record.exc_info:
payload["exception"] = self.formatException(record.exc_info)
return json.dumps(payload)
def configure_logging() -> None:
"""Configure root logger with JSON output."""
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
root_logger = logging.getLogger()
root_logger.handlers = [handler]
root_logger.setLevel(settings.log_level.upper())
def _probe_database_engine() -> None:
"""Build and dispose an engine to validate configured URL syntax."""
probe_engine = create_engine(settings.database_url, pool_pre_ping=True, echo=False)
probe_engine.dispose()
configure_logging()
logger = logging.getLogger(__name__)
REQUEST_METRICS = {
"health_checks_total": 0,
"datasets_requests_total": 0,
"jobs_requests_total": 0,
"inference_requests_total": 0,
}
@asynccontextmanager
async def lifespan(_app: FastAPI):
"""Initialize required runtime components on startup."""
try:
_probe_database_engine()
init_db()
logger.info("FastAPI backend initialized")
except Exception:
logger.exception("Backend startup completed with database initialization warning")
yield
app = FastAPI(title="Data Cleaning Platform", version="1.0.0", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000",
"http://localhost:5173",
"http://localhost:5174",
"http://127.0.0.1:5173",
"http://127.0.0.1:5174",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(MetricsMiddleware)
@app.get("/api/health")
@app.get("/health")
async def health_check() -> dict[str, str]:
"""Simple liveness endpoint."""
REQUEST_METRICS["health_checks_total"] += 1
return {
"status": "ok",
"environment": settings.environment,
"port": str(settings.api_port),
}
app.include_router(datasets_router)
app.include_router(jobs_router)
app.include_router(inference_router)
@app.get("/metrics")
async def metrics() -> Response:
"""Expose Prometheus metrics in text format."""
payload, content_type = export_metrics()
return Response(content=payload, media_type=content_type)