Skip to content

Commit aab5f49

Browse files
authored
[mux] Add request filters like otelhttp (#4230)
1 parent 3ad5a2c commit aab5f49

File tree

4 files changed

+65
-0
lines changed

4 files changed

+65
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
3232
- `WithRouteTag` in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` adds HTTP route attribute to metrics. (#615)
3333
- Add `WithSpanOptions` option in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#3768)
3434
- Add testing support for Go 1.21. (#4233)
35+
- Add `WithFilter` option to `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux`. (#4230)
3536

3637
### Changed
3738

instrumentation/github.com/gorilla/mux/otelmux/config.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type config struct {
2828
spanNameFormatter func(string, *http.Request) string
2929
PublicEndpoint bool
3030
PublicEndpointFn func(*http.Request) bool
31+
Filters []Filter
3132
}
3233

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

45+
// Filter is a predicate used to determine whether a given http.request should
46+
// be traced. A Filter must return true if the request should be traced.
47+
type Filter func(*http.Request) bool
48+
4449
// WithPublicEndpoint configures the Handler to link the span with an incoming
4550
// span context. If this option is not provided, then the association is a child
4651
// association instead of a link.
@@ -91,3 +96,15 @@ func WithSpanNameFormatter(fn func(routeName string, r *http.Request) string) Op
9196
cfg.spanNameFormatter = fn
9297
})
9398
}
99+
100+
// WithFilter adds a filter to the list of filters used by the handler.
101+
// If any filter indicates to exclude a request then the request will not be
102+
// traced. All filters must allow a request to be traced for a Span to be created.
103+
// If no filters are provided then all requests are traced.
104+
// Filters will be invoked for each processed request, it is advised to make them
105+
// simple and fast.
106+
func WithFilter(f Filter) Option {
107+
return optionFunc(func(c *config) {
108+
c.Filters = append(c.Filters, f)
109+
})
110+
}

instrumentation/github.com/gorilla/mux/otelmux/mux.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func Middleware(service string, opts ...Option) mux.MiddlewareFunc {
6464
spanNameFormatter: cfg.spanNameFormatter,
6565
publicEndpoint: cfg.PublicEndpoint,
6666
publicEndpointFn: cfg.PublicEndpointFn,
67+
filters: cfg.Filters,
6768
}
6869
}
6970
}
@@ -76,6 +77,7 @@ type traceware struct {
7677
spanNameFormatter func(string, *http.Request) string
7778
publicEndpoint bool
7879
publicEndpointFn func(*http.Request) bool
80+
filters []Filter
7981
}
8082

8183
type recordingResponseWriter struct {
@@ -127,6 +129,14 @@ func defaultSpanNameFunc(routeName string, _ *http.Request) string { return rout
127129
// ServeHTTP implements the http.Handler interface. It does the actual
128130
// tracing of the request.
129131
func (tw traceware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
132+
for _, f := range tw.filters {
133+
if !f(r) {
134+
// Simply pass through to the handler if a filter rejects the request
135+
tw.handler.ServeHTTP(w, r)
136+
return
137+
}
138+
}
139+
130140
ctx := tw.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
131141
routeStr := ""
132142
route := mux.CurrentRoute(r)

instrumentation/github.com/gorilla/mux/otelmux/mux_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,40 @@ func TestResponseWriterInterfaces(t *testing.T) {
162162

163163
router.ServeHTTP(w, r)
164164
}
165+
166+
func TestFilter(t *testing.T) {
167+
prop := propagation.TraceContext{}
168+
169+
router := mux.NewRouter()
170+
var calledHealth, calledTest int
171+
router.Use(Middleware("foobar", WithFilter(func(r *http.Request) bool {
172+
return r.URL.Path != "/health"
173+
})))
174+
router.HandleFunc("/health", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
175+
calledHealth++
176+
span := trace.SpanFromContext(r.Context())
177+
assert.NotEqual(t, sc, span.SpanContext())
178+
w.WriteHeader(http.StatusOK)
179+
}))
180+
router.HandleFunc("/test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
181+
calledTest++
182+
span := trace.SpanFromContext(r.Context())
183+
assert.Equal(t, sc, span.SpanContext())
184+
w.WriteHeader(http.StatusOK)
185+
}))
186+
187+
r := httptest.NewRequest("GET", "/health", nil)
188+
ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc)
189+
prop.Inject(ctx, propagation.HeaderCarrier(r.Header))
190+
w := httptest.NewRecorder()
191+
router.ServeHTTP(w, r)
192+
193+
r = httptest.NewRequest("GET", "/test", nil)
194+
ctx = trace.ContextWithRemoteSpanContext(context.Background(), sc)
195+
prop.Inject(ctx, propagation.HeaderCarrier(r.Header))
196+
w = httptest.NewRecorder()
197+
router.ServeHTTP(w, r)
198+
199+
assert.Equal(t, 1, calledHealth, "failed to run test")
200+
assert.Equal(t, 1, calledTest, "failed to run test")
201+
}

0 commit comments

Comments
 (0)