gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.

Home Page:https://gin-gonic.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Gin Context implementation breaks context.Context contract

dlinkov-roblox opened this issue · comments

Description

As also described in this issue, open-telemetry/opentelemetry-go-contrib#5277, the existing OpenTelemetry packages do not interact very well with gin.Context. It was pointed out here that this is an example of gin.Context not fulfilling the following specification of context.Context.WithValue: 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.

The two pieces of code that are having colliding values in the context are here for OpenTelemetry, and here for Gin.

How to reproduce

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	oteltrace "go.opentelemetry.io/otel/sdk/trace"
	"go.opentelemetry.io/otel/trace"
)

func main() {
	sampler := oteltrace.AlwaysSample()
	exporter, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())

	tp := oteltrace.NewTracerProvider(
		oteltrace.WithBatcher(exporter),
		oteltrace.WithSampler(sampler),
	)
	otel.SetTracerProvider(tp)
	r := gin.New()
	r.GET("/health_check", otelgin.Middleware("example-service"), HealthCheckHandler)
	r.Run(":9000")
}

func HealthCheckHandler(c *gin.Context) {
	span := trace.SpanFromContext(c)
	c.JSON(http.StatusOK, gin.H{"span_id": span.SpanContext().SpanID()})
}

Expectations

$ curl http://localhost:9000/health_check
<A span ID that is not all zeros>

Actual result

$ curl -i http://localhost:9000/hello/world
<A span ID that is all zeros>

Note that this is an issue as the span context will not be propagated properly unless a user passes c.Request.Context() explicitly. Since a gin.Context implements the context.Context interface, this is a subtle bug that would not be caught by the type system.

Environment

  • go version: 1.19
  • gin version (or commit ref): 1.9.1
  • operating system: Mac OS Sonoma