Skip to content

Commit

Permalink
[mux] Add request filters like otelhttp (#4230)
Browse files Browse the repository at this point in the history
  • Loading branch information
RangelReale authored Sep 6, 2023
1 parent 3ad5a2c commit aab5f49
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- `WithRouteTag` in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` adds HTTP route attribute to metrics. (#615)
- Add `WithSpanOptions` option in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#3768)
- Add testing support for Go 1.21. (#4233)
- Add `WithFilter` option to `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux`. (#4230)

### Changed

Expand Down
17 changes: 17 additions & 0 deletions instrumentation/github.com/gorilla/mux/otelmux/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type config struct {
spanNameFormatter func(string, *http.Request) string
PublicEndpoint bool
PublicEndpointFn func(*http.Request) bool
Filters []Filter
}

// Option specifies instrumentation configuration options.
Expand All @@ -41,6 +42,10 @@ func (o optionFunc) apply(c *config) {
o(c)
}

// 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

// WithPublicEndpoint configures the Handler to link the span with an incoming
// span context. If this option is not provided, then the association is a child
// association instead of a link.
Expand Down Expand Up @@ -91,3 +96,15 @@ func WithSpanNameFormatter(fn func(routeName string, r *http.Request) string) Op
cfg.spanNameFormatter = fn
})
}

// 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)
})
}
10 changes: 10 additions & 0 deletions instrumentation/github.com/gorilla/mux/otelmux/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func Middleware(service string, opts ...Option) mux.MiddlewareFunc {
spanNameFormatter: cfg.spanNameFormatter,
publicEndpoint: cfg.PublicEndpoint,
publicEndpointFn: cfg.PublicEndpointFn,
filters: cfg.Filters,
}
}
}
Expand All @@ -76,6 +77,7 @@ type traceware struct {
spanNameFormatter func(string, *http.Request) string
publicEndpoint bool
publicEndpointFn func(*http.Request) bool
filters []Filter
}

type recordingResponseWriter struct {
Expand Down Expand Up @@ -127,6 +129,14 @@ func defaultSpanNameFunc(routeName string, _ *http.Request) string { return rout
// ServeHTTP implements the http.Handler interface. It does the actual
// tracing of the request.
func (tw traceware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, f := range tw.filters {
if !f(r) {
// Simply pass through to the handler if a filter rejects the request
tw.handler.ServeHTTP(w, r)
return
}
}

ctx := tw.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
routeStr := ""
route := mux.CurrentRoute(r)
Expand Down
37 changes: 37 additions & 0 deletions instrumentation/github.com/gorilla/mux/otelmux/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,40 @@ func TestResponseWriterInterfaces(t *testing.T) {

router.ServeHTTP(w, r)
}

func TestFilter(t *testing.T) {
prop := propagation.TraceContext{}

router := mux.NewRouter()
var calledHealth, calledTest int
router.Use(Middleware("foobar", WithFilter(func(r *http.Request) bool {
return r.URL.Path != "/health"
})))
router.HandleFunc("/health", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calledHealth++
span := trace.SpanFromContext(r.Context())
assert.NotEqual(t, sc, span.SpanContext())
w.WriteHeader(http.StatusOK)
}))
router.HandleFunc("/test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calledTest++
span := trace.SpanFromContext(r.Context())
assert.Equal(t, sc, span.SpanContext())
w.WriteHeader(http.StatusOK)
}))

r := httptest.NewRequest("GET", "/health", nil)
ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc)
prop.Inject(ctx, propagation.HeaderCarrier(r.Header))
w := httptest.NewRecorder()
router.ServeHTTP(w, r)

r = httptest.NewRequest("GET", "/test", nil)
ctx = trace.ContextWithRemoteSpanContext(context.Background(), sc)
prop.Inject(ctx, propagation.HeaderCarrier(r.Header))
w = httptest.NewRecorder()
router.ServeHTTP(w, r)

assert.Equal(t, 1, calledHealth, "failed to run test")
assert.Equal(t, 1, calledTest, "failed to run test")
}

0 comments on commit aab5f49

Please sign in to comment.