this commit did not fix problem,just replace 0 to ContextRequestKey #3897 Gin reopen

this code can solve this problem Gin reopen

Comment From: huangjilaiqin

@FarmerChillax @appleboy check this please

Comment From: FarmerChillax

see: https://github.com/gin-gonic/gin/issues/3993#issuecomment-2172593121

Comment From: FarmerChillax

this code can solve this problem

this is a break change, I would not recommend making such changes in a widely used framework.

And this doesn't seem to be the root cause (with otel)

Comment From: huangjilaiqin

this is the root cause, ContextRequestKey==0 make otel's func SpanFromContext can not get the Span but get the c.Request Gin reopen Gin reopen

Comment From: FarmerChillax

In otel the currentSpanKey is Type of traceConrextKeyType. In here, you can set ContextWithFallback = true to solue this problem

see: https://github.com/gin-gonic/gin/issues/3993#issuecomment-2172593121

with Go Context standards:

The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys. To avoid allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported context key variables' static type should be a pointer or interface.

ref: https://pkg.go.dev/context#WithValue

Comment From: FarmerChillax

Minimum case, Hope this helps. Go playground: https://go.dev/play/p/V_-_k4qhbMK

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
    "go.opentelemetry.io/otel"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    "go.opentelemetry.io/otel/trace"
)

var applicationName string = "demo"

func main() {
    otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.AlwaysSample()))))
    app := gin.Default()
    app.ContextWithFallback = true
    app.Use(otelgin.Middleware(applicationName))

    app.GET("/", func(ctx *gin.Context) {
        traceID, spanID, isSampled := GetTraceInfo(ctx)
        fmt.Printf("traceID: %v; spanID: %v; isSampled: %v\n", traceID, spanID, isSampled)
    })

    go func() {
        if err := app.Run(":8080"); err != nil {
            log.Fatalln(err)
        }
    }()

    http.Get("http://localhost:8080/")

}

func GetTraceInfo(ctx context.Context) (traceID string, spanID string, isSampled bool) {
    spanCtx := trace.SpanContextFromContext(ctx)

    if spanCtx.HasTraceID() {
        traceID = spanCtx.TraceID().String()
    }
    if spanCtx.HasSpanID() {
        spanID = spanCtx.SpanID().String()
    }

    isSampled = spanCtx.IsSampled()

    return traceID, spanID, isSampled
}

In shell:

$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

traceID: 68e41a688d27fa3c9f895417414b6dc5; spanID: de7b716f74c755e8; isSampled: true
[GIN] 2024/10/15 - 09:57:34 | 200 |      67.291µs |       127.0.0.1 | GET      "/"

go mod:

module tmp

go 1.22.0

toolchain go1.22.5

require (
    github.com/gin-gonic/gin v1.10.0
    go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.56.0
    go.opentelemetry.io/contrib/propagators/b3 v1.31.0
    go.opentelemetry.io/otel v1.31.0
    go.opentelemetry.io/otel/sdk v1.31.0
    go.opentelemetry.io/otel/trace v1.31.0
)

require (
    github.com/bytedance/sonic v1.12.3 // indirect
    github.com/bytedance/sonic/loader v0.2.0 // indirect
    github.com/cloudwego/base64x v0.1.4 // indirect
    github.com/cloudwego/iasm v0.2.0 // indirect
    github.com/gabriel-vasile/mimetype v1.4.5 // indirect
    github.com/gin-contrib/sse v0.1.0 // indirect
    github.com/go-logr/logr v1.4.2 // indirect
    github.com/go-logr/stdr v1.2.2 // indirect
    github.com/go-playground/locales v0.14.1 // indirect
    github.com/go-playground/universal-translator v0.18.1 // indirect
    github.com/go-playground/validator/v10 v10.22.1 // indirect
    github.com/goccy/go-json v0.10.3 // indirect
    github.com/google/uuid v1.6.0 // indirect
    github.com/json-iterator/go v1.1.12 // indirect
    github.com/klauspost/cpuid/v2 v2.2.8 // indirect
    github.com/leodido/go-urn v1.4.0 // indirect
    github.com/mattn/go-isatty v0.0.20 // indirect
    github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
    github.com/modern-go/reflect2 v1.0.2 // indirect
    github.com/pelletier/go-toml/v2 v2.2.3 // indirect
    github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
    github.com/ugorji/go/codec v1.2.12 // indirect
    go.opentelemetry.io/otel/metric v1.31.0 // indirect
    golang.org/x/arch v0.11.0 // indirect
    golang.org/x/crypto v0.28.0 // indirect
    golang.org/x/net v0.30.0 // indirect
    golang.org/x/sys v0.26.0 // indirect
    golang.org/x/text v0.19.0 // indirect
    google.golang.org/protobuf v1.35.1 // indirect
    gopkg.in/yaml.v3 v3.0.1 // indirect
)

Comment From: huangjilaiqin

yes, this really work ,thk

In otel the currentSpanKey is Type of traceConrextKeyType. In here, you can set ContextWithFallback = true to solue this problem

see: #3993 (comment)

with Go Context standards:

The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys. To avoid allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported context key variables' static type should be a pointer or interface.

ref: https://pkg.go.dev/context#WithValue

Comment From: FarmerChillax

Add example in https://github.com/gin-gonic/examples/pull/171 @appleboy