internal/logging/slowlogs/middleware.go (54 lines of code) (raw):
package slowlogs
import (
"context"
"net/http"
"time"
"github.com/sirupsen/logrus"
"gitlab.com/gitlab-org/labkit/log"
)
// LoggerFunc Define a type for the logging function
type LoggerFunc func(r *http.Request) *logrus.Entry
const SlowRequestTimeThreshold = 500 * time.Millisecond
// NewMiddleware returns middleware which logs timing info if downstream time is more than 0.5 seconds
func NewMiddleware(handler http.Handler, logger LoggerFunc, slowRequestThreshold time.Duration) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// update the request context with timing info
ctx := withTimingInfo(r.Context())
r = r.WithContext(ctx)
handler.ServeHTTP(w, r)
totalDownstreamTime := time.Since(start)
if totalDownstreamTime >= slowRequestThreshold {
logger(r).WithFields(log.Fields{
"total_execution_time_ms": totalDownstreamTime.Milliseconds(),
"middleware_time_distribution": prepareMiddlewareTimingInfoInMS(r.Context()),
"other_time_distribution": prepareOtherTimingInfoInMS(r.Context()),
}).Info("Request time exceeded 0.5 seconds")
}
})
}
// prepareMiddlewareTimingInfoLog return timing info as map of string and duration in milliseconds.
func prepareMiddlewareTimingInfoInMS(ctx context.Context) map[string]int64 {
info := ctx.Value(timingContextKey).(*timingInfo)
return prepareTimingInfoInMS(info.MiddlewareTimings)
}
// prepareOtherTimingInfoLog return timing info as map of string and duration in milliseconds.
func prepareOtherTimingInfoInMS(ctx context.Context) map[string]int64 {
info := ctx.Value(timingContextKey).(*timingInfo)
return prepareTimingInfoInMS(info.OtherTimings)
}
func prepareTimingInfoInMS(timingDurations map[string]time.Duration) map[string]int64 {
timingsInMillis := make(map[string]int64)
for key, duration := range timingDurations {
timingsInMillis[key+"_ms"] = duration.Milliseconds()
}
return timingsInMillis
}
// LogHandlerTiming calculates handler execution time excluding downstream handler execution time
func LogHandlerTiming(handler http.Handler, handlerName string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
startLogTime := time.Now()
defer calculateMiddlewareExecutionTime(r.Context(), startLogTime, handlerName)
handler.ServeHTTP(w, r)
})
}
func calculateMiddlewareExecutionTime(ctx context.Context, startTime time.Time, handlerName string) {
info := getTimingInfo(ctx)
spentTime := time.Since(startTime)
info.MiddlewareTimings[handlerName] = spentTime - info.LastDuration
info.LastDuration = spentTime
}