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

Модуль 01 / Урок 2 из 27

Трейс, спан и контекст

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

Вся модель трассировки держится на четырёх понятиях: trace, span, span context и propagation. Разберём их до деталей — дальше весь курс будет опираться на эту лексику.

Span — единица работы

Спан — интервал времени с именем, в котором выполнялась некоторая операция: «обработка HTTP-запроса», «SQL-запрос SELECT к orders», «продюсирование сообщения в topic». У спана:

  • operation name — человекочитаемое имя («GET /api/orders/{id}»);
  • start timestamp и duration — когда и сколько;
  • span ID — 64 бита, уникальный идентификатор;
  • trace ID — 128 бит, общий для всех спанов одного запроса;
  • атрибуты (tags) — key/value метаданные: http.method=POST, db.system=postgresql, user.id=1001;
  • события (logs) — записи с таймстемпами внутри спана («retry attempt 2», «cache miss»);
  • статус — OK / ERROR (+ описание ошибки);
  • ссылки (references) — связи с другими спанами (урок 6.1).

Trace — дерево спанов

Трейс — совокупность спанов с одним trace ID, образующих дерево: корень — входящий запрос в систему, дети — вложенные операции. Дерево, а не список, потому что работа ветвится: один запрос → параллельные вызовы трёх сервисов → их вызовы в БД.

Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736
└─ gateway: POST /checkout            (900ms)   span 00f067aa0ba902b7
   ├─ ordersvc: POST /orders          (600ms)   span 12a4b5c6d7e8f901
   │  ├─ ordersvc: SQL INSERT orders  (120ms)
   │  └─ paymentsvc: Charge           (350ms)
   │     └─ paymentsvc: HTTP bank-api (300ms)  ← здесь съедена треть запроса
   └─ cartsvc: GetCart                (150ms)

Именно эта картина появляется в Jaeger UI в виде waterfall. Глядя на неё, вы мгновенно видите: самая толстая нога — HTTP к банку.

Span Context — то, что путешествует по сети

Ключевая тонкость: между сервисами передаётся не спан, а span context — небольшой неизменяемый объект: trace ID, span ID (текущего спана — он станет parent для следующего), флаги сэмплирования и прочие поля. Span context сериализуется в заголовки запроса. Стандарт — W3C Trace Context:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
#           версия |             trace-id              |      parent-span-id  | флаги
  • Флаг 01 — сэмплировано; 00 — нет (о сэмплировании — урок 2.5);
  • Дополнительно — tracestate (vendor-специфичные данные) и baggage (пользовательские ключ-значения, урок 2.4);
  • Именно поэтому трассировка работает через сторонние SDK: все, кто понимает traceparent, срастаются в общий трейс.

Как спаны находят друг друга

Каждый спан хранит ссылку на родителя (parent span ID). Когда сервис B получил traceparent и начал свою работу, он создаёт спан с parent = span ID из заголовка. Так из локальных интервалов собирается глобальное дерево — без какого-либо центрального координатора. Jaeger потом просто сгруппирует спаны по trace ID.

Атрибуты: карточность — враг

⚠️ В атрибутах спана полезно: методы, маршруты, коды ответов, идентификаторы (user.id, order.id), имена очередей. Вредно: содержимое запросов/ответов, случайные значения с высокой карточностью (request ids как теги поиска) и всё, что объёмно. Трейс — метаданные операции, а не снепшот трафика.

Практика

  1. Нарисуйте (на бумаге) дерево спанов вашего последнего «медленного» запроса: какие операции, кто родитель, где бы вы искали 300 «лишних» мс.
  2. Найдите traceparent в реальных HTTP-ответах известных вам сервисов (curl -v): какие системы уже передают контекст рядом с вами.
  3. Выпишите 10 кандидатов-атрибутов для спана «HTTP handler» вашего приложения и отметьте, какие из них вы бы реально использовали в поиске.
Мини-квиз

1. Что общего у всех спанов одного запроса? — Trace ID.

2. Что передаётся в traceparent? — Версия, trace ID, parent span ID, флаги.

3. Почему спан — это дерево? — Потому что операции вложены и ветвятся: один запрос порождает параллельные дочерние вызовы.