contrib/gorilla/mux

Those integration are enabled by having the following import in the project’s orchestrion.tool.go file:

import (
	_ "github.com/DataDog/orchestrion"

	_ "github.com/DataDog/dd-trace-go/contrib/gorilla/mux/v2" // integration
	//...
)

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

Router.__dd_config

Join Point
Definition of mux.Router
Advice
Introduce new declarations:
// Using the following synthetic imports:
import (
	http "net/http"
	instrumentation "github.com/DataDog/dd-trace-go/v2/instrumentation"
	tracer "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)
var __dd_instr *instrumentation.Instrumentation

func init() {
	__dd_instr = instrumentation.Load(instrumentation.PackageGorillaMux)
}

type ddRouterConfig struct {
	ignoreRequest func(*http.Request) bool
	headerTags    instrumentation.HeaderTags
	resourceNamer func(*Router, *http.Request) string
	serviceName   string
	spanOpts      []tracer.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)
}
Advice
Add new field __dd_config of type ddRouterConfig.

NewRouter

Join Point
All of
Advice
Prepend statements produced by the following template:
// Using the following synthetic imports:
import (
	ext "github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
	http "net/http"
	instrumentation "github.com/DataDog/dd-trace-go/v2/instrumentation"
	math "math"
	tracer "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)
{{- $res := .Function.Result 0 -}}
defer func() {
	analyticsRate := __dd_instr.AnalyticsRate(true)
	{{ $res }}.__dd_config.headerTags = __dd_instr.HTTPHeadersAsTags()
	{{ $res }}.__dd_config.serviceName = __dd_instr.ServiceName(instrumentation.ComponentServer, nil)
	{{ $res }}.__dd_config.resourceNamer = ddDefaultResourceNamer
	{{ $res }}.__dd_config.ignoreRequest = func(_ *http.Request) bool { return false }

	{{ $res }}.__dd_config.spanOpts = []tracer.StartSpanOption{
		tracer.Tag(ext.Component, instrumentation.PackageGorillaMux),
		tracer.Tag(ext.SpanKind, ext.SpanKindServer),
	}
	if !math.IsNaN(analyticsRate) {
		{{ $res }}.__dd_config.spanOpts = append(
			{{ $res }}.__dd_config.spanOpts,
			tracer.Tag(ext.EventSampleRate, analyticsRate),
		)
	}
}()

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 "github.com/DataDog/dd-trace-go/contrib/net/http/v2"
	instrhttptrace "github.com/DataDog/dd-trace-go/v2/instrumentation/httptrace"
	options "github.com/DataDog/dd-trace-go/v2/instrumentation/options"
	tracer "github.com/DataDog/dd-trace-go/v2/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, instrhttptrace.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
}