From 1c2b22097c858a4added81fbcb6c951a920c4a8e Mon Sep 17 00:00:00 2001 From: Guntur Akhmad Fauzi Date: Mon, 14 Oct 2024 12:27:32 +0700 Subject: [PATCH 1/6] feat(otelgin): Support metrics for otelgin --- .../gin-gonic/gin/otelgin/gintrace.go | 110 +++++++++++++++++- .../gin-gonic/gin/otelgin/gintrace_test.go | 69 +++++++++++ .../gin-gonic/gin/otelgin/option.go | 17 +++ 3 files changed, 194 insertions(+), 2 deletions(-) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go index 0aa1d7129d0..a09cbb469c7 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go @@ -17,7 +17,10 @@ package otelgin // import "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" import ( + "bytes" "fmt" + "io" + "time" "github.com/gin-gonic/gin" @@ -25,6 +28,8 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + otelmetric "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/propagation" semconv "go.opentelemetry.io/otel/semconv/v1.20.0" oteltrace "go.opentelemetry.io/otel/trace" @@ -54,6 +59,56 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { if cfg.Propagators == nil { cfg.Propagators = otel.GetTextMapPropagator() } + + if cfg.MeterProvider == nil { + cfg.MeterProvider = otel.GetMeterProvider() + } + meter := cfg.MeterProvider.Meter( + ScopeName, + otelmetric.WithInstrumentationVersion(Version()), + ) + + var err error + cfg.reqDuration, err = meter.Float64Histogram("http.server.request.duration", + otelmetric.WithDescription("Measures the duration of inbound RPC."), + otelmetric.WithUnit("ms")) + if err != nil { + otel.Handle(err) + if cfg.reqDuration == nil { + cfg.reqDuration = noop.Float64Histogram{} + } + } + + cfg.reqSize, err = meter.Int64UpDownCounter("http.server.request.body.size", + otelmetric.WithDescription("Measures size of RPC request messages (uncompressed)."), + otelmetric.WithUnit("By")) + if err != nil { + otel.Handle(err) + if cfg.reqSize == nil { + cfg.reqSize = noop.Int64UpDownCounter{} + } + } + + cfg.respSize, err = meter.Int64UpDownCounter("http.server.response.body.size", + otelmetric.WithDescription("Measures size of RPC response messages (uncompressed)."), + otelmetric.WithUnit("By")) + if err != nil { + otel.Handle(err) + if cfg.respSize == nil { + cfg.respSize = noop.Int64UpDownCounter{} + } + } + + cfg.activeReqs, err = meter.Int64UpDownCounter("http.server.active_requests", + otelmetric.WithDescription("Measures the number of messages received per RPC. Should be 1 for all non-streaming RPCs."), + otelmetric.WithUnit("{count}")) + if err != nil { + otel.Handle(err) + if cfg.activeReqs == nil { + cfg.activeReqs = noop.Int64UpDownCounter{} + } + } + return func(c *gin.Context) { for _, f := range cfg.Filters { if !f(c.Request) { @@ -73,6 +128,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { oteltrace.WithAttributes(semconvutil.HTTPServerRequest(service, c.Request)...), oteltrace.WithSpanKind(oteltrace.SpanKindServer), } + metricAttrs := semconvutil.HTTPServerRequestMetrics(service, c.Request) var spanName string if cfg.SpanNameFormatter == nil { spanName = c.FullPath() @@ -84,24 +140,47 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { } else { rAttr := semconv.HTTPRoute(spanName) opts = append(opts, oteltrace.WithAttributes(rAttr)) + metricAttrs = append(metricAttrs, rAttr) } ctx, span := tracer.Start(ctx, spanName, opts...) defer span.End() // pass the span through the request context c.Request = c.Request.WithContext(ctx) + // calculate the size of the request. + reqSize := calcReqSize(c) + before := time.Now() // serve the request to the next middleware c.Next() + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedTime := float64(time.Since(before)) / float64(time.Millisecond) + respSize := c.Writer.Size() + // If nothing written in the response yet, a value of -1 may be returned. + if respSize < 0 { + respSize = 0 + } + status := c.Writer.Status() span.SetStatus(semconvutil.HTTPServerStatus(status)) + + metricAttrSet := attribute.NewSet(metricAttrs...) // create a set to minimize alloc + cfg.reqSize.Add(ctx, int64(reqSize), otelmetric.WithAttributeSet(metricAttrSet)) + cfg.respSize.Add(ctx, int64(respSize), otelmetric.WithAttributeSet(metricAttrSet)) + + extraAttrs := make([]attribute.KeyValue, 0, 2) // pre-alloc cap of 2 if status > 0 { - span.SetAttributes(semconv.HTTPStatusCode(status)) + extraAttrs = append(extraAttrs, semconv.HTTPStatusCode(status)) } if len(c.Errors) > 0 { - span.SetAttributes(attribute.String("gin.errors", c.Errors.String())) + extraAttrs = append(extraAttrs, attribute.String("gin.errors", c.Errors.String())) } + span.SetAttributes(extraAttrs...) // call set just once + + extraMetricAttrSet := attribute.NewSet(extraAttrs...) // create another set to minimize alloc + cfg.reqDuration.Record(ctx, elapsedTime, otelmetric.WithAttributeSet(metricAttrSet), otelmetric.WithAttributeSet(extraMetricAttrSet)) + cfg.activeReqs.Add(ctx, 1, otelmetric.WithAttributeSet(metricAttrSet), otelmetric.WithAttributeSet(extraMetricAttrSet)) } } @@ -139,3 +218,30 @@ func HTML(c *gin.Context, code int, name string, obj interface{}) { }() c.HTML(code, name, obj) } + +// calcReqSize returns the total size of the request. +// It will calculate the header size by iterate all the header KVs +// and add with body size. +func calcReqSize(c *gin.Context) int { + // Calculate the size of headers + headerSize := 0 + for name, values := range c.Request.Header { + headerSize += len(name) + 2 // Colon and space + for _, value := range values { + headerSize += len(value) + } + } + + // Read the request body + body, err := io.ReadAll(c.Request.Body) + if err != nil { + // can't read the body, just return the headerSize. + return headerSize + } + + // Restore the request body for further processing + c.Request.Body = io.NopCloser(bytes.NewReader(body)) + + // Calculate the total size of the request (headers + body) + return headerSize + len(body) +} diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace_test.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace_test.go index 66cb4ff9631..5d5025e5c6a 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace_test.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace_test.go @@ -17,7 +17,9 @@ package otelgin import ( + "bytes" "context" + "io" "net/http" "net/http/httptest" "testing" @@ -106,3 +108,70 @@ func TestPropagationWithCustomPropagators(t *testing.T) { router.ServeHTTP(w, r) } + +// TestCalcReqSize tests the calcReqSize function. +func TestCalcReqSize(t *testing.T) { + // Create a sample request with a body and headers + body := []byte("sample body") + req, err := http.NewRequest("POST", "/test", bytes.NewReader(body)) + if err != nil { + t.Fatalf("Failed to create request: %v", err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer token") + + // Create a Gin context with the request + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = req + + // Call the function to calculate the request size + size := calcReqSize(c) + + // Calculate the expected size (body + headers + extra bytes for header formatting) + expectedSize := len(body) + len("Content-Type") + len("application/json") + len("Authorization") + len("Bearer token") + 4 // 4 extra bytes for ": " and "\r\n" + + // Check if the calculated size matches the expected size + if size != expectedSize { + t.Errorf("Expected request size %d, got %d", expectedSize, size) + } +} + +// TestCalcReqSizeWithBodyRead tests the calcReqSize function and ensures the request body can still be read afterward. +func TestCalcReqSizeWithBodyRead(t *testing.T) { + // Create a sample request with a body and headers + body := []byte("sample body") + req, err := http.NewRequest("POST", "/test", bytes.NewReader(body)) + if err != nil { + t.Fatalf("Failed to create request: %v", err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer token") + + // Create a Gin context with the request + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = req + + // Call the function to calculate the request size + size := calcReqSize(c) + + // Calculate the expected size (body + headers + extra bytes for header formatting) + expectedSize := len(body) + len("Content-Type") + len("application/json") + len("Authorization") + len("Bearer token") + 4 // 4 extra bytes for ": " and "\r\n" + + // Check if the calculated size matches the expected size + if size != expectedSize { + t.Errorf("Expected request size %d, got %d", expectedSize, size) + } + + // Read the request body again + newBody, err := io.ReadAll(c.Request.Body) + if err != nil { + t.Fatalf("Failed to read request body: %v", err) + } + + // Check if the body is unchanged + if !bytes.Equal(newBody, body) { + t.Errorf("Expected request body %q, got %q", body, newBody) + } +} diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/option.go b/instrumentation/github.com/gin-gonic/gin/otelgin/option.go index c13c3dad815..7cfb374639e 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/option.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/option.go @@ -19,15 +19,22 @@ package otelgin // import "go.opentelemetry.io/contrib/instrumentation/github.co import ( "net/http" + otelmetric "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" oteltrace "go.opentelemetry.io/otel/trace" ) type config struct { TracerProvider oteltrace.TracerProvider + MeterProvider otelmetric.MeterProvider Propagators propagation.TextMapPropagator Filters []Filter SpanNameFormatter SpanNameFormatter + + reqDuration otelmetric.Float64Histogram + reqSize otelmetric.Int64UpDownCounter + respSize otelmetric.Int64UpDownCounter + activeReqs otelmetric.Int64UpDownCounter } // Filter is a predicate used to determine whether a given http.request should @@ -69,6 +76,16 @@ func WithTracerProvider(provider oteltrace.TracerProvider) Option { }) } +// WithMeterProvider specifies a meter provider to use for creating a meter. +// If none is specified, the global provider is used. +func WithMeterProvider(provider otelmetric.MeterProvider) Option { + return optionFunc(func(cfg *config) { + if provider != nil { + cfg.MeterProvider = provider + } + }) +} + // WithFilter adds a filter to the list of filters used by the handler. // If any filter indicates to exclude a request then the request will not be // traced. All filters must allow a request to be traced for a Span to be created. From 0d1d18a8d7d698bd38be62971f6904cfb678c295 Mon Sep 17 00:00:00 2001 From: Guntur Akhmad Fauzi Date: Mon, 14 Oct 2024 12:34:44 +0700 Subject: [PATCH 2/6] docs(CHANGELOG): update changelog --- CHANGELOG.md | 1 + instrumentation/github.com/gin-gonic/gin/otelgin/go.mod | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0227cc707b..91805bf494b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108) +- Support metrics for `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6245) ## [1.22.0/0.47.0/0.16.0/0.2.0] - 2024-01-18 diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/go.mod b/instrumentation/github.com/gin-gonic/gin/otelgin/go.mod index 8789c8f4917..c6b84d8565a 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/go.mod +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/go.mod @@ -9,6 +9,7 @@ require ( github.com/stretchr/testify v1.8.4 go.opentelemetry.io/contrib/propagators/b3 v1.22.0 go.opentelemetry.io/otel v1.22.0 + go.opentelemetry.io/otel/metric v1.22.0 go.opentelemetry.io/otel/trace v1.22.0 ) @@ -34,7 +35,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - go.opentelemetry.io/otel/metric v1.22.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.18.0 // indirect golang.org/x/net v0.20.0 // indirect From f3da1c6b2ee4815787fa1c226a00330a2dccea7b Mon Sep 17 00:00:00 2001 From: Guntur Akhmad Fauzi Date: Mon, 14 Oct 2024 12:51:40 +0700 Subject: [PATCH 3/6] docs(CHANGELOG): remove conflict --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 041765bd9f7..8f29a9fbfb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -254,7 +254,6 @@ The next release will require at least [Go 1.21]. ### Fixed - Do not panic in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` if `MeterProvider` returns a `nil` instrument. (#4875) ->>>>>>> main ## [1.22.0/0.47.0/0.16.0/0.2.0] - 2024-01-18 From 6403414fec14748bfc6fbd54e94121fcd47a5b49 Mon Sep 17 00:00:00 2001 From: Guntur Akhmad Fauzi Date: Mon, 14 Oct 2024 13:01:46 +0700 Subject: [PATCH 4/6] fix(otelgin): do not override spanname if empty --- instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go index 3e6a3a8a1d5..6acd87976b2 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go @@ -136,9 +136,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { if spanName == "" { spanName = fmt.Sprintf("HTTP %s route not found", c.Request.Method) } else { - rAttr := semconv.HTTPRoute(spanName) - opts = append(opts, oteltrace.WithAttributes(rAttr)) - metricAttrs = append(metricAttrs, rAttr) + metricAttrs = append(metricAttrs, semconv.HTTPRoute(spanName)) } ctx, span := tracer.Start(ctx, spanName, opts...) defer span.End() From 10cbf417595f003f6b5a02adf5bfa21c20fdf52b Mon Sep 17 00:00:00 2001 From: Guntur Akhmad Fauzi Date: Mon, 14 Oct 2024 13:55:07 +0700 Subject: [PATCH 5/6] test(otelgin): add metric tests --- .../gin-gonic/gin/otelgin/gintrace.go | 32 ++--- .../gin/otelgin/test/gintrace_test.go | 120 ++++++++++++++++++ 2 files changed, 136 insertions(+), 16 deletions(-) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go index 6acd87976b2..beaf53cc79a 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go @@ -59,7 +59,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { var err error cfg.reqDuration, err = meter.Float64Histogram("http.server.request.duration", - otelmetric.WithDescription("Measures the duration of inbound RPC."), + otelmetric.WithDescription("Duration of HTTP server requests."), otelmetric.WithUnit("ms")) if err != nil { otel.Handle(err) @@ -69,7 +69,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { } cfg.reqSize, err = meter.Int64UpDownCounter("http.server.request.body.size", - otelmetric.WithDescription("Measures size of RPC request messages (uncompressed)."), + otelmetric.WithDescription("Size of HTTP server request bodies."), otelmetric.WithUnit("By")) if err != nil { otel.Handle(err) @@ -79,7 +79,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { } cfg.respSize, err = meter.Int64UpDownCounter("http.server.response.body.size", - otelmetric.WithDescription("Measures size of RPC response messages (uncompressed)."), + otelmetric.WithDescription("Size of HTTP server response bodies."), otelmetric.WithUnit("By")) if err != nil { otel.Handle(err) @@ -89,8 +89,8 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { } cfg.activeReqs, err = meter.Int64UpDownCounter("http.server.active_requests", - otelmetric.WithDescription("Measures the number of messages received per RPC. Should be 1 for all non-streaming RPCs."), - otelmetric.WithUnit("{count}")) + otelmetric.WithDescription("Number of active HTTP server requests."), + otelmetric.WithUnit("{request}")) if err != nil { otel.Handle(err) if cfg.activeReqs == nil { @@ -161,22 +161,22 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { status := c.Writer.Status() span.SetStatus(semconvutil.HTTPServerStatus(status)) - metricAttrSet := attribute.NewSet(metricAttrs...) // create a set to minimize alloc - cfg.reqSize.Add(ctx, int64(reqSize), otelmetric.WithAttributeSet(metricAttrSet)) - cfg.respSize.Add(ctx, int64(respSize), otelmetric.WithAttributeSet(metricAttrSet)) - - extraAttrs := make([]attribute.KeyValue, 0, 2) // pre-alloc cap of 2 if status > 0 { - extraAttrs = append(extraAttrs, semconv.HTTPStatusCode(status)) + statCodeAttr := semconv.HTTPStatusCode(status) + metricAttrs = append(metricAttrs, statCodeAttr) + span.SetAttributes(statCodeAttr) } if len(c.Errors) > 0 { - extraAttrs = append(extraAttrs, attribute.String("gin.errors", c.Errors.String())) + ginErrAttr := attribute.String("gin.errors", c.Errors.String()) + metricAttrs = append(metricAttrs, ginErrAttr) + span.SetAttributes(ginErrAttr) } - span.SetAttributes(extraAttrs...) // call set just once - extraMetricAttrSet := attribute.NewSet(extraAttrs...) // create another set to minimize alloc - cfg.reqDuration.Record(ctx, elapsedTime, otelmetric.WithAttributeSet(metricAttrSet), otelmetric.WithAttributeSet(extraMetricAttrSet)) - cfg.activeReqs.Add(ctx, 1, otelmetric.WithAttributeSet(metricAttrSet), otelmetric.WithAttributeSet(extraMetricAttrSet)) + metricAttrSet := attribute.NewSet(metricAttrs...) // create a set to minimize alloc + cfg.reqSize.Add(ctx, int64(reqSize), otelmetric.WithAttributeSet(metricAttrSet)) + cfg.respSize.Add(ctx, int64(respSize), otelmetric.WithAttributeSet(metricAttrSet)) + cfg.reqDuration.Record(ctx, elapsedTime, otelmetric.WithAttributeSet(metricAttrSet)) + cfg.activeReqs.Add(ctx, 1, otelmetric.WithAttributeSet(metricAttrSet)) } } 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 fb8698422dc..8a1bb47cbc1 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 @@ -6,6 +6,7 @@ package test import ( + "context" "errors" "html/template" "net/http" @@ -20,8 +21,13 @@ import ( "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" + semconv "go.opentelemetry.io/otel/semconv/v1.20.0" "go.opentelemetry.io/otel/attribute" oteltrace "go.opentelemetry.io/otel/trace" @@ -321,3 +327,117 @@ func TestWithGinFilter(t *testing.T) { assert.Len(t, sr.Ended(), 1) }) } + +func TestMetric(t *testing.T) { + t.Setenv("OTEL_METRICS_EXEMPLAR_FILTER", "always_off") + sr := tracetest.NewSpanRecorder() + provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + + mr := metric.NewManualReader() + mp := metric.NewMeterProvider(metric.WithReader(mr)) + + router := gin.New() + router.Use(otelgin.Middleware("foobar", + otelgin.WithTracerProvider(provider), + otelgin.WithMeterProvider(mp), + ), + ) + router.GET("/user/:id", func(c *gin.Context) { + id := c.Param("id") + _, _ = c.Writer.Write([]byte(id)) + }) + + r := httptest.NewRequest("GET", "/user/123", nil) + w := httptest.NewRecorder() + + // do and verify the request + router.ServeHTTP(w, r) + response := w.Result() //nolint:bodyclose // False positive for httptest.ResponseRecorder: https://github.com/timakin/bodyclose/issues/59. + require.Equal(t, http.StatusOK, response.StatusCode) + + // verify traces look good + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + assert.Equal(t, "/user/:id", span.Name()) + assert.Equal(t, oteltrace.SpanKindServer, span.SpanKind()) + attr := span.Attributes() + assert.Contains(t, attr, attribute.String("http.method", "GET")) + assert.Contains(t, attr, attribute.String("http.route", "/user/:id")) + + // verify metrics look good. + rm := metricdata.ResourceMetrics{} + err := mr.Collect(context.Background(), &rm) + assert.NoError(t, err) + require.Len(t, rm.ScopeMetrics, 1) + attrs := []attribute.KeyValue{ + semconv.HTTPMethod("GET"), + semconv.HTTPRoute("/user/:id"), + semconv.HTTPSchemeHTTP, + semconv.HTTPStatusCode(200), + semconv.NetHostName("foobar"), + semconv.NetProtocolName("http"), + semconv.NetProtocolVersion("1.1"), + } + want := metricdata.ScopeMetrics{ + Scope: instrumentation.Scope{ + Name: "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin", + Version: otelgin.Version(), + }, + Metrics: []metricdata.Metrics{ + { + Name: "http.server.request.duration", + Description: "Duration of HTTP server requests.", + Unit: "ms", + Data: metricdata.Histogram[float64]{ + Temporality: metricdata.CumulativeTemporality, + DataPoints: []metricdata.HistogramDataPoint[float64]{ + { + Attributes: attribute.NewSet(attrs...), + }, + }, + }, + }, + { + Name: "http.server.request.body.size", + Description: "Size of HTTP server request bodies.", + Unit: "By", + Data: metricdata.Sum[int64]{ + Temporality: metricdata.CumulativeTemporality, + DataPoints: []metricdata.DataPoint[int64]{ + { + Attributes: attribute.NewSet(attrs...), + }, + }, + }, + }, + { + Name: "http.server.response.body.size", + Description: "Size of HTTP server response bodies.", + Unit: "By", + Data: metricdata.Sum[int64]{ + Temporality: metricdata.CumulativeTemporality, + DataPoints: []metricdata.DataPoint[int64]{ + { + Attributes: attribute.NewSet(attrs...), + }, + }, + }, + }, + { + Name: "http.server.active_requests", + Description: "Number of active HTTP server requests.", + Unit: "{request}", + Data: metricdata.Sum[int64]{ + Temporality: metricdata.CumulativeTemporality, + DataPoints: []metricdata.DataPoint[int64]{ + { + Attributes: attribute.NewSet(attrs...), + }, + }, + }, + }, + }, + } + metricdatatest.AssertEqual(t, want, rm.ScopeMetrics[0], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue()) +} From 5d2281ccf24eba7b55b546c7fa075440d9fcb337 Mon Sep 17 00:00:00 2001 From: Guntur Akhmad Fauzi Date: Mon, 14 Oct 2024 13:56:05 +0700 Subject: [PATCH 6/6] chore(otelgin): tidy deps --- instrumentation/github.com/gin-gonic/gin/otelgin/test/go.mod | 1 + instrumentation/github.com/gin-gonic/gin/otelgin/test/go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.mod b/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.mod index 19f81273e99..6c0f5c8aa86 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.mod +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.mod @@ -8,6 +8,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0 go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel/sdk v1.31.0 + go.opentelemetry.io/otel/sdk/metric v1.31.0 go.opentelemetry.io/otel/trace v1.31.0 ) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.sum b/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.sum index af428acc453..7abf05af93e 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.sum +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/test/go.sum @@ -75,6 +75,8 @@ go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozR go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=