5.8 KiB
Логи, метрики и трейсы
Зачем нужны три сигнала
Логи
Логи отвечают на вопрос: что произошло.
Примеры:
- пришел HTTP-запрос
- пользователь не найден
- сервис стартовал
Метрики
Метрики отвечают на вопрос: как часто и насколько долго что-то происходит.
Примеры:
- сколько было запросов
- сколько было ошибок
- сколько миллисекунд заняла операция
Трейсы
Трейсы отвечают на вопрос: как прошел путь одного конкретного запроса через систему.
Примеры:
- HTTP request -> use case -> repository
- где именно замедление
- на каком шаге произошла ошибка
Как observability устроена в этом проекте
Внутренние слои не знают про OpenTelemetry напрямую.
Они работают только через порты из usecase/interface.py:
LoggerMetricsTracerSpan
Такой подход сохраняет чистую архитектуру: use case зависит от абстракций, а не от SDK.
Где лежат реализации
Логирование
adapter/observability/logging.py- логирование вstdoutили файлadapter/otel/logging.py- логирование через OpenTelemetry exporter
Поддерживаются режимы:
stdoutfileotel
Поддерживаются форматы:
textjson
Метрики
adapter/otel/metrics.py- адаптер над OTelMeter
Метрики создаются лениво: счетчик или гистограмма заводятся при первом обращении по имени.
Трейсинг
adapter/otel/tracing.py- адаптер над OTelTracer
Use case или repository может открыть span через with tracer.start_span(...):.
Runtime factory
adapter/observability/factory.py- выбирает, какой runtime собратьadapter/observability/noop.py- noop-реализации, когда сигнал отключенadapter/otel/bootstrap.py- создает OTel providers и exporters
Как это работает на старте приложения
- Загружается
AppConfig build_observability(config)выбирает нужные реализации- Если хотя бы один сигнал должен идти через OTel, создается OTel runtime
- В контейнер кладутся готовые
logger,metrics,tracer - Use case и repository получают их через конструктор
Как observability работает в HTTP
Request logging
В adapter/http/fastapi/middleware.py есть HTTP middleware, которое:
- замеряет длительность запроса
- фиксирует метод, путь и статус
- пишет событие
http_request
HTTP traces и HTTP metrics
В adapter/http/fastapi/app.py вызывается FastAPIInstrumentor.instrument_app(...).
Это дает стандартную OTel instrumentation для FastAPI/ASGI без кастомного trace middleware.
Как пользоваться логгером в use case
Пример:
class CreateUser:
def __init__(self, logger: Logger) -> None:
self._logger = logger
def execute(self, email: str) -> None:
self._logger.info('user_create', attrs={'user.email': email})
Рекомендации:
- message короткий и стабильный
- важные поля передавать в
attrs - не шить JSON руками в строку сообщения
Как пользоваться метриками
Пример:
class CreateUser:
def __init__(self, metrics: Metrics) -> None:
self._metrics = metrics
def execute(self, email: str) -> None:
self._metrics.increment('user.create.total', attrs={'source': 'api'})
self._metrics.record('user.create.duration_ms', 12.5)
Практика именования:
- счетчики:
something.total,something.error - длительности:
something.duration_ms - атрибуты делать короткими и стабильными
Как пользоваться трейсами
Пример:
class CreateUser:
def __init__(self, tracer: Tracer) -> None:
self._tracer = tracer
def execute(self, user_id: str) -> None:
with self._tracer.start_span('usecase.create_user', attrs={'user.id': user_id}) as span:
span.set_attribute('user.flow', 'signup')
Если внутри возникнет ошибка, адаптер трейсинга сможет записать ее в span.
Как включать и выключать сигналы
В config/app.yaml и env есть основные флаги:
logging.output=stdout|file|otellogging.format=text|jsonlogging.file_path=...metrics.enabled=true|falsetracing.enabled=true|false
Когда метрики или трейсы выключены, внутренние слои получают Noop-реализации.
Это значит, что код use case не приходится усложнять проверками if enabled.
Главное правило
domain/ и usecase/ не импортируют OpenTelemetry.
Весь OTel код остается во внешних слоях.