Программа курса

Модуль 02 / Урок 8 из 27

Baggage и связь трейсов с логами

Теория и практика

Baggage — «рюкзак» пользовательских данных, путешествующих вместе с контекстом. А корреляция трейсов с логами превращает Jaeger и ELK в единую систему. Обе темы — про то, что видит инженер при расследовании.

Baggage: контекст вне трейса

Span context — служебные ID. Baggage — ваши произвольные key-value, которые распространяются по всей цепочке и доступны каждому сервису:

# Python: кладём значение у входа в систему
from opentelemetry.baggage import set_baggage, get_baggage
from opentelemetry.context import attach, get_current

ctx = set_baggage("tenant.id", "acme")
token = attach(ctx)
# дальше по цепочке любой сервис:
tenant = get_baggage("tenant.id")        # "acme"

Заголовок baggage: tenant.id=acme,user.tier=premium — и значение видно в каждом сервисе, включая те, что не принимают участие в «бизнес-запросе» (логирование, лимиты).

Для чего годится и не годится

  • ✔ tenant/team-маркеры для мультитенанта, tier пользователя, версия фичи, request-«путь» для маршрутизации;
  • ✘ большие данные (в каждый заголовок каждого запроса!), секреты, персональные данные;
  • ✘ данные, которые не должны покидать сервис: baggage — часть публичного контракта заголовков.
⚠️ Баггаж распространяется только через инструментированные границы. Как и traceparent, он может быть вырезан прокси и теряется в неинструментированных переходах. И ещё: baggage не попадает в спаны автоматически — если нужно видеть его в Jaeger, добавляйте в атрибуты (processor attributes на Collector или в коде).

Корреляция с логами: trace_id в логе

Канонический цикл расследования: метрика → трейс → лог. Чтобы он сработал, в каждый лог-порядок попадают идентификаторы текущего спана:

  • Python: LoggingHandler из OTel либо вручную trace.get_current_span().get_span_context() → trace_id/span_id в поля лога;
  • Java: MDC — traceId/spanId (logback pattern %X{trace_id}; OTel-агент умеет заполнять MDC автоматически через opentelemetry-logback-1.0 интеграции);
  • Node: pino-модули или ручной запрос контекста из AsyncLocalStorage.
# Java logback pattern
%d{HH:mm:ss} %-5level [tid=%X{trace_id} sid=%X{span_id}] %logger - %msg%n

Двунаправленные переходы

  • Из лога в трейс: скопируйте trace_id из лога → Jaeger UI → вставьте в поле поиска (поиск по Trace ID) → весь запрос;
  • Из трейса в логи: в Kibana/клике поиска по полю trace_id — все логи этого запроса во всех сервисах;
  • Ссылка в UI: Jaeger умеет deep-link из спана во внешний UI (span links в process tags — настраивается в query-сервисе), но и просто копирование ID уже работает.

Паттерн «один запрос — вся история»

  1. Метрика: p99 endpoint'а вырос →
  2. Трейс: медленный сегмент в сервисе B, атрибут order.id=42 →
  3. Логи: запросы Kibana по trace_id показывают retry-цикл и предупреждения →
  4. Код: локализована причина.

Без trace_id в логах шаг 3 распадается: вы не можете найти «логи именно этого запроса».

Практика

  1. Добавьте в оба сервиса практикума логгер с trace_id/span_id в каждом сообщении; убедитесь, что идентификаторы совпадают с трейсом в UI.
  2. Передайте baggage tenant.id из A в B и залогируйте его в B (проверьте: дошёл ли баггаж, если вызов идёт через неинструментированный клиент?).
  3. Положите baggage ключ с 2 КБ текста и посмотрите на размер заголовков каждого исходящего запроса — почувствуйте, почему большие значения запрещены.
Мини-квиз

1. Чем baggage отличается от атрибутов спана? — Баггаж — распространяемый контекст между сервисами; атрибуты — данные конкретного спана.

2. Что нужно, чтобы цикл «метрика→трейс→лог» работал? — trace_id/span_id в каждом лог-порядке + поиск по ним в log-системе.

3. Почему в baggage нельзя секреты? — Он летит в открытых заголовках через все сервисы и прокси.