Files
grafana/pkg/middleware/request_metrics.go
T

154 lines
4.4 KiB
Go
Raw Normal View History

package middleware
import (
"net/http"
2017-09-06 22:24:10 +02:00
"strconv"
"strings"
2017-09-06 22:24:10 +02:00
"time"
"github.com/grafana/grafana/pkg/infra/log"
2019-02-23 23:35:26 +01:00
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/web"
"github.com/prometheus/client_golang/prometheus"
)
var (
httpRequestsInFlight prometheus.Gauge
httpRequestDurationHistogram *prometheus.HistogramVec
// DefBuckets are histogram buckets for the response time (in seconds)
// of a network service, including one that is responding very slowly.
defBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 25}
)
func init() {
httpRequestsInFlight = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "grafana",
Name: "http_request_in_flight",
Help: "A gauge of requests currently being served by Grafana.",
},
)
httpRequestDurationHistogram = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "grafana",
Name: "http_request_duration_seconds",
Help: "Histogram of latencies for HTTP requests.",
Buckets: defBuckets,
},
[]string{"handler", "status_code", "method"},
)
prometheus.MustRegister(httpRequestsInFlight, httpRequestDurationHistogram)
}
// RequestMetrics is a middleware handler that instruments the request.
2022-08-09 14:58:50 +02:00
func RequestMetrics(features featuremgmt.FeatureToggles) web.Middleware {
log := log.New("middleware.request-metrics")
2022-08-09 14:58:50 +02:00
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rw := web.Rw(w, r)
now := time.Now()
httpRequestsInFlight.Inc()
defer httpRequestsInFlight.Dec()
next.ServeHTTP(w, r)
2022-08-09 14:58:50 +02:00
status := rw.Status()
code := sanitizeCode(status)
2022-08-09 14:58:50 +02:00
handler := "unknown"
// TODO: do not depend on web.Context from the future
if routeOperation, exists := routeOperationName(web.FromContext(r.Context()).Req); exists {
handler = routeOperation
} else {
2022-08-09 14:58:50 +02:00
// if grafana does not recognize the handler and returns 404 we should register it as `notfound`
if status == http.StatusNotFound {
handler = "notfound"
} else {
// log requests where we could not identify handler so we can register them.
if features.IsEnabled(featuremgmt.FlagLogRequestsInstrumentedAsUnknown) {
log.Warn("request instrumented as unknown", "path", r.URL.Path, "status_code", status)
}
}
}
2017-09-06 22:24:10 +02:00
2022-08-09 14:58:50 +02:00
// avoiding the sanitize functions for in the new instrumentation
// since they dont make much sense. We should remove them later.
histogram := httpRequestDurationHistogram.
WithLabelValues(handler, code, r.Method)
if traceID := tracing.TraceIDFromContext(r.Context(), true); traceID != "" {
// Need to type-convert the Observer to an
// ExemplarObserver. This will always work for a
// HistogramVec.
histogram.(prometheus.ExemplarObserver).ObserveWithExemplar(
time.Since(now).Seconds(), prometheus.Labels{"traceID": traceID},
)
return
}
histogram.Observe(time.Since(now).Seconds())
2022-08-09 14:58:50 +02:00
switch {
case strings.HasPrefix(r.RequestURI, "/api/datasources/proxy"):
countProxyRequests(status)
case strings.HasPrefix(r.RequestURI, "/api/"):
countApiRequests(status)
default:
countPageRequests(status)
}
})
}
}
2016-09-12 13:29:31 +02:00
func countApiRequests(status int) {
switch status {
case 200:
metrics.MApiStatus.WithLabelValues("200").Inc()
2016-09-12 13:29:31 +02:00
case 404:
metrics.MApiStatus.WithLabelValues("404").Inc()
2016-09-12 13:29:31 +02:00
case 500:
metrics.MApiStatus.WithLabelValues("500").Inc()
2016-09-12 13:29:31 +02:00
default:
metrics.MApiStatus.WithLabelValues("unknown").Inc()
2016-09-12 13:29:31 +02:00
}
}
func countPageRequests(status int) {
switch status {
case 200:
metrics.MPageStatus.WithLabelValues("200").Inc()
2016-09-12 13:29:31 +02:00
case 404:
metrics.MPageStatus.WithLabelValues("404").Inc()
2016-09-12 13:29:31 +02:00
case 500:
metrics.MPageStatus.WithLabelValues("500").Inc()
2016-09-12 13:29:31 +02:00
default:
metrics.MPageStatus.WithLabelValues("unknown").Inc()
2016-09-12 13:29:31 +02:00
}
}
func countProxyRequests(status int) {
switch status {
case 200:
metrics.MProxyStatus.WithLabelValues("200").Inc()
2016-09-12 13:29:31 +02:00
case 404:
metrics.MProxyStatus.WithLabelValues("400").Inc()
2016-09-12 13:29:31 +02:00
case 500:
metrics.MProxyStatus.WithLabelValues("500").Inc()
2016-09-12 13:29:31 +02:00
default:
metrics.MProxyStatus.WithLabelValues("unknown").Inc()
2016-09-12 13:29:31 +02:00
}
}
2017-09-06 22:24:10 +02:00
// If the wrapped http.Handler has not set a status code, i.e. the value is
// currently 0, sanitizeCode will return 200, for consistency with behavior in
2017-09-06 22:24:10 +02:00
// the stdlib.
func sanitizeCode(s int) string {
2021-05-26 12:18:54 +02:00
if s == 0 {
2017-09-06 22:24:10 +02:00
return "200"
}
2021-05-26 12:18:54 +02:00
return strconv.Itoa(s)
2017-09-06 22:24:10 +02:00
}