from contextlib import asynccontextmanager import asyncio from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from opentelemetry import trace from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor from opentelemetry.trace import StatusCode from common.db import engine from common.logging_config import configure_logging from common.redis_client import redis_client from common.telemetry import setup_telemetry from . import consumer SERVICE_NAME = "notifier" logger = configure_logging(SERVICE_NAME) setup_telemetry(service_name=SERVICE_NAME) @asynccontextmanager async def lifespan(app: FastAPI): task = asyncio.create_task(consumer.run()) yield task.cancel() app = FastAPI(title=SERVICE_NAME, lifespan=lifespan) FastAPIInstrumentor.instrument_app( app, excluded_urls="/healthz", exclude_spans=["receive", "send"], ) SQLAlchemyInstrumentor().instrument(engines=[engine.sync_engine]) @app.get("/") async def root(): return {"service": SERVICE_NAME, "version": "0.1.0"} @app.get("/healthz") async def healthz(request: Request): if request.headers.get("X-Ledgerline-Probe") == "chatty-health": logger.info("received chatty-health healthz probe from gateway") return {"status": "ok", "service": SERVICE_NAME} @app.get("/readyz") async def readyz(): try: async with engine.connect() as conn: await conn.exec_driver_sql("SELECT 1") await redis_client.ping() except Exception as exc: span = trace.get_current_span() span.record_exception(exc) span.set_status(StatusCode.ERROR, str(exc)) logger.exception(f"readyz check failed: {exc}") return JSONResponse(status_code=503, content={"status": "unavailable"}) return {"status": "ok"}