Golang OpenTelemetry: как пробрасывать трейсы между архитектурными слоями
В Go для передачи контекста трассировки между архитектурными слоями обычно используется стандартный механизм контекста (context.Context
) совместно с инструментами OpenTelemetry (Otel). Вот пошаговое руководство, как это сделать:
1. Создание и управление контекстом трассировки
Инициализация трейсинга
Перед началом работы убедитесь, что ваш проект настроен для использования OpenTelemetry. Обычно это включает настройку экспорта трассировок (например, в Jaeger, Zipkin или OTLP).
Пример инициализации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" "log" ) func initTracer() func() { exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil { log.Fatal(err) } tp := trace.NewTracerProvider( trace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) return func() { _ = tp.Shutdown(nil) } } |
Вызывайте initTracer
при запуске приложения.
2. Создание нового трейса в слое верхнего уровня
В верхнем уровне архитектуры (например, в HTTP- или gRPC-обработчике) создавайте новый спан и добавляйте его в контекст.
Пример для HTTP-обработчика:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import ( "context" "fmt" "net/http" "go.opentelemetry.io/otel" ) func handler(w http.ResponseWriter, r *http.Request) { tracer := otel.Tracer("example-tracer") ctx, span := tracer.Start(r.Context(), "HandleRequest") defer span.End() // Передаём контекст дальше result := businessLogic(ctx) fmt.Fprintf(w, "Result: %s", result) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } |
3. Передача контекста между слоями
Передавайте context.Context
через функции или методы, чтобы сохранить связность трейсов. Каждый слой может создавать новые дочерние спаны.
Пример бизнес-логики:
1 2 3 4 5 6 7 8 9 |
func businessLogic(ctx context.Context) string { tracer := otel.Tracer("example-tracer") ctx, span := tracer.Start(ctx, "BusinessLogic") defer span.End() // Передаём контекст дальше return databaseQuery(ctx) } |
1 2 3 4 5 6 7 8 9 |
func databaseQuery(ctx context.Context) string { tracer := otel.Tracer("example-tracer") _, span := tracer.Start(ctx, "DatabaseQuery") defer span.End() // Здесь идёт реальная работа с базой данных return "QueryResult" } |
4. Обогащение трейсов
Вы можете добавлять атрибуты (ключ-значение) и события в спаны на каждом уровне:
Добавление атрибутов:
1 2 3 4 5 |
span.SetAttributes( attribute.String("db.system", "mysql"), attribute.String("db.statement", "SELECT * FROM users"), ) |
1 2 |
span.AddEvent("Fetching data from cache", trace.WithAttributes(attribute.String("cache.key", "user123"))) |
5. Проверка контекста трейса
Вы можете проверить, содержит ли context.Context
активный спан, с помощью:
1 2 3 4 |
if span := trace.SpanFromContext(ctx); span != nil { fmt.Println("Активный спан:", span.SpanContext().TraceID()) } |
6. Межсервисное взаимодействие
Если ваш код взаимодействует с другими микросервисами, вы должны передавать контекст трассировки между сервисами, используя пропагатор Otel.
Пример передачи через HTTP:
1 2 3 4 5 6 7 8 9 10 11 |
import ( "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) func callAnotherService(ctx context.Context) { client := http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)} req, _ := http.NewRequestWithContext(ctx, "GET", "http://another-service/api", nil) client.Do(req) // Otel автоматически добавит заголовки в запрос } |
Резюме
- Используйте
context.Context
: передавайте контекст через все уровни архитектуры. - Создавайте дочерние спаны: каждый слой может добавлять свои спаны, сохраняя общую цепочку.
- Обогащайте трейсы: добавляйте атрибуты и события, чтобы упростить диагностику.
- Передавайте контекст между сервисами: используйте пропагаторы для сериализации и десериализации контекста трассировки.
Это позволяет интегрировать трейсинг в многослойную архитектуру и сохранять полную картину цепочек вызовов.
Recommended Posts
Golang Sarama: настройка Partitioner
20.03.2024