Вся модель трассировки держится на четырёх понятиях: 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 как теги поиска) и всё, что объёмно. Трейс — метаданные операции, а не снепшот трафика.
Практика
- Нарисуйте (на бумаге) дерево спанов вашего последнего «медленного» запроса: какие операции, кто родитель, где бы вы искали 300 «лишних» мс.
- Найдите
traceparentв реальных HTTP-ответах известных вам сервисов (curl -v): какие системы уже передают контекст рядом с вами. - Выпишите 10 кандидатов-атрибутов для спана «HTTP handler» вашего приложения и отметьте, какие из них вы бы реально использовали в поиске.
Мини-квиз
1. Что общего у всех спанов одного запроса? — Trace ID.
2. Что передаётся в traceparent? — Версия, trace ID, parent span ID, флаги.
3. Почему спан — это дерево? — Потому что операции вложены и ветвятся: один запрос порождает параллельные дочерние вызовы.