From 0c5532976990f0f653e077a343f3edf7bf1b2c30 Mon Sep 17 00:00:00 2001 From: Azamat Date: Fri, 20 Mar 2026 21:38:51 +0300 Subject: [PATCH] [feat] wire observability into app --- adapter/di/container.py | 7 ++++--- adapter/http/fastapi/app.py | 2 +- adapter/http/fastapi/lifespan.py | 12 +++++++----- adapter/http/fastapi/middleware.py | 18 ++++++++---------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/adapter/di/container.py b/adapter/di/container.py index 95ee499..9ceedd2 100644 --- a/adapter/di/container.py +++ b/adapter/di/container.py @@ -4,7 +4,8 @@ from pathlib import Path from adapter.config.loader import load_config from adapter.config.model import AppConfig -from adapter.otel.bootstrap import OtelRuntime, setup_otel +from adapter.observability.factory import build_observability +from adapter.observability.runtime import ObservabilityRuntime from repository.user import InMemoryUserRepository from usecase.user import GetUser @@ -22,7 +23,7 @@ class AppUsecases: @dataclass(slots=True) class AppContainer: config: AppConfig - observability: OtelRuntime + observability: ObservabilityRuntime repositories: AppRepositories usecases: AppUsecases _is_shutdown: bool = field(default=False, init=False, repr=False) @@ -51,7 +52,7 @@ def build_container( environ=environ, ) - observability = setup_otel(app_config) + observability = build_observability(app_config) user_repository = InMemoryUserRepository() repositories = AppRepositories(user=user_repository) diff --git a/adapter/http/fastapi/app.py b/adapter/http/fastapi/app.py index d7ed189..a28440f 100644 --- a/adapter/http/fastapi/app.py +++ b/adapter/http/fastapi/app.py @@ -13,6 +13,6 @@ def create_app(config: AppConfig | None = None) -> FastAPI: app_config = load_config() if config is None else config app = FastAPI(title=app_config.app.name, lifespan=app_lifespan) setattr(app.state, APP_CONFIG_STATE, app_config) - register_middleware(app) + register_middleware(app, app_config) app.include_router(v1_router, prefix=API_V1_PREFIX) return app diff --git a/adapter/http/fastapi/lifespan.py b/adapter/http/fastapi/lifespan.py index 4c82bc1..6c0a171 100644 --- a/adapter/http/fastapi/lifespan.py +++ b/adapter/http/fastapi/lifespan.py @@ -19,11 +19,13 @@ async def app_lifespan(app: FastAPI) -> AsyncIterator[None]: setattr(app.state, APP_CONTAINER_STATE, container) try: - FastAPIInstrumentor.instrument_app( - app, - tracer_provider=container.observability.tracer_provider, - ) - instrumented = True + tracer_provider = container.observability.tracer_provider + if tracer_provider is not None: + FastAPIInstrumentor.instrument_app( + app, + tracer_provider=tracer_provider, + ) + instrumented = True yield finally: if instrumented: diff --git a/adapter/http/fastapi/middleware.py b/adapter/http/fastapi/middleware.py index bcdd36e..7f5d4b0 100644 --- a/adapter/http/fastapi/middleware.py +++ b/adapter/http/fastapi/middleware.py @@ -1,5 +1,6 @@ from time import perf_counter +from adapter.config.model import AppConfig from adapter.http.fastapi.dependencies import get_container from fastapi import FastAPI, Request, Response @@ -7,7 +8,7 @@ REQUEST_COUNT = 'http.server.request.count' REQUEST_DURATION = 'http.server.request.duration' -def register_middleware(app: FastAPI) -> None: +def register_middleware(app: FastAPI, config: AppConfig) -> None: @app.middleware('http') async def request_logging_middleware( request: Request, @@ -33,6 +34,9 @@ def register_middleware(app: FastAPI) -> None: }, ) + if not config.metrics.enabled: + return + @app.middleware('http') async def metrics_middleware( request: Request, @@ -48,9 +52,11 @@ def register_middleware(app: FastAPI) -> None: finally: duration_ms = (perf_counter() - start) * 1000 container = get_container(request) + route = request.scope.get('route') + path = getattr(route, 'path', None) attrs: dict[str, str | int] = { 'http.method': request.method, - 'http.path': _metric_path(request), + 'http.path': path if isinstance(path, str) and path else 'unmatched', 'http.status_code': status_code, } container.observability.metrics.increment(REQUEST_COUNT, attrs=attrs) @@ -59,11 +65,3 @@ def register_middleware(app: FastAPI) -> None: duration_ms, attrs=attrs, ) - - -def _metric_path(request: Request) -> str: - route = request.scope.get('route') - path = getattr(route, 'path', None) - if isinstance(path, str) and path: - return path - return 'unmatched'