dimfeld / httptreemux

High-speed, flexible tree-based HTTP router for Go.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Expose LookupResult params field

devillecodes opened this issue · comments

We would like to be able to instrument httptreemux for observability with Datadog, similar to what was done for julienschmidt/httprouter in DataDog/dd-trace-go/contrib/julienschmidt/httprouter/httprouter.go.

In short, in order to build the Datadog resource name from the request path we need access to the LookupResult params field. For this reason we would like to export this struct field, i.e. rename LookupResult.params to LookupResult.Params.

This is what the router could could look like to add Datadog trace support to httptreemux:

// Package httptreemux provides functions to trace the dimfeld/httptreemux/v5 package (https://github.com/dimfeld/httptreemux).
package httptreemux // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/dimfeld/httptreemux/v5"

import (
	"math"
	"net/http"
	"strings"

	httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http"
	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
	"gopkg.in/DataDog/dd-trace-go.v1/internal/log"

	"github.com/dimfeld/httptreemux/v5"
)

// Router is a traced version of httptreemux.TreeMux.
type Router struct {
	*httptreemux.TreeMux
	config *routerConfig
}

// New returns a new router augmented with tracing.
func New(opts ...RouterOption) *Router {
	cfg := new(routerConfig)
	defaults(cfg)
	for _, fn := range opts {
		fn(cfg)
	}
	if !math.IsNaN(cfg.analyticsRate) {
		cfg.spanOpts = append(cfg.spanOpts, tracer.Tag(ext.EventSampleRate, cfg.analyticsRate))
	}
	cfg.spanOpts = append(cfg.spanOpts, tracer.Measured())
	log.Debug("contrib/dimfeld/httptreemux/v5: Configuring Router: %#v", cfg)
	return &Router{httptreemux.New(), cfg}
}

// ServeHTTP implements http.Handler.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// get the resource associated to this request
	route := req.URL.Path
	lr, _ := r.Lookup(w, req)
	for k, v := range lr.Params {
		// replace parameter values in URL path with their names
		route = strings.Replace(route, v, ":"+k, 1)
	}
	resource := req.Method + " " + route
	// pass r.TreeMux to avoid a circular reference panic on calling r.ServeHTTP
	httptrace.TraceAndServe(r.TreeMux, w, req, &httptrace.ServeConfig{
		Service:  r.config.serviceName,
		Resource: resource,
		SpanOpts: r.config.spanOpts,
	})
}

The plan is to submit a PR against DataDog/dd-trace-go once the necessary access to the LookupResult params is granted.