diff --git a/CHANGELOG.md b/CHANGELOG.md index 1846b0348d8..26f1b9bdd15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Added support for providing `endpoint`, `pollingIntervalMs` and `initialSamplingRate` using environment variable `OTEL_TRACES_SAMPLER_ARG` in `go.opentelemetry.io/contrib/samples/jaegerremote`. (#6310) - Added support exporting logs via OTLP over gRPC in `go.opentelemetry.io/contrib/config`. (#6340) +### Changed + +- Record errors instead of setting the `gin.errors` attribute in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6346) + ### Fixed - Fix broken AWS presigned URLs when using instrumentation in `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws`. (#5975) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go index 1affd4d6ca5..7582d5a4184 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go @@ -95,7 +95,9 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { span.SetAttributes(semconv.HTTPStatusCode(status)) } if len(c.Errors) > 0 { - span.SetAttributes(attribute.String("gin.errors", c.Errors.String())) + for _, err := range c.Errors { + span.RecordError(err.Err) + } } } } diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go b/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go index e648640ab58..23cb66723ce 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go @@ -164,6 +164,7 @@ func TestTrace200(t *testing.T) { assert.Contains(t, attr, attribute.Int("http.status_code", http.StatusOK)) assert.Contains(t, attr, attribute.String("http.method", "GET")) assert.Contains(t, attr, attribute.String("http.route", "/user/:id")) + assert.Empty(t, span.Events()) } func TestError(t *testing.T) { @@ -177,7 +178,8 @@ func TestError(t *testing.T) { // configure a handler that returns an error and 5xx status // code router.GET("/server_err", func(c *gin.Context) { - _ = c.AbortWithError(http.StatusInternalServerError, errors.New("oh no")) + _ = c.Error(errors.New("oh no one")) + _ = c.AbortWithError(http.StatusInternalServerError, errors.New("oh no two")) }) r := httptest.NewRequest("GET", "/server_err", nil) w := httptest.NewRecorder() @@ -193,7 +195,17 @@ func TestError(t *testing.T) { attr := span.Attributes() assert.Contains(t, attr, attribute.String("net.host.name", "foobar")) assert.Contains(t, attr, attribute.Int("http.status_code", http.StatusInternalServerError)) - assert.Contains(t, attr, attribute.String("gin.errors", "Error #01: oh no\n")) + + // verify the error events + events := span.Events() + assert.Len(t, events, 2) + assert.Equal(t, "exception", events[0].Name) + assert.Contains(t, events[0].Attributes, attribute.String("exception.type", "*errors.errorString")) + assert.Contains(t, events[0].Attributes, attribute.String("exception.message", "oh no one")) + assert.Equal(t, "exception", events[1].Name) + assert.Contains(t, events[1].Attributes, attribute.String("exception.type", "*errors.errorString")) + assert.Contains(t, events[1].Attributes, attribute.String("exception.message", "oh no two")) + // server errors set the status assert.Equal(t, codes.Error, span.Status().Code) }