feat: add trace context propagation to http headers

Trace context should now be propagated correctly in headers of requests
and responses touched by the fiber_tracing middleware. This should enable
true distributed tracing between multiple services.
This commit is contained in:
Natalia Goc 2024-07-24 15:19:51 +02:00
parent fc38f26e1f
commit 9971ef17cb

View File

@ -8,6 +8,7 @@ import (
"git.ma-al.com/maal-libraries/observer/pkg/attr"
"github.com/gofiber/fiber/v2"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.25.0"
"go.opentelemetry.io/otel/trace"
)
@ -45,6 +46,38 @@ func configDefaults(config ...middlewareConfig) middlewareConfig {
return cfg
}
// A helper implementing `propagation.TextMapCarrier`
type headersCarrier struct {
*fiber.Ctx
}
func (h headersCarrier) Get(key string) string {
c := h.Ctx
headers := c.GetReqHeaders()
if val, ok := headers[key]; ok {
if len(val) > 0 {
return val[len(val)-1]
}
}
return ""
}
func (h headersCarrier) Set(key string, value string) {
c := h.Ctx
c.Set(key, value)
return
}
func (h headersCarrier) Keys() []string {
c := h.Ctx
headers := c.GetRespHeaders()
keys := make([]string, 0)
for k, _ := range headers {
keys = append(keys, k)
}
return keys
}
func new(config ...middlewareConfig) fiber.Handler {
// Set default config
cfg := configDefaults(config...)
@ -54,7 +87,6 @@ func new(config ...middlewareConfig) fiber.Handler {
spanStartAttributes := []attr.KeyValue{
semconv.HTTPMethod(c.Method()),
semconv.HTTPTarget(string(c.Request().RequestURI())),
semconv.HTTPRoute(c.Route().Path),
semconv.HTTPURL(c.OriginalURL()),
semconv.HTTPUserAgent(string(c.Request().Header.UserAgent())),
semconv.HTTPRequestContentLength(c.Request().Header.ContentLength()),
@ -69,22 +101,32 @@ func new(config ...middlewareConfig) fiber.Handler {
}
opts = append(opts, cfg.TracerStartAttributes...)
// Init context using values from request headers if possible:
headersCarrier := headersCarrier{c}
ctx := c.UserContext()
tctx := propagation.TraceContext{}
ctx = tctx.Extract(ctx, headersCarrier)
otelCtx, span := Tracer.Start(
c.UserContext(),
ctx,
c.Method()+" "+c.OriginalURL(),
opts...,
)
c.SetUserContext(otelCtx)
defer span.End()
err := c.Next()
statusCode := c.Response().StatusCode()
attrs := semconv.HTTPResponseStatusCode(statusCode)
span.SetAttributes(attrs)
// Return with trace context added to headers
ctx = c.UserContext()
tctx.Inject(ctx, headersCarrier)
return err
}
}