97 lines
2.6 KiB
Go
97 lines
2.6 KiB
Go
|
// +build go1.7
|
||
|
|
||
|
package nethttp
|
||
|
|
||
|
import (
|
||
|
"net/http"
|
||
|
|
||
|
opentracing "github.com/opentracing/opentracing-go"
|
||
|
"github.com/opentracing/opentracing-go/ext"
|
||
|
)
|
||
|
|
||
|
type statusCodeTracker struct {
|
||
|
http.ResponseWriter
|
||
|
status int
|
||
|
}
|
||
|
|
||
|
func (w *statusCodeTracker) WriteHeader(status int) {
|
||
|
w.status = status
|
||
|
w.ResponseWriter.WriteHeader(status)
|
||
|
}
|
||
|
|
||
|
type mwOptions struct {
|
||
|
opNameFunc func(r *http.Request) string
|
||
|
componentName string
|
||
|
}
|
||
|
|
||
|
// MWOption controls the behavior of the Middleware.
|
||
|
type MWOption func(*mwOptions)
|
||
|
|
||
|
// OperationNameFunc returns a MWOption that uses given function f
|
||
|
// to generate operation name for each server-side span.
|
||
|
func OperationNameFunc(f func(r *http.Request) string) MWOption {
|
||
|
return func(options *mwOptions) {
|
||
|
options.opNameFunc = f
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MWComponentName returns a MWOption that sets the component name
|
||
|
// name for the server-side span.
|
||
|
func MWComponentName(componentName string) MWOption {
|
||
|
return func(options *mwOptions) {
|
||
|
options.componentName = componentName
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Middleware wraps an http.Handler and traces incoming requests.
|
||
|
// Additionally, it adds the span to the request's context.
|
||
|
//
|
||
|
// By default, the operation name of the spans is set to "HTTP {method}".
|
||
|
// This can be overriden with options.
|
||
|
//
|
||
|
// Example:
|
||
|
// http.ListenAndServe("localhost:80", nethttp.Middleware(tracer, http.DefaultServeMux))
|
||
|
//
|
||
|
// The options allow fine tuning the behavior of the middleware.
|
||
|
//
|
||
|
// Example:
|
||
|
// mw := nethttp.Middleware(
|
||
|
// tracer,
|
||
|
// http.DefaultServeMux,
|
||
|
// nethttp.OperationName(func(r *http.Request) string {
|
||
|
// return "HTTP " + r.Method + ":/api/customers"
|
||
|
// }),
|
||
|
// )
|
||
|
func Middleware(tr opentracing.Tracer, h http.Handler, options ...MWOption) http.Handler {
|
||
|
opts := mwOptions{
|
||
|
opNameFunc: func(r *http.Request) string {
|
||
|
return "HTTP " + r.Method
|
||
|
},
|
||
|
}
|
||
|
for _, opt := range options {
|
||
|
opt(&opts)
|
||
|
}
|
||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||
|
ctx, _ := tr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
|
||
|
sp := tr.StartSpan(opts.opNameFunc(r), ext.RPCServerOption(ctx))
|
||
|
ext.HTTPMethod.Set(sp, r.Method)
|
||
|
ext.HTTPUrl.Set(sp, r.URL.String())
|
||
|
|
||
|
// set component name, use "net/http" if caller does not specify
|
||
|
componentName := opts.componentName
|
||
|
if componentName == "" {
|
||
|
componentName = defaultComponentName
|
||
|
}
|
||
|
ext.Component.Set(sp, componentName)
|
||
|
|
||
|
w = &statusCodeTracker{w, 200}
|
||
|
r = r.WithContext(opentracing.ContextWithSpan(r.Context(), sp))
|
||
|
|
||
|
h.ServeHTTP(w, r)
|
||
|
|
||
|
ext.HTTPStatusCode.Set(sp, uint16(w.(*statusCodeTracker).status))
|
||
|
sp.Finish()
|
||
|
}
|
||
|
return http.HandlerFunc(fn)
|
||
|
}
|