mirror of https://github.com/prometheus/prometheus
144 lines
4.3 KiB
Go
144 lines
4.3 KiB
Go
// +build go1.7
|
|
|
|
package nethttp
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
|
|
opentracing "github.com/opentracing/opentracing-go"
|
|
"github.com/opentracing/opentracing-go/ext"
|
|
)
|
|
|
|
type mwOptions struct {
|
|
opNameFunc func(r *http.Request) string
|
|
spanFilter func(r *http.Request) bool
|
|
spanObserver func(span opentracing.Span, r *http.Request)
|
|
urlTagFunc func(u *url.URL) 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
|
|
// for the server-side span.
|
|
func MWComponentName(componentName string) MWOption {
|
|
return func(options *mwOptions) {
|
|
options.componentName = componentName
|
|
}
|
|
}
|
|
|
|
// MWSpanFilter returns a MWOption that filters requests from creating a span
|
|
// for the server-side span.
|
|
// Span won't be created if it returns false.
|
|
func MWSpanFilter(f func(r *http.Request) bool) MWOption {
|
|
return func(options *mwOptions) {
|
|
options.spanFilter = f
|
|
}
|
|
}
|
|
|
|
// MWSpanObserver returns a MWOption that observe the span
|
|
// for the server-side span.
|
|
func MWSpanObserver(f func(span opentracing.Span, r *http.Request)) MWOption {
|
|
return func(options *mwOptions) {
|
|
options.spanObserver = f
|
|
}
|
|
}
|
|
|
|
// MWURLTagFunc returns a MWOption that uses given function f
|
|
// to set the span's http.url tag. Can be used to change the default
|
|
// http.url tag, eg to redact sensitive information.
|
|
func MWURLTagFunc(f func(u *url.URL) string) MWOption {
|
|
return func(options *mwOptions) {
|
|
options.urlTagFunc = f
|
|
}
|
|
}
|
|
|
|
// 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.OperationNameFunc(func(r *http.Request) string {
|
|
// return "HTTP " + r.Method + ":/api/customers"
|
|
// }),
|
|
// nethttp.MWSpanObserver(func(sp opentracing.Span, r *http.Request) {
|
|
// sp.SetTag("http.uri", r.URL.EscapedPath())
|
|
// }),
|
|
// )
|
|
func Middleware(tr opentracing.Tracer, h http.Handler, options ...MWOption) http.Handler {
|
|
return MiddlewareFunc(tr, h.ServeHTTP, options...)
|
|
}
|
|
|
|
// MiddlewareFunc wraps an http.HandlerFunc and traces incoming requests.
|
|
// It behaves identically to the Middleware function above.
|
|
//
|
|
// Example:
|
|
// http.ListenAndServe("localhost:80", nethttp.MiddlewareFunc(tracer, MyHandler))
|
|
func MiddlewareFunc(tr opentracing.Tracer, h http.HandlerFunc, options ...MWOption) http.HandlerFunc {
|
|
opts := mwOptions{
|
|
opNameFunc: func(r *http.Request) string {
|
|
return "HTTP " + r.Method
|
|
},
|
|
spanFilter: func(r *http.Request) bool { return true },
|
|
spanObserver: func(span opentracing.Span, r *http.Request) {},
|
|
urlTagFunc: func(u *url.URL) string {
|
|
return u.String()
|
|
},
|
|
}
|
|
for _, opt := range options {
|
|
opt(&opts)
|
|
}
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
if !opts.spanFilter(r) {
|
|
h(w, r)
|
|
return
|
|
}
|
|
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, opts.urlTagFunc(r.URL))
|
|
opts.spanObserver(sp, r)
|
|
|
|
// set component name, use "net/http" if caller does not specify
|
|
componentName := opts.componentName
|
|
if componentName == "" {
|
|
componentName = defaultComponentName
|
|
}
|
|
ext.Component.Set(sp, componentName)
|
|
|
|
sct := &statusCodeTracker{ResponseWriter: w}
|
|
r = r.WithContext(opentracing.ContextWithSpan(r.Context(), sp))
|
|
|
|
defer func() {
|
|
ext.HTTPStatusCode.Set(sp, uint16(sct.status))
|
|
if sct.status >= http.StatusInternalServerError || !sct.wroteheader {
|
|
ext.Error.Set(sp, true)
|
|
}
|
|
sp.Finish()
|
|
}()
|
|
|
|
h(sct.wrappedResponseWriter(), r)
|
|
}
|
|
return http.HandlerFunc(fn)
|
|
}
|