Логирование и трассировка (OpenTelemetry)

Материал из llmdocs — справочник разработчика
Портал: Git, CI/CD, DevOps
Логирование и трассировка (OpenTelemetry)
ПроектOpenTelemetry (CNCF, с 2019)
ПредшественникиOpenTracing и OpenCensus
Сигналытрассы, метрики, логи, профили
ПротоколOTLP (gRPC и HTTP)
КомпонентыAPI, SDK, Collector, автоинструментирование
КонтекстW3C Trace Context (traceparent)
БэкендыJaeger, Tempo, Zipkin, Prometheus, Loki, коммерческие APM

Логирование и трассировка — способы понять, что происходит внутри работающей системы. Вместе с метриками их называют тремя сигналами наблюдаемости (observability): метрики показывают, что сломалось, логи — почему, а распределённые трассы — где именно в цепочке сервисов. OpenTelemetry (OTel) — открытый стандарт и набор библиотек для сбора всех трёх сигналов единым способом и отправки их в любую систему хранения, без привязки к конкретному поставщику.

Три сигнала

Сигнал Что это Сильная сторона Пример хранилища
Метрики числа во времени (RPS, задержка, память) дёшево, алерты, тренды Prometheus
Логи записи о событиях подробности конкретного случая Loki, Elasticsearch
Трассы путь одного запроса через сервисы где тратится время и возникает ошибка Jaeger, Tempo

Сила наблюдаемости — в связях между сигналами: из всплеска ошибок на графике Grafana перейти к трассам этих запросов, а из трассы — к логам конкретного сервиса с тем же trace_id.[2]

Логирование

Современная практика — структурированные логи: каждая запись — объект JSON с полями, а не произвольная строка.

{"ts":"2026-11-09T14:03:12Z","level":"error","service":"payments",
 "msg":"charge failed","order_id":"A-1042","trace_id":"4bf92f3577b34da6"}

Такие логи можно фильтровать и агрегировать по полям. Принципы:

  • писать в stdout, а сбор и доставку оставить среде: systemd и journald, Docker, агенты Fluent Bit, Vector или OTel Collector — так советует методология Twelve-Factor App;[6]
  • использовать уровни (debug, info, warn, error) и не включать debug в продакшене постоянно;
  • не писать в логи пароли, токены и персональные данные;
  • добавлять идентификатор запроса или трассы, чтобы связать записи разных сервисов.

Распределённая трассировка

Трасса описывает путь одного запроса и состоит из спанов — операций с началом, длительностью, атрибутами и ссылкой на родителя. Запрос к API порождает спан, внутри — спаны обращения к базе данных и вызова другого сервиса, у того — свои спаны. В интерфейсе это выглядит как водопадная диаграмма, где сразу видно самое медленное звено.

Чтобы спаны разных сервисов сложились в одну трассу, контекст передаётся с запросом — в HTTP это заголовок traceparent по стандарту W3C Trace Context:[4]

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
             версия-trace_id-родительский span_id-флаги

Хранить каждую трассу дорого, поэтому применяют сэмплирование: на входе (сохранять, например, 10% запросов) или по завершении (tail sampling — сохранять все медленные и ошибочные).

OpenTelemetry

OpenTelemetry появился в 2019 году слиянием двух конкурирующих проектов — OpenTracing и OpenCensus — и входит в CNCF; по активности это один из крупнейших проектов фонда после Kubernetes.[1] Он включает:

  • API и SDK для основных языков (Go, Java, Python, JavaScript, .NET, Rust и др.);
  • автоинструментирование — агенты, которые без изменения кода создают спаны для HTTP-фреймворков, клиентов баз данных и очередей;
  • OTLP — протокол передачи телеметрии;
  • Collector — отдельный сервис, принимающий, обрабатывающий и отправляющий данные;
  • семантические соглашения — единые имена атрибутов (http.request.method, db.system), чтобы данные разных сервисов были сопоставимы.[5]

Пример ручного спана на Python:

from opentelemetry import trace

tracer = trace.get_tracer("payments")

def charge(order):
    with tracer.start_as_current_span("charge") as span:
        span.set_attribute("order.id", order.id)
        return gateway.charge(order)

Для веб-приложения на FastAPI достаточно запустить его через opentelemetry-instrument — спаны для входящих запросов и вызовов базы появятся автоматически.

Collector

Collector отделяет приложения от бэкендов: сервисы отправляют всё по OTLP на локальный Collector, а он фильтрует, сэмплирует, добавляет метки окружения и пересылает данные в нужные системы.[3]

receivers:
  otlp:
    protocols: {grpc: {}, http: {}}
processors:
  batch: {}
exporters:
  otlphttp/tempo: {endpoint: http://tempo:4318}
  prometheus: {endpoint: 0.0.0.0:8889}
service:
  pipelines:
    traces:  {receivers: [otlp], processors: [batch], exporters: [otlphttp/tempo]}
    metrics: {receivers: [otlp], processors: [batch], exporters: [prometheus]}

Сменить бэкенд — Jaeger на Tempo или открытое решение на коммерческое — можно правкой конфигурации Collector, не трогая код сервисов.

Наблюдаемость LLM-приложений

Те же принципы применяют к системам на языковых моделях: каждый вызов модели, шаг агента и обращение к инструменту становится спаном с атрибутами — модель, число токенов, задержка. Для этого в OpenTelemetry развиваются семантические соглашения GenAI; на них опираются инструменты вроде Langfuse и Phoenix.

См. также

Источники

  1. OpenTelemetry — What is OpenTelemetry
  2. OpenTelemetry — Observability primer
  3. OpenTelemetry Collector
  4. W3C Trace Context
  5. OpenTelemetry — Semantic Conventions
  6. The Twelve-Factor App — Logs