goopen-telemetrygorilla

Get trace_id from OTEL trace instrumented with golang otelmux


In my golang application, I've instrumented OTEL tracing middleware with otelmux middleware and ddotel tracer provider:

package tracing

import (
    "github.com/gorilla/mux"
    "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux"
    ddotel "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentelemetry"
    "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

// Middleware returns a tracing middleware that will add a span for each request.
func Middleware(t *TraceProvider) mux.MiddlewareFunc {
    sampler := tracer.NewRateSampler(0.1)
    provider := ddotel.NewTracerProvider(
        tracer.WithSampler(sampler), 
        tracer.WithRuntimeMetrics(),
    )
    return otelmux.Middleware("my-service", otelmux.WithTracerProvider(provider))
}

Whenever a trace is attached to a request (considering the sampling rate), I'd like to pull the trace ID from the root span, so I can inject it into a logging API in order to correlate the log from that request with the trace from same request.

So far, I didn't find a straightforward to get the trace ID from the request context.

How can I extract the trace ID?


Solution

  • Thanks for best solution as commented by @hamed-n:

    Here is a function to extract the trace ID from the span context using the trace package: "go.opentelemetry.io/otel/trace":

    func GetTraceID(ctx context.Context) string {
        spanCtx := trace.SpanContextFromContext(ctx)
        if spanCtx.HasTraceID() {
            traceID := spanCtx.TraceID()
            return traceID.String()
        }
        return ""
    }
    

    In my case, since the provider I'm using is DataDog, I had to convert the trace ID to uint64 big endian:

    func GetTraceID(ctx context.Context) string {
        spanCtx := trace.SpanContextFromContext(ctx)
        if spanCtx.HasTraceID() {
            // Since datadog trace provider (ddtrace) uses big endian uint64 for the trace ID, we must first to first convert it back to uint64.
            traceID := spanCtx.TraceID()
            traceIDRaw := [16]byte(traceID)
            traceIDUint64 := byteArrToUint64(traceIDRaw[8:])
            traceIDStr := strconv.FormatUint(traceIDUint64, 10)
            return traceIDStr
        }
        return ""
    }
    
    func byteArrToUint64(buf []byte) uint64 {
        var x uint64
        for i, b := range buf {
            x = x<<8 + uint64(b)
            if i == 7 {
                return x
            }
        }
        return x
    }