github.com/gorilla/mux

Package gorilla/mux implements a request router and dispatcher for matching incoming requests to their respective handler.

Add fields to Router

Join Point
Definition of mux.Router
Advice
Introduce new declarations:
// Using the following synthetic imports:
import (
	ddtrace "gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
	http "net/http"
	internal "gopkg.in/DataDog/dd-trace-go.v1/internal"
	telemetry "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry"
	tracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
type ddRouterConfig struct {
	ignoreRequest func(*http.Request) bool
	headerTags    *internal.LockMap
	resourceNamer func(*Router, *http.Request) string
	serviceName   string
	spanOpts      []ddtrace.StartSpanOption
}

func ddDefaultResourceNamer(router *Router, req *http.Request) string {
	var (
		match RouteMatch
		route = "unknown"
	)
	if router.Match(req, &match) && match.Route != nil {
		if r, err := match.Route.GetPathTemplate(); err == nil {
			route = r
		}
	}
	return fmt.Sprintf("%s %s", req.Method, route)
}

func init() {
	telemetry.LoadIntegration("gorilla/mux")
	tracer.MarkIntegrationImported("github.com/gorilla/mux")
}
Advice
Add new field __dd_config of type ddRouterConfig.

Setup in NewRouter

Join Point
All of
Advice
Prepend statements produced by the following template:
// Using the following synthetic imports:
import (
	ddtrace "gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
	ext "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
	globalconfig "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
	http "net/http"
	internal "gopkg.in/DataDog/dd-trace-go.v1/internal"
	math "math"
	namingschema "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema"
	tracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
{{- $res := .Function.Result 0 -}}
defer func() {
	var analyticsRate float64
	if internal.BoolEnv("DD_TRACE_MUX_ANALYTICS_ENABLED", false) {
		analyticsRate = 1.0
	} else {
		analyticsRate = globalconfig.AnalyticsRate()
	}

	{{ $res }}.__dd_config.headerTags = globalconfig.HeaderTagMap()
	{{ $res }}.__dd_config.ignoreRequest = func(*http.Request) bool { return false }
	{{ $res }}.__dd_config.resourceNamer = ddDefaultResourceNamer
	{{ $res }}.__dd_config.serviceName = namingschema.ServiceName("mux.router")
	{{ $res }}.__dd_config.spanOpts = []ddtrace.StartSpanOption{
		tracer.Tag(ext.Component, "gorilla/mux"),
		tracer.Tag(ext.SpanKind, ext.SpanKindServer),
	}
	if !math.IsNaN(analyticsRate) {
		{{ $res }}.__dd_config.spanOpts = append(
			{{ $res }}.__dd_config.spanOpts,
			tracer.Tag(ext.EventSampleRate, analyticsRate),
		)
	}
}()

Instrument Router.ServeHTTP

Join Point
Function body
  • Function declaration
Advice
Prepend statements produced by the following template:
// Using the following synthetic imports:
import (
	http "net/http"
	httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http"
	httptraceinternal "gopkg.in/DataDog/dd-trace-go.v1/contrib/internal/httptrace"
	options "gopkg.in/DataDog/dd-trace-go.v1/contrib/internal/options"
	tracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
{{- $r := .Function.Receiver -}}
{{- $w := .Function.Argument 0 -}}
{{- $req := .Function.Argument 1 -}}
if !{{ $r }}.__dd_config.ignoreRequest({{ $req }}) {
	var (
		match    RouteMatch
		route    string
		spanOpts = options.Copy({{ $r }}.__dd_config.spanOpts...)
	)
	if {{ $r }}.Match({{ $req }}, &match) && match.Route != nil {
		if h, err := match.Route.GetHostTemplate(); err == nil {
			spanOpts = append(spanOpts, tracer.Tag("mux.host", h))
		}
		route, _ = match.Route.GetPathTemplate()
	}
	spanOpts = append(spanOpts, httptraceinternal.HeaderTagsFromRequest({{ $req }}, {{ $r }}.__dd_config.headerTags))
	resource := {{ $r }}.__dd_config.resourceNamer({{ $r }}, {{ $req }})

	// This is a temporary workaround/hack to prevent endless recursion via httptrace.TraceAndServe, which
	// basically implies passing a shallow copy of this router that ignores all requests down to
	// httptrace.TraceAndServe.
	var rCopy Router
	rCopy = *{{ $r }}
	rCopy.__dd_config.ignoreRequest = func(*http.Request) bool { return true }

	httptrace.TraceAndServe(&rCopy, {{ $w }}, {{ $req }}, &httptrace.ServeConfig{
		Service: {{ $r }}.__dd_config.serviceName,
		Resource: resource,
		SpanOpts: spanOpts,
		RouteParams: match.Vars,
		Route: route,
	})
	return
}