Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add `WithAttributes` option to set instrumentation scope attributes on the created `log.Logger` in `go.opentelemetry.io/contrib/bridges/otelslog`. (#6965)
- Add `WithAttributes` option to set instrumentation scope attributes on the created `log.Logger` in `go.opentelemetry.io/contrib/bridges/otellogrus`. (#6966)
- Add `WithAttributes` option to set instrumentation scope attributes on the created `log.Logger` in `go.opentelemetry.io/contrib/bridges/otellogr`. (#6967)
- Add the `WithGinMetricAttributes` option to allow setting dynamic, per-request metric attributes based on `*gin.Context` in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6932)

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import (
)

type config struct {
TracerProvider oteltrace.TracerProvider
Propagators propagation.TextMapPropagator
Filters []Filter
GinFilters []GinFilter
SpanNameFormatter SpanNameFormatter
MeterProvider metric.MeterProvider
MetricAttributeFn MetricAttributeFn
TracerProvider oteltrace.TracerProvider
Propagators propagation.TextMapPropagator
Filters []Filter
GinFilters []GinFilter
SpanNameFormatter SpanNameFormatter
MeterProvider metric.MeterProvider
MetricAttributeFn MetricAttributeFn
GinMetricAttributeFn GinMetricAttributeFn
}

// Filter is a predicate used to determine whether a given http.request should
Expand All @@ -41,6 +42,10 @@ type SpanNameFormatter func(r *http.Request) string
// and return them as a slice of attribute.KeyValue.
type MetricAttributeFn func(*http.Request) []attribute.KeyValue

// GinMetricAttributeFn is used to extract additional attributes from the gin.Context
// and return them as a slice of attribute.KeyValue.
type GinMetricAttributeFn func(*gin.Context) []attribute.KeyValue

// Option specifies instrumentation configuration options.
type Option interface {
apply(*config)
Expand Down Expand Up @@ -110,8 +115,20 @@ func WithMeterProvider(mp metric.MeterProvider) Option {

// WithMetricAttributeFn specifies a function that extracts additional attributes from the http.Request
// and returns them as a slice of attribute.KeyValue.
//
// If attributes are duplicated between this method and `WithGinMetricAttributeFn`, the attributes in this method will be overridden.
func WithMetricAttributeFn(f MetricAttributeFn) Option {
return optionFunc(func(c *config) {
c.MetricAttributeFn = f
})
}

// WithGinMetricAttributeFn specifies a function that extracts additional attributes from the gin.Context
// and returns them as a slice of attribute.KeyValue.
Comment thread
dmathieu marked this conversation as resolved.
//
// If attributes are duplicated between this method and `WithMetricAttributeFn`, the attributes in this method will be used.
func WithGinMetricAttributeFn(f GinMetricAttributeFn) Option {
return optionFunc(func(c *config) {
c.GinMetricAttributeFn = f
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc {
// Record the server-side attributes.
var additionalAttributes []attribute.KeyValue
if cfg.MetricAttributeFn != nil {
additionalAttributes = cfg.MetricAttributeFn(c.Request)
additionalAttributes = append(additionalAttributes, cfg.MetricAttributeFn(c.Request)...)
}
if cfg.GinMetricAttributeFn != nil {
additionalAttributes = append(additionalAttributes, cfg.GinMetricAttributeFn(c)...)
}

sc.RecordMetrics(ctx, semconv.ServerMetricData{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,17 +439,30 @@ func TestWithGinFilter(t *testing.T) {

func TestMetrics(t *testing.T) {
tests := []struct {
name string
metricAttributeExtractor func(*http.Request) []attribute.KeyValue
name string
metricAttributeExtractor func(*http.Request) []attribute.KeyValue
ginMetricAttributeExtractor func(*gin.Context) []attribute.KeyValue
}{
{"default", nil},
{"with metric attributes callback", func(req *http.Request) []attribute.KeyValue {
return []attribute.KeyValue{
attribute.String("key1", "value1"),
attribute.String("key2", "value"),
attribute.String("method", strings.ToUpper(req.Method)),
}
}},
{
name: "default",
metricAttributeExtractor: nil,
ginMetricAttributeExtractor: nil,
},
{
name: "with metric attributes callback",
metricAttributeExtractor: func(r *http.Request) []attribute.KeyValue {
return []attribute.KeyValue{
attribute.String("key1", "value1"),
attribute.String("key2", "value"),
attribute.String("method", strings.ToUpper(r.Method)),
}
},
ginMetricAttributeExtractor: func(c *gin.Context) []attribute.KeyValue {
return []attribute.KeyValue{
attribute.String("key3", "value3"),
}
},
},
}

for _, tt := range tests {
Expand All @@ -461,6 +474,7 @@ func TestMetrics(t *testing.T) {
router.Use(otelgin.Middleware("foobar",
otelgin.WithMeterProvider(meterProvider),
otelgin.WithMetricAttributeFn(tt.metricAttributeExtractor),
otelgin.WithGinMetricAttributeFn(tt.ginMetricAttributeExtractor),
))
router.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
Expand All @@ -470,6 +484,8 @@ func TestMetrics(t *testing.T) {

r := httptest.NewRequest("GET", "/user/123", nil)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = r
router.ServeHTTP(w, r)

// verify metrics
Expand All @@ -493,6 +509,9 @@ func TestMetrics(t *testing.T) {
if tt.metricAttributeExtractor != nil {
attrs = append(attrs, tt.metricAttributeExtractor(r)...)
}
if tt.ginMetricAttributeExtractor != nil {
attrs = append(attrs, tt.ginMetricAttributeExtractor(c)...)
}

metricdatatest.AssertEqual(t, metricdata.Metrics{
Name: "http.server.request.size",
Expand Down
Loading