Plugins: Auto instrumentation improvements (#94193)
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/instrumentationutils"
|
||||
plog "github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
|
||||
// NewLoggerMiddleware creates a new backend.HandlerMiddleware that will
|
||||
@@ -61,7 +60,7 @@ func (m *LoggerMiddleware) logRequest(ctx context.Context, pCtx backend.PluginCo
|
||||
if err != nil {
|
||||
logParams = append(logParams, "error", err)
|
||||
}
|
||||
logParams = append(logParams, "statusSource", pluginrequestmeta.StatusSourceFromContext(ctx))
|
||||
logParams = append(logParams, "statusSource", backend.ErrorSourceFromContext(ctx))
|
||||
|
||||
if status > instrumentationutils.RequestStatusOK {
|
||||
logFunc = ctxLogger.Error
|
||||
@@ -93,7 +92,8 @@ func (m *LoggerMiddleware) QueryData(ctx context.Context, req *backend.QueryData
|
||||
"refID", refID,
|
||||
"status", int(dr.Status),
|
||||
"error", dr.Error,
|
||||
"statusSource", pluginrequestmeta.StatusSourceFromPluginErrorSource(dr.ErrorSource),
|
||||
"statusSource", dr.ErrorSource.String(),
|
||||
"target", m.pluginTarget(ctx, req.PluginContext),
|
||||
}
|
||||
ctxLogger.Error("Partial data response error", logParams...)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/instrumentationutils"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
|
||||
// pluginMetrics contains the prometheus metrics used by the MetricsMiddleware.
|
||||
@@ -115,7 +114,7 @@ func (m *MetricsMiddleware) instrumentPluginRequest(ctx context.Context, pluginC
|
||||
status, err := fn(ctx)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
statusSource := pluginrequestmeta.StatusSourceFromContext(ctx)
|
||||
statusSource := backend.ErrorSourceFromContext(ctx)
|
||||
endpoint := backend.EndpointFromContext(ctx)
|
||||
|
||||
pluginRequestDurationWithLabels := m.pluginRequestDuration.WithLabelValues(pluginCtx.PluginID, string(endpoint), target, string(statusSource))
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/instrumentationutils"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -90,7 +89,7 @@ func TestInstrumentationMiddleware(t *testing.T) {
|
||||
require.Equal(t, 1, testutil.CollectAndCount(promRegistry, metricRequestDurationMs))
|
||||
require.Equal(t, 1, testutil.CollectAndCount(promRegistry, metricRequestDurationS))
|
||||
|
||||
counter := mw.pluginMetrics.pluginRequestCounter.WithLabelValues(pluginID, string(tc.expEndpoint), instrumentationutils.RequestStatusOK.String(), string(backendplugin.TargetUnknown), string(pluginrequestmeta.DefaultStatusSource))
|
||||
counter := mw.pluginMetrics.pluginRequestCounter.WithLabelValues(pluginID, string(tc.expEndpoint), instrumentationutils.RequestStatusOK.String(), string(backendplugin.TargetUnknown), string(backend.DefaultErrorSource))
|
||||
require.Equal(t, 1.0, testutil.ToFloat64(counter))
|
||||
for _, m := range []string{metricRequestDurationMs, metricRequestDurationS} {
|
||||
require.NoError(t, checkHistogram(promRegistry, m, map[string]string{
|
||||
@@ -155,12 +154,11 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
}))
|
||||
metricsMw := newMetricsMiddleware(promRegistry, pluginsRegistry)
|
||||
cdt := handlertest.NewHandlerMiddlewareTest(t, handlertest.WithMiddlewares(
|
||||
NewPluginRequestMetaMiddleware(),
|
||||
backend.HandlerMiddlewareFunc(func(next backend.Handler) backend.Handler {
|
||||
metricsMw.BaseHandler = backend.NewBaseHandler(next)
|
||||
return metricsMw
|
||||
}),
|
||||
NewStatusSourceMiddleware(),
|
||||
backend.NewErrorSourceMiddleware(),
|
||||
))
|
||||
|
||||
t.Run("Metrics", func(t *testing.T) {
|
||||
@@ -185,12 +183,12 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
responses map[string]backend.DataResponse
|
||||
expStatusSource pluginrequestmeta.StatusSource
|
||||
expStatusSource backend.ErrorSource
|
||||
}{
|
||||
{
|
||||
"Default status source for ok responses should be plugin",
|
||||
map[string]backend.DataResponse{"A": okResponse},
|
||||
pluginrequestmeta.StatusSourcePlugin,
|
||||
backend.ErrorSourcePlugin,
|
||||
},
|
||||
{
|
||||
"Plugin errors should have higher priority than downstream errors",
|
||||
@@ -198,12 +196,12 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
"A": pluginErrorResponse,
|
||||
"B": downstreamErrorResponse,
|
||||
},
|
||||
pluginrequestmeta.StatusSourcePlugin,
|
||||
backend.ErrorSourcePlugin,
|
||||
},
|
||||
{
|
||||
"Errors without ErrorSource should be reported as plugin status source",
|
||||
map[string]backend.DataResponse{"A": legacyErrorResponse},
|
||||
pluginrequestmeta.StatusSourcePlugin,
|
||||
backend.ErrorSourcePlugin,
|
||||
},
|
||||
{
|
||||
"Downstream errors should have higher priority than ok responses",
|
||||
@@ -211,7 +209,7 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
"A": okResponse,
|
||||
"B": downstreamErrorResponse,
|
||||
},
|
||||
pluginrequestmeta.StatusSourceDownstream,
|
||||
backend.ErrorSourceDownstream,
|
||||
},
|
||||
{
|
||||
"Plugin errors should have higher priority than ok responses",
|
||||
@@ -219,7 +217,7 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
"A": okResponse,
|
||||
"B": pluginErrorResponse,
|
||||
},
|
||||
pluginrequestmeta.StatusSourcePlugin,
|
||||
backend.ErrorSourcePlugin,
|
||||
},
|
||||
{
|
||||
"Legacy errors should have higher priority than ok responses",
|
||||
@@ -227,7 +225,7 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
"A": okResponse,
|
||||
"B": legacyErrorResponse,
|
||||
},
|
||||
pluginrequestmeta.StatusSourcePlugin,
|
||||
backend.ErrorSourcePlugin,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -242,7 +240,7 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
}
|
||||
_, err := cdt.MiddlewareHandler.QueryData(context.Background(), &backend.QueryDataRequest{PluginContext: pCtx})
|
||||
require.NoError(t, err)
|
||||
ctxStatusSource := pluginrequestmeta.StatusSourceFromContext(cdt.QueryDataCtx)
|
||||
ctxStatusSource := backend.ErrorSourceFromContext(cdt.QueryDataCtx)
|
||||
require.Equal(t, tc.expStatusSource, ctxStatusSource)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package clientmiddleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
|
||||
// NewPluginRequestMetaMiddleware returns a new backend.HandlerMiddleware that sets up the default
|
||||
// values for the plugin request meta in the context.Context. All middlewares that are executed
|
||||
// after this one are be able to access plugin request meta via the pluginrequestmeta package.
|
||||
func NewPluginRequestMetaMiddleware() backend.HandlerMiddleware {
|
||||
return backend.HandlerMiddlewareFunc(func(next backend.Handler) backend.Handler {
|
||||
return &PluginRequestMetaMiddleware{
|
||||
BaseHandler: backend.NewBaseHandler(next),
|
||||
defaultStatusSource: pluginrequestmeta.DefaultStatusSource,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type PluginRequestMetaMiddleware struct {
|
||||
backend.BaseHandler
|
||||
defaultStatusSource pluginrequestmeta.StatusSource
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) withDefaultPluginRequestMeta(ctx context.Context) context.Context {
|
||||
// Setup plugin request status source
|
||||
ctx = pluginrequestmeta.WithStatusSource(ctx, m.defaultStatusSource)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.QueryData(ctx, req)
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.CallResource(ctx, req, sender)
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.CheckHealth(ctx, req)
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) CollectMetrics(ctx context.Context, req *backend.CollectMetricsRequest) (*backend.CollectMetricsResult, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.CollectMetrics(ctx, req)
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.SubscribeStream(ctx, req)
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.PublishStream(ctx, req)
|
||||
}
|
||||
|
||||
func (m *PluginRequestMetaMiddleware) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.RunStream(ctx, req, sender)
|
||||
}
|
||||
|
||||
// ValidateAdmission implements backend.AdmissionHandler.
|
||||
func (m *PluginRequestMetaMiddleware) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.ValidateAdmission(ctx, req)
|
||||
}
|
||||
|
||||
// MutateAdmission implements backend.AdmissionHandler.
|
||||
func (m *PluginRequestMetaMiddleware) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.MutateAdmission(ctx, req)
|
||||
}
|
||||
|
||||
// ConvertObject implements backend.AdmissionHandler.
|
||||
func (m *PluginRequestMetaMiddleware) ConvertObjects(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) {
|
||||
ctx = m.withDefaultPluginRequestMeta(ctx)
|
||||
return m.BaseHandler.ConvertObjects(ctx, req)
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package clientmiddleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/handlertest"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
|
||||
func TestPluginRequestMetaMiddleware(t *testing.T) {
|
||||
t.Run("default", func(t *testing.T) {
|
||||
cdt := handlertest.NewHandlerMiddlewareTest(t,
|
||||
handlertest.WithMiddlewares(NewPluginRequestMetaMiddleware()),
|
||||
)
|
||||
_, err := cdt.MiddlewareHandler.QueryData(context.Background(), &backend.QueryDataRequest{})
|
||||
require.NoError(t, err)
|
||||
ss := pluginrequestmeta.StatusSourceFromContext(cdt.QueryDataCtx)
|
||||
require.Equal(t, pluginrequestmeta.StatusSourcePlugin, ss)
|
||||
})
|
||||
|
||||
t.Run("other value", func(t *testing.T) {
|
||||
cdt := handlertest.NewHandlerMiddlewareTest(t,
|
||||
handlertest.WithMiddlewares(backend.HandlerMiddlewareFunc(func(next backend.Handler) backend.Handler {
|
||||
return &PluginRequestMetaMiddleware{
|
||||
BaseHandler: backend.NewBaseHandler(next),
|
||||
defaultStatusSource: "test",
|
||||
}
|
||||
})),
|
||||
)
|
||||
_, err := cdt.MiddlewareHandler.QueryData(context.Background(), &backend.QueryDataRequest{})
|
||||
require.NoError(t, err)
|
||||
ss := pluginrequestmeta.StatusSourceFromContext(cdt.QueryDataCtx)
|
||||
require.Equal(t, pluginrequestmeta.StatusSource("test"), ss)
|
||||
})
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package clientmiddleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
|
||||
// NewStatusSourceMiddleware returns a new backend.HandlerMiddleware that sets the status source in the
|
||||
// plugin request meta stored in the context.Context, according to the query data responses returned by QueryError.
|
||||
// If at least one query data response has a "downstream" status source and there isn't one with a "plugin" status source,
|
||||
// the plugin request meta in the context is set to "downstream".
|
||||
func NewStatusSourceMiddleware() backend.HandlerMiddleware {
|
||||
return backend.HandlerMiddlewareFunc(func(next backend.Handler) backend.Handler {
|
||||
return &StatusSourceMiddleware{
|
||||
BaseHandler: backend.NewBaseHandler(next),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type StatusSourceMiddleware struct {
|
||||
backend.BaseHandler
|
||||
}
|
||||
|
||||
func (m *StatusSourceMiddleware) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
resp, err := m.BaseHandler.QueryData(ctx, req)
|
||||
if resp == nil || len(resp.Responses) == 0 {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Set downstream status source in the context if there's at least one response with downstream status source,
|
||||
// and if there's no plugin error
|
||||
var hasPluginError bool
|
||||
var hasDownstreamError bool
|
||||
for _, r := range resp.Responses {
|
||||
if r.Error == nil {
|
||||
continue
|
||||
}
|
||||
if r.ErrorSource == backend.ErrorSourceDownstream {
|
||||
hasDownstreamError = true
|
||||
} else {
|
||||
hasPluginError = true
|
||||
}
|
||||
}
|
||||
|
||||
// A plugin error has higher priority than a downstream error,
|
||||
// so set to downstream only if there's no plugin error
|
||||
if hasDownstreamError && !hasPluginError {
|
||||
if err := pluginrequestmeta.WithDownstreamStatusSource(ctx); err != nil {
|
||||
return resp, fmt.Errorf("failed to set downstream status source: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package clientmiddleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/handlertest"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
|
||||
func TestStatusSourceMiddleware(t *testing.T) {
|
||||
someErr := errors.New("oops")
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
|
||||
queryDataResponse *backend.QueryDataResponse
|
||||
|
||||
expStatusSource pluginrequestmeta.StatusSource
|
||||
}{
|
||||
{
|
||||
name: `no error should be "plugin" status source`,
|
||||
queryDataResponse: nil,
|
||||
expStatusSource: pluginrequestmeta.StatusSourcePlugin,
|
||||
},
|
||||
{
|
||||
name: `single downstream error should be "downstream" status source`,
|
||||
queryDataResponse: &backend.QueryDataResponse{
|
||||
Responses: map[string]backend.DataResponse{
|
||||
"A": {Error: someErr, ErrorSource: backend.ErrorSourceDownstream},
|
||||
},
|
||||
},
|
||||
expStatusSource: pluginrequestmeta.StatusSourceDownstream,
|
||||
},
|
||||
{
|
||||
name: `single plugin error should be "plugin" status source`,
|
||||
queryDataResponse: &backend.QueryDataResponse{
|
||||
Responses: map[string]backend.DataResponse{
|
||||
"A": {Error: someErr, ErrorSource: backend.ErrorSourcePlugin},
|
||||
},
|
||||
},
|
||||
expStatusSource: pluginrequestmeta.StatusSourcePlugin,
|
||||
},
|
||||
{
|
||||
name: `multiple downstream errors should be "downstream" status source`,
|
||||
queryDataResponse: &backend.QueryDataResponse{
|
||||
Responses: map[string]backend.DataResponse{
|
||||
"A": {Error: someErr, ErrorSource: backend.ErrorSourceDownstream},
|
||||
"B": {Error: someErr, ErrorSource: backend.ErrorSourceDownstream},
|
||||
},
|
||||
},
|
||||
expStatusSource: pluginrequestmeta.StatusSourceDownstream,
|
||||
},
|
||||
{
|
||||
name: `single plugin error mixed with downstream errors should be "plugin" status source`,
|
||||
queryDataResponse: &backend.QueryDataResponse{
|
||||
Responses: map[string]backend.DataResponse{
|
||||
"A": {Error: someErr, ErrorSource: backend.ErrorSourceDownstream},
|
||||
"B": {Error: someErr, ErrorSource: backend.ErrorSourcePlugin},
|
||||
"C": {Error: someErr, ErrorSource: backend.ErrorSourceDownstream},
|
||||
},
|
||||
},
|
||||
expStatusSource: pluginrequestmeta.StatusSourcePlugin,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cdt := handlertest.NewHandlerMiddlewareTest(t,
|
||||
handlertest.WithMiddlewares(
|
||||
NewPluginRequestMetaMiddleware(),
|
||||
NewStatusSourceMiddleware(),
|
||||
),
|
||||
)
|
||||
cdt.TestHandler.QueryDataFunc = func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
cdt.QueryDataCtx = ctx
|
||||
return tc.queryDataResponse, nil
|
||||
}
|
||||
|
||||
_, _ = cdt.MiddlewareHandler.QueryData(context.Background(), &backend.QueryDataRequest{})
|
||||
|
||||
ss := pluginrequestmeta.StatusSourceFromContext(cdt.QueryDataCtx)
|
||||
require.Equal(t, tc.expStatusSource, ss)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user