Всем привет! Сегодня хочется поговорить про механизм распространения контекста трассировки в OpenTelemetry. Разберем, как он работает, и посмотрим простой пример на Go. Всё — коротко и по делу!
Меня зовут Носорев Константин, я backend-разработчик в Яндекс Пей, автор канала "Константин про IT" и просто любознательный инженер.
OpenTelemetry уже давно стал стандартом для построения системы наблюдаемости (observability) в микросервисной архитектуре. Начнем с небольшого ликбеза, чтобы проще воспринимать материал.
Трассировка строится вокруг двух базовых понятий: trace и span.
Trace — это путь, который проходит запрос.
Span — это отдельный шаг в обработке запроса.
Один trace начинается с корневого span'а, который порождает дочерние span'ы. Все они образуют дерево вызовов, которое можно визуализировать в системах наблюдаемости.

У каждого trace и span есть соответствующие идентификаторы — trace_id
и span_id
. Когда мы создаем первый span, библиотека OpenTelemetry (далее будем говорить просто otel) генерирует эти идентификаторы, сохраняет их в памяти и затем экспортирует во внешнюю систему. Для понимания принципа работы нам достаточно знать, что вся информация о трассировках каким-то образом доходит до системы мониторинга.
Каждый новый span привязан к trace'у и, при наличии, к родительскому span'у — так формируется полная цепочка.
Когда мы остаемся в рамках одного приложения или потока, сохранить эту цепочку довольно просто. Но сила трассировки именно в том, что она позволяет связать действия, происходящие между сервисами, в единую историю.
Возникает вопрос: как otel передает идентификаторы между сервисами?
Для этого используется механизм Propagation. Он позволяет передавать контекст трассировки с помощью двух компонентов:
traceparent
— обязательный заголовок, содержащий версию протокола,trace_id
иspan_id
родителя;tracestate
— дополнительная информация, специфичная для вендора.
В случае HTTP-запросов эти значения передаются в заголовках. В большинстве случаев вам не нужно делать это вручную — otel предоставляет готовые инструменты для встраивания заголовков в HTTP-запросы. Рекомендую изучить библиотеку otel для вашего языка.
Теперь рассмотрим более интересный случай — передачу контекста в фоновые процессы, например, при реализации асинхронных API. В этом случае контекст нужно передавать через внешние системы: очереди сообщений, БД и т.д.
Пример: передача контекста через БД
Представим, что у нас есть сервис с двумя обработчиками:
POST-ручка сохраняет задачу в БД и возвращает
task_id
;GET-ручка по
task_id
возвращает статус задачи;Плюс есть фоновый воркер, который обрабатывает задачи и обновляет статус в БД.
Мы хотим получить сквозной trace, в котором будет видно всё: создание задачи, её обработку и обновление.
Для этого нужно передать trace-контекст от POST-запроса в воркер. Сохраним его в БД вместе с задачей. Otel предоставляет для этого TextMapPropagator
— механизм, позволяющий сериализовать контекст в словарь (map[string]string
), который можно, например, сохранить в поле jsonb
в PostgreSQL.
Такой объект будем называть traceContext
.
Код на Go:
package trace
import (
"context"
"go.opentelemetry.io/otel/propagation"
)
type TraceContext struct {
Trace propagation.MapCarrier `json:"trace"`
}
func Inject(ctx context.Context) TraceContext{
md := propagation.MapCarrier{}
traceContext := propagation.TraceContext{}
traceContext.Inject(ctx, md)
return TraceContext{Trace: md}
}
func Extract(ctx context.Context, tc TraceContext) context.Context {
traceContext := propagation.TraceContext{}
ctx = traceContext.Extract(ctx, tc.Trace)
return ctx
}
Мы опустили часть с сохранением traceContext
в БД — оставим это в качестве домашнего задания ;)
На стороне воркера всё просто: извлекаем traceContext
из БД, восстанавливаем контекст, создаем новый span, и он автоматически становится частью исходного trace'а.
Резюме
Сегодня мы познакомились с механизмом propagation в OpenTelemetry и рассмотрели практический пример на Go. Этот механизм позволяет обеспечить связность трассировки даже при передаче контекста через внешние хранилища.
На этом всё! Если вам зашёл такой формат — ставьте лайк, делитесь мнением в комментариях и подписывайтесь на мой телеграм-канал Константин про IT. Если будет интерес, продолжу рассказывать про полезные инструменты разработки — коротко и по делу!