Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for filters to the otelgin package #2965

Merged
merged 5 commits into from Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

- [otelgin] Add support for filters to the `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin` package to provide the way to control which inbound requests are traced. (#2963)
Orogenesis marked this conversation as resolved.
Show resolved Hide resolved

## [1.11.1/0.36.4/0.5.2]

### Added
Expand Down
Expand Up @@ -54,6 +54,14 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc {
cfg.Propagators = otel.GetTextMapPropagator()
}
return func(c *gin.Context) {
for _, f := range cfg.Filters {
if !f(c.Request) {
// Serve the request to the next middleware
// if a filter rejects the request.
c.Next()
return
}
}
c.Set(tracerKey, tracer)
savedCtx := c.Request.Context()
defer func() {
Expand Down
19 changes: 19 additions & 0 deletions instrumentation/github.com/gin-gonic/gin/otelgin/option.go
Expand Up @@ -17,15 +17,22 @@
package otelgin // import "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"

import (
"net/http"

"go.opentelemetry.io/otel/propagation"
oteltrace "go.opentelemetry.io/otel/trace"
)

type config struct {
TracerProvider oteltrace.TracerProvider
Propagators propagation.TextMapPropagator
Filters []Filter
}

// Filter is a predicate used to determine whether a given http.request should
// be traced. A Filter must return true if the request should be traced.
type Filter func(*http.Request) bool

// Option specifies instrumentation configuration options.
type Option interface {
apply(*config)
Expand Down Expand Up @@ -57,3 +64,15 @@ func WithTracerProvider(provider oteltrace.TracerProvider) Option {
}
})
}

// 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.
// If no filters are provided then all requests are traced.
// Filters will be invoked for each processed request, it is advised to make them
// simple and fast.
func WithFilter(f ...Filter) Option {
return optionFunc(func(c *config) {
c.Filters = append(c.Filters, f...)
})
}
Expand Up @@ -203,3 +203,37 @@ func TestHTML(t *testing.T) {
require.NotNil(t, tspan)
assert.Contains(t, tspan.Attributes(), attribute.String("go.template", "hello"))
}

func TestWithFilter(t *testing.T) {
t.Run("custom filter filtering route", func(t *testing.T) {
sr := tracetest.NewSpanRecorder()
otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)))

router := gin.New()
f := func(req *http.Request) bool { return req.URL.Path != "/healthcheck" }
router.Use(otelgin.Middleware("foobar", otelgin.WithFilter(f)))
router.GET("/healthcheck", func(c *gin.Context) {})

r := httptest.NewRequest("GET", "/healthcheck", nil)
w := httptest.NewRecorder()

router.ServeHTTP(w, r)
assert.Len(t, sr.Ended(), 0)
})

t.Run("custom filter not filtering route", func(t *testing.T) {
sr := tracetest.NewSpanRecorder()
otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)))

router := gin.New()
f := func(req *http.Request) bool { return req.URL.Path != "/healthcheck" }
router.Use(otelgin.Middleware("foobar", otelgin.WithFilter(f)))
router.GET("/user/:id", func(c *gin.Context) {})

r := httptest.NewRequest("GET", "/user/123", nil)
w := httptest.NewRecorder()

router.ServeHTTP(w, r)
assert.Len(t, sr.Ended(), 1)
})
}