Перейти к содержанию

Трейсинг (OpenTelemetry)

pip install "endocore[otel]"

Инструментируем каждый запрос

# Middleware/__init__.py
from endocore.middleware import tracing_middleware

middlewares = [tracing_middleware()]

Это открывает один спан на каждый запрос (с именем вида "GET /v1/user/42"), с атрибутами http.method, http.target, http.status_code. Брошенный HTTPError проставляет http.status_code из своего статуса, но помечает спан как ошибку только для 5xx — NotFound не является отказом сервера и не должен выглядеть так в бэкенде трейсинга. Любое другое необработанное исключение записывается на спан и помечает его как ошибку — так же, как это уже логирует logging_middleware.

Без настроенного экспортёра спаны всё равно создаются — это само по себе полезно для корреляции trace-id/логов ниже — но никуда не отправляются.

Отправляем спаны куда-то

# extensions.py
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from endocore.extensions.tracing import TracingExtension

extensions = [TracingExtension(exporter=OTLPSpanExporter(), service_name="my-app")]

EndoCore не зависит ни от какого конкретного пакета-экспортёра — поставьте тот, что нужен вашему коллектору (opentelemetry-exporter-otlp, консольный экспортёр для локальной отладки и т.д.), и передайте уже созданный экземпляр. TracingExtension выставляет его как глобальный TracerProvider и сбрасывает накопленные спаны при остановке.

Сопоставление трейсов с логами

У каждого запроса уже есть X-Request-ID (см. Логирование). tracing_middleware дополнительно кладёт trace id спана в request.scope["trace_id"], так что хендлер (или другой middleware) может залогировать оба значения рядом:

async def handler(request):
    log.info("trace=%s request=%s", request.scope.get("trace_id"), request.scope.get("request_id"))