1package middleware 2 3import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strings" 8 9 opentracing "github.com/opentracing/opentracing-go" 10 "github.com/opentracing/opentracing-go/ext" 11 12 "github.com/grafana/grafana/pkg/web" 13) 14 15type contextKey struct{} 16 17var routeOperationNameKey = contextKey{} 18 19// ProvideRouteOperationName creates a named middleware responsible for populating 20// the context with the route operation name that can be used later in the request pipeline. 21// Implements routing.RegisterNamedMiddleware. 22func ProvideRouteOperationName(name string) web.Handler { 23 return func(res http.ResponseWriter, req *http.Request, c *web.Context) { 24 ctx := context.WithValue(c.Req.Context(), routeOperationNameKey, name) 25 c.Req = c.Req.WithContext(ctx) 26 } 27} 28 29// RouteOperationNameFromContext receives the route operation name from context, if set. 30func RouteOperationNameFromContext(ctx context.Context) (string, bool) { 31 if val := ctx.Value(routeOperationNameKey); val != nil { 32 op, ok := val.(string) 33 return op, ok 34 } 35 36 return "", false 37} 38 39func RequestTracing() web.Handler { 40 return func(res http.ResponseWriter, req *http.Request, c *web.Context) { 41 if strings.HasPrefix(c.Req.URL.Path, "/public/") || 42 c.Req.URL.Path == "robots.txt" { 43 c.Next() 44 return 45 } 46 47 rw := res.(web.ResponseWriter) 48 49 tracer := opentracing.GlobalTracer() 50 wireContext, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header)) 51 span := tracer.StartSpan(fmt.Sprintf("HTTP %s %s", req.Method, req.URL.Path), ext.RPCServerOption(wireContext)) 52 53 ctx := opentracing.ContextWithSpan(req.Context(), span) 54 c.Req = req.WithContext(ctx) 55 c.Map(c.Req) 56 57 c.Next() 58 59 // Only call span.Finish when a route operation name have been set, 60 // meaning that not set the span would not be reported. 61 if routeOperation, exists := RouteOperationNameFromContext(c.Req.Context()); exists { 62 defer span.Finish() 63 span.SetOperationName(fmt.Sprintf("HTTP %s %s", req.Method, routeOperation)) 64 } 65 66 status := rw.Status() 67 68 ext.HTTPStatusCode.Set(span, uint16(status)) 69 ext.HTTPUrl.Set(span, req.RequestURI) 70 ext.HTTPMethod.Set(span, req.Method) 71 if status >= 400 { 72 ext.Error.Set(span, true) 73 } 74 } 75} 76