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

[mux] Add request filters like otelhttp #4230

Merged
merged 5 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
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")
}