Instrumentation: Add status_source label to request metrics/logs (#74114)

Ref #68480

Co-authored-by: Giuseppe Guerra <giuseppe.guerra@grafana.com>
This commit is contained in:
Marcus Efraimsson
2023-09-11 12:13:13 +02:00
committed by GitHub
co-authored by Giuseppe Guerra
parent 97d568e60a
commit 8ee43f3705
27 changed files with 487 additions and 67 deletions
+6
View File
@@ -23,6 +23,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/middleware/requestmeta"
"github.com/grafana/grafana/pkg/util/errutil"
@@ -127,6 +128,11 @@ func (l *loggerImpl) prepareLogParams(c *contextmodel.ReqContext, duration time.
logParams = append(logParams, "handler", handler)
}
if l.flags.IsEnabled(featuremgmt.FlagRequestInstrumentationStatusSource) {
rmd := requestmeta.GetRequestMetaData(c.Req.Context())
logParams = append(logParams, "status_source", rmd.StatusSource)
}
logParams = append(logParams, errorLogParams(c.Error)...)
return logParams, lvl
+11 -1
View File
@@ -36,6 +36,11 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
)
histogramLabels := []string{"handler", "status_code", "method"}
if features.IsEnabled(featuremgmt.FlagRequestInstrumentationStatusSource) {
histogramLabels = append(histogramLabels, "status_source")
}
if cfg.MetricsIncludeTeamLabel {
histogramLabels = append(histogramLabels, "team")
}
@@ -80,8 +85,13 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
}
labelValues := []string{handler, code, r.Method}
rmd := requestmeta.GetRequestMetaData(r.Context())
if features.IsEnabled(featuremgmt.FlagRequestInstrumentationStatusSource) {
labelValues = append(labelValues, string(rmd.StatusSource))
}
if cfg.MetricsIncludeTeamLabel {
rmd := requestmeta.GetRequestMetaData(r.Context())
labelValues = append(labelValues, rmd.Team)
}
+43 -7
View File
@@ -13,10 +13,18 @@ const (
TeamCore = "core"
)
type StatusSource string
const (
StatusSourceServer StatusSource = "server"
StatusSourceDownstream StatusSource = "downstream"
)
type rMDContextKey struct{}
type RequestMetaData struct {
Team string
Team string
StatusSource StatusSource
}
var requestMetaDataContextKey = rMDContextKey{}
@@ -27,8 +35,7 @@ func SetupRequestMetadata() web.Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rmd := defaultRequestMetadata()
ctx := context.WithValue(r.Context(), requestMetaDataContextKey, rmd)
ctx := SetRequestMetaData(r.Context(), rmd)
*r = *r.WithContext(ctx)
next.ServeHTTP(w, r)
@@ -46,7 +53,13 @@ func GetRequestMetaData(ctx context.Context) *RequestMetaData {
return value
}
return defaultRequestMetadata()
rmd := defaultRequestMetadata()
return &rmd
}
// SetRequestMetaData sets the request metadata for the context.
func SetRequestMetaData(ctx context.Context, rmd RequestMetaData) context.Context {
return context.WithValue(ctx, requestMetaDataContextKey, &rmd)
}
// SetOwner returns an `web.Handler` that sets the team name for an request.
@@ -57,8 +70,31 @@ func SetOwner(team string) web.Handler {
}
}
func defaultRequestMetadata() *RequestMetaData {
return &RequestMetaData{
Team: TeamCore,
// WithDownstreamStatusSource sets the StatusSource field of the [RequestMetaData] for the
// context to [StatusSourceDownstream].
func WithDownstreamStatusSource(ctx context.Context) {
v := GetRequestMetaData(ctx)
v.StatusSource = StatusSourceDownstream
}
// WithStatusSource sets the StatusSource field of the [RequestMetaData] for the
// context based on the provided statusCode.
// If statusCode >= 500 then [StatusSourceDownstream].
// If statusCode < 500 then [StatusSourceServer].
func WithStatusSource(ctx context.Context, statusCode int) {
v := GetRequestMetaData(ctx)
if statusCode >= 500 {
v.StatusSource = StatusSourceDownstream
return
}
v.StatusSource = StatusSourceServer
}
func defaultRequestMetadata() RequestMetaData {
return RequestMetaData{
Team: TeamCore,
StatusSource: StatusSourceServer,
}
}
@@ -0,0 +1,47 @@
package requestmeta
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/require"
)
func TestStatusSource(t *testing.T) {
ctx := context.Background()
ctx = SetRequestMetaData(ctx, defaultRequestMetadata())
rmd := GetRequestMetaData(ctx)
require.Equal(t, StatusSourceServer, rmd.StatusSource)
WithDownstreamStatusSource(ctx)
rmd = GetRequestMetaData(ctx)
require.Equal(t, StatusSourceDownstream, rmd.StatusSource)
}
func TestWithStatusSource(t *testing.T) {
tcs := []struct {
status int
expectedSource StatusSource
}{
{status: http.StatusOK, expectedSource: StatusSourceServer},
{status: http.StatusBadRequest, expectedSource: StatusSourceServer},
{status: http.StatusForbidden, expectedSource: StatusSourceServer},
{status: http.StatusUnauthorized, expectedSource: StatusSourceServer},
{status: http.StatusInternalServerError, expectedSource: StatusSourceDownstream},
{status: http.StatusBadGateway, expectedSource: StatusSourceDownstream},
{status: http.StatusGatewayTimeout, expectedSource: StatusSourceDownstream},
{status: 599, expectedSource: StatusSourceDownstream},
}
for _, tc := range tcs {
t.Run(fmt.Sprintf("status %d => source %s ", tc.status, tc.expectedSource), func(t *testing.T) {
ctx := context.Background()
ctx = SetRequestMetaData(ctx, defaultRequestMetadata())
WithStatusSource(ctx, tc.status)
rmd := GetRequestMetaData(ctx)
require.Equal(t, tc.expectedSource, rmd.StatusSource)
})
}
}