eventRT/middleware/trace.go

83 lines
2.5 KiB
Go
Raw Permalink Normal View History

// Package middleware defines OTel tracing infrastructure and gin middlewares.
package middleware
import (
"context"
"fmt"
"eventRT/config"
"eventRT/constants"
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
oteltrace "go.opentelemetry.io/otel/trace"
)
// InitTracerProvider creates an OTLP TracerProvider and registers it as the global provider.
// It registers the W3C TraceContext propagator (traceparent header).
// The caller is responsible for calling Shutdown on the returned provider during graceful shutdown.
func InitTracerProvider(ctx context.Context, cfg config.EventRTConfig) (*sdktrace.TracerProvider, error) {
opts := []otlptracehttp.Option{
otlptracehttp.WithEndpoint(cfg.OtelConfig.Endpoint),
}
if cfg.OtelConfig.Insecure {
opts = append(opts, otlptracehttp.WithInsecure())
}
exporter, err := otlptracehttp.New(ctx, opts...)
if err != nil {
return nil, fmt.Errorf("create OTLP exporter: %w", err)
}
res := sdkresource.NewSchemaless(
attribute.String("service.name", cfg.ServiceName),
attribute.String("deployment.environment", cfg.DeployEnv),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
// StartTrace extracts upstream W3C trace context from request headers and starts a server span.
func StartTrace() gin.HandlerFunc {
tracer := otel.Tracer("eventRT/http")
return func(c *gin.Context) {
ctx := otel.GetTextMapPropagator().Extract(
c.Request.Context(),
propagation.HeaderCarrier(c.Request.Header),
)
spanName := c.FullPath()
if spanName == "" {
spanName = c.Request.URL.Path
}
ctx, span := tracer.Start(ctx, spanName,
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
)
defer span.End()
spanCtx := span.SpanContext()
ctx = context.WithValue(ctx, constants.CtxKeyTraceID, spanCtx.TraceID().String())
ctx = context.WithValue(ctx, constants.CtxKeySpanID, spanCtx.SpanID().String())
c.Request = c.Request.WithContext(ctx)
c.Set(constants.HeaderTraceID, spanCtx.TraceID().String())
c.Set(constants.HeaderSpanID, spanCtx.SpanID().String())
c.Next()
}
}