Skip to content

Commit

Permalink
contrib/gorilla/mux: allow custom resource naming (DataDog#617)
Browse files Browse the repository at this point in the history
Adds a new `WithResourceNamer` that allows customizing how resources are named based on information from the router and the request.
  • Loading branch information
tanordheim authored and mingrammer committed Dec 22, 2020
1 parent da700f4 commit 1bacf14
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 5 deletions.
19 changes: 14 additions & 5 deletions contrib/gorilla/mux/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,27 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var (
match mux.RouteMatch
spanopts []ddtrace.StartSpanOption
route = "unknown"
)
// get the resource associated to this request
if r.Match(req, &match) && match.Route != nil {
if r, err := match.Route.GetPathTemplate(); err == nil {
route = r
}
if h, err := match.Route.GetHostTemplate(); err == nil {
spanopts = append(spanopts, tracer.Tag("mux.host", h))
}
}
spanopts = append(spanopts, r.config.spanOpts...)
resource := req.Method + " " + route
resource := r.config.resourceNamer(r, req)
httputil.TraceAndServe(r.Router, w, req, r.config.serviceName, resource, spanopts...)
}

// defaultResourceNamer attempts to quantize the resource for an HTTP request by
// retrieving the path template associated with the route from the request.
func defaultResourceNamer(router *Router, req *http.Request) string {
var match mux.RouteMatch
// get the resource associated with the given request
if router.Match(req, &match) && match.Route != nil {
if r, err := match.Route.GetPathTemplate(); err == nil {
return req.Method + " " + r
}
}
return req.Method + " unknown"
}
20 changes: 20 additions & 0 deletions contrib/gorilla/mux/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,26 @@ func TestAnalyticsSettings(t *testing.T) {
})
}

func TestResourceNamer(t *testing.T) {
staticName := "static resource name"
staticNamer := func(*Router, *http.Request) string {
return staticName
}

assert := assert.New(t)
mt := mocktracer.Start()
defer mt.Stop()
mux := NewRouter(WithResourceNamer(staticNamer))
mux.Handle("/200", okHandler()).Host("localhost")
r := httptest.NewRequest("GET", "http://localhost/200", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, r)

spans := mt.FinishedSpans()
assert.Equal(1, len(spans))
assert.Equal(staticName, spans[0].Tag(ext.ResourceName))
}

func router() http.Handler {
mux := NewRouter(WithServiceName("my-service"))
mux.Handle("/200", okHandler())
Expand Down
11 changes: 11 additions & 0 deletions contrib/gorilla/mux/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package mux

import (
"math"
"net/http"

"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
Expand All @@ -16,6 +17,7 @@ type routerConfig struct {
serviceName string
spanOpts []ddtrace.StartSpanOption // additional span options to be applied
analyticsRate float64
resourceNamer func(*Router, *http.Request) string
}

// RouterOption represents an option that can be passed to NewRouter.
Expand All @@ -24,6 +26,7 @@ type RouterOption func(*routerConfig)
func defaults(cfg *routerConfig) {
cfg.analyticsRate = globalconfig.AnalyticsRate()
cfg.serviceName = "mux.router"
cfg.resourceNamer = defaultResourceNamer
}

// WithServiceName sets the given service name for the router.
Expand Down Expand Up @@ -63,3 +66,11 @@ func WithAnalyticsRate(rate float64) RouterOption {
}
}
}

// WithResourceNamer specifies a quantizing function which will be used to
// obtain the resource name for a given request.
func WithResourceNamer(namer func(router *Router, req *http.Request) string) RouterOption {
return func(cfg *routerConfig) {
cfg.resourceNamer = namer
}
}

0 comments on commit 1bacf14

Please sign in to comment.