mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 13:09:22 -06:00
caa420f92f
Before these changes the request tracing was added for each route registered using the routing.RouteRegister, see code. This had the consequence that middleware executed earlier/later in the request pipeline was not part of the request tracing middleware life-cycle which measures the duration of requests among other things. In the logger middleware we do extract the current distributed trace identifier, if available, and set that on request info/error log messages. With these changes we can extract the current distributed trace identifier, if available, and set that on the contextual HTTP request logger (models.ReqContext.Logger) which would improve the possibility to correlate all HTTP request log messages with traces. In addition, the request tracing middleware is now executed first and last in the request pipeline and should therefore result in more accurate timing measurements (request duration). Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
75 lines
2.2 KiB
Go
75 lines
2.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
opentracing "github.com/opentracing/opentracing-go"
|
|
"github.com/opentracing/opentracing-go/ext"
|
|
|
|
"gopkg.in/macaron.v1"
|
|
)
|
|
|
|
type contextKey struct{}
|
|
|
|
var routeOperationNameKey = contextKey{}
|
|
|
|
// ProvideRouteOperationName creates a named middleware responsible for populating
|
|
// the context with the route operation name that can be used later in the request pipeline.
|
|
// Implements routing.RegisterNamedMiddleware.
|
|
func ProvideRouteOperationName(name string) macaron.Handler {
|
|
return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {
|
|
ctx := context.WithValue(c.Req.Context(), routeOperationNameKey, name)
|
|
c.Req.Request = c.Req.WithContext(ctx)
|
|
}
|
|
}
|
|
|
|
// RouteOperationNameFromContext receives the route operation name from context, if set.
|
|
func RouteOperationNameFromContext(ctx context.Context) (string, bool) {
|
|
if val := ctx.Value(routeOperationNameKey); val != nil {
|
|
op, ok := val.(string)
|
|
return op, ok
|
|
}
|
|
|
|
return "", false
|
|
}
|
|
|
|
func RequestTracing() macaron.Handler {
|
|
return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {
|
|
if strings.HasPrefix(c.Req.URL.Path, "/public/") ||
|
|
c.Req.URL.Path == "robots.txt" {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
rw := res.(macaron.ResponseWriter)
|
|
|
|
tracer := opentracing.GlobalTracer()
|
|
wireContext, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
|
|
span := tracer.StartSpan(fmt.Sprintf("HTTP %s %s", req.Method, req.URL.Path), ext.RPCServerOption(wireContext))
|
|
|
|
ctx := opentracing.ContextWithSpan(req.Context(), span)
|
|
c.Req.Request = req.WithContext(ctx)
|
|
|
|
c.Next()
|
|
|
|
// Only call span.Finish when a route operation name have been set,
|
|
// meaning that not set the span would not be reported.
|
|
if routeOperation, exists := RouteOperationNameFromContext(c.Req.Context()); exists {
|
|
defer span.Finish()
|
|
span.SetOperationName(fmt.Sprintf("HTTP %s %s", req.Method, routeOperation))
|
|
}
|
|
|
|
status := rw.Status()
|
|
|
|
ext.HTTPStatusCode.Set(span, uint16(status))
|
|
ext.HTTPUrl.Set(span, req.RequestURI)
|
|
ext.HTTPMethod.Set(span, req.Method)
|
|
if status >= 400 {
|
|
ext.Error.Set(span, true)
|
|
}
|
|
}
|
|
}
|