Version 0.4.0
All checks were successful
Run linters on applied template / Python 3.13 lint and build (push) Successful in 1m40s

Changes:
- put ObservabilityMiddleware before ExceptionHandlerMiddleware to avoid repetative code
- add application startup and last metrics update metrics along with CPU usage metric and threads count
- move host and port to new uvicorn section at config along with new reload and forwarded_allow_ips
- add request_id and remove trace_id/span_id generation if tracing is disabled
- move logging logic from utils to observability
- pass trace_id/span_id in HEX form
This commit is contained in:
2026-01-03 11:01:43 +03:00
parent b8acb017fd
commit 53f14a8624
26 changed files with 901 additions and 730 deletions

View File

@@ -4,14 +4,14 @@ import asyncio
from typing import Literal
import aiohttp
from fastapi import HTTPException, Request
from fastapi import Depends, HTTPException
from fastapi.responses import JSONResponse
from opentelemetry import trace
from starlette import status
from {{project_slug}}.dependencies import auth_dep
from {{project_slug}}.observability.utils import get_tracing_headers
from {{project_slug}}.schemas import PingResponse
from {{project_slug}}.utils.observability import get_span_headers
from .routers import debug_errors_router
@@ -34,9 +34,7 @@ class DebugExceptionWithParams(Exception):
status_code=status.HTTP_200_OK,
)
async def get_status_code(status_code: int, as_exception: bool = False):
"""
Return given status code. If `as_exception` is set to True, return as HTTPException.
"""
"""Return given status code. If `as_exception` is set to True, return as HTTPException."""
if as_exception:
raise HTTPException(
status_code=status_code, detail=f"debugging with status code {status_code} as http_exception"
@@ -50,9 +48,7 @@ async def get_status_code(status_code: int, as_exception: bool = False):
status_code=status.HTTP_200_OK,
)
async def get_exception(error_type: Literal["RuntimeError", "DebugException", "DebugExceptionWithParams"]):
"""
Raise exception: `RuntimeError`, `DebugException` or `DebugExceptionWithParams` depending on given parameter.
"""
"""Raise exception: `RuntimeError`, `DebugException` or `DebugExceptionWithParams` depending on given parameter."""
if error_type == "DebugException":
raise DebugException()
if error_type == "DebugExceptionWithParams":
@@ -88,7 +84,7 @@ async def inner_get(host: str = "http://127.0.0.1:8080"):
return asyncio.create_task(
session.get(
"/api/debug/tracing_check",
headers=get_span_headers(),
headers=get_tracing_headers(),
)
)
@@ -115,9 +111,7 @@ async def inner_get(host: str = "http://127.0.0.1:8080"):
"/authentication_info",
status_code=status.HTTP_200_OK,
)
async def authentication_info(request: Request):
async def authentication_info(auth: auth_dep.AuthenticationData = Depends(auth_dep.from_request)):
"""Check authentication data from `Authorization` and `X-API-Key` headers."""
auth = auth_dep.obtain(request)
return JSONResponse({"auth_data": {"x-api-key": auth.api_key, "jwt_payload": auth.jwt_payload}})

View File

@@ -3,6 +3,7 @@
import fastapi
from starlette import status
from {{project_slug}}.db.connection.manager import PostgresConnectionManager
from {{project_slug}}.dependencies import connection_manager_dep
from {{project_slug}}.logic import system as system_logic
from {{project_slug}}.schemas import PingResponse
@@ -11,9 +12,9 @@ from .routers import system_router
@system_router.get("/", status_code=status.HTTP_307_TEMPORARY_REDIRECT, include_in_schema=False)
@system_router.get("/api/", status_code=status.HTTP_307_TEMPORARY_REDIRECT, include_in_schema=False)
@system_router.get("/api", status_code=status.HTTP_307_TEMPORARY_REDIRECT, include_in_schema=False)
async def redirect_to_swagger_docs():
"""Redirects to **/api/docs** from **/**"""
"""Redirects to **/api/docs** from **/** and **/api**."""
return fastapi.responses.RedirectResponse("/api/docs", status_code=status.HTTP_307_TEMPORARY_REDIRECT)
@@ -34,10 +35,10 @@ async def ping():
response_model=PingResponse,
status_code=status.HTTP_200_OK,
)
async def ping_db(request: fastapi.Request, readonly: bool = False):
"""
Check that database connection is valid.
"""
connection_manager = connection_manager_dep.obtain(request)
async def ping_db(
readonly: bool = False,
connection_manager: PostgresConnectionManager = fastapi.Depends(connection_manager_dep.from_request),
):
"""Check that database connection is valid."""
return await system_logic.ping_db(connection_manager, readonly)