Example Python logging

from __future__ import annotations

import logging
from contextlib import asynccontextmanager

import uvicorn
from fastapi import FastAPI
from fastapi.responses import JSONResponse


logging.basicConfig(
    filename="app.log",
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s",
    datefmt="%H:%M:%S",
    force=True,
)
logger = logging.getLogger(__name__)


@asynccontextmanager
async def lifespan(_: FastAPI):
    logger.info("Application startup complete")
    yield


app = FastAPI(
    title="Logging Demo",
    description="A very small FastAPI app that demonstrates file logging.",
    lifespan=lifespan,
)


@app.get("/")
async def root():
    logger.info("Home page visited")
    return {
        "message": "Logging demo is running",
        "ideas": [
            "Log messages can be written to a file",
            "Use log levels: INFO, WARNING, ERROR",
            "Log exceptions with stack traces",
        ],
        "log_file": "app.log",
    }


@app.get("/items/{item_id}")
async def get_item(item_id: int):
    logger.info("Looking up item %s", item_id)
    if item_id < 0:
        logger.warning("Negative item requested: %s", item_id)
        return JSONResponse(
            status_code=400,
            content={"error": "item_id must be non-negative"},
        )

    return {"item_id": item_id, "status": "ok"}


@app.get("/crash")
async def crash():
    try:
        raise RuntimeError("Simulated server failure for logging demo")
    except RuntimeError:
        logger.exception("Example error")
        return JSONResponse(
            status_code=500,
            content={"error": "example failure was logged"},
        )


def main() -> None:
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=False)


if __name__ == "__main__":
    main()