grafana/pkg/infra/httpclient/httpclientprovider/prometheus_metrics_middleware_test.go
Giuseppe Guerra a89202eab2
Plugins: Improve instrumentation by adding metrics and tracing (#61035)
* WIP: Plugins tracing

* Trace ID middleware

* Add prometheus metrics and tracing to plugins updater

* Add TODOs

* Add instrumented http client

* Add tracing to grafana update checker

* Goimports

* Moved plugins tracing to middleware

* goimports, fix tests

* Removed X-Trace-Id header

* Fix comment in NewTracingHeaderMiddleware

* Add metrics to instrumented http client

* Add instrumented http client options

* Removed unused function

* Switch to contextual logger

* Refactoring, fix tests

* Moved InstrumentedHTTPClient and PrometheusMetrics to their own package

* Tracing middleware: handle errors

* Report span status codes when recording errors

* Add tests for tracing middleware

* Moved fakeSpan and fakeTracer to pkg/infra/tracing

* Add TestHTTPClientTracing

* Lint

* Changes after PR review

* Tests: Made "ended" in FakeSpan private, allow calling End only once

* Testing: panic in FakeSpan if span already ended

* Refactoring: Simplify Grafana updater checks

* Refactoring: Simplify plugins updater error checks and logs

* Fix wrong call to checkForUpdates -> instrumentedCheckForUpdates

* Tests: Fix wrong call to checkForUpdates -> instrumentedCheckForUpdates

* Log update checks duration, use Info log level for check succeeded logs

* Add plugin context span attributes in tracing_middleware

* Refactor prometheus metrics as httpclient middleware

* Fix call to ProvidePluginsService in plugins_test.go

* Propagate context to update checker outgoing http requests

* Plugin client tracing middleware: Removed operation name in status

* Fix tests

* Goimports tracing_middleware.go

* Goimports

* Fix imports

* Changed span name to plugins client middleware

* Add span name assertion in TestTracingMiddleware

* Removed Prometheus metrics middleware from grafana and plugins updatechecker

* Add span attributes for ds name, type, uid, panel and dashboard ids

* Fix http header reading in tracing middlewares

* Use contexthandler.FromContext, add X-Query-Group-Id

* Add test for RunStream

* Fix imports

* Changes from PR review

* TestTracingMiddleware: Changed assert to require for didPanic assertion

* Lint

* Fix imports
2023-03-28 11:01:06 +02:00

95 lines
3.1 KiB
Go

package httpclientprovider
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/require"
)
func TestPrometheusMetricsMiddleware(t *testing.T) {
noOpHandlerFunc := func(writer http.ResponseWriter, request *http.Request) {}
for _, tc := range []struct {
name string
handler http.HandlerFunc
assert func(t *testing.T, metrics *PrometheusMetrics)
}{
{
name: "successful",
assert: func(t *testing.T, metrics *PrometheusMetrics) {
require.Equal(t, float64(0), testutil.ToFloat64(metrics.inFlightGauge))
require.Equal(t, float64(1), testutil.ToFloat64(metrics.requestsCounter))
require.Equal(t, float64(0), testutil.ToFloat64(metrics.failureCounter))
},
},
{
name: "failure",
handler: func(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusInternalServerError)
},
assert: func(t *testing.T, metrics *PrometheusMetrics) {
require.Equal(t, float64(0), testutil.ToFloat64(metrics.inFlightGauge))
require.Equal(t, float64(1), testutil.ToFloat64(metrics.requestsCounter))
require.Equal(t, float64(1), testutil.ToFloat64(metrics.failureCounter))
},
},
} {
t.Run(tc.name, func(t *testing.T) {
// Create metrics and make sure they are 0
metrics := NewPrometheusMetricsMiddleware("test")
require.Equal(t, float64(0), testutil.ToFloat64(metrics.inFlightGauge))
require.Equal(t, float64(0), testutil.ToFloat64(metrics.requestsCounter))
require.Equal(t, float64(0), testutil.ToFloat64(metrics.failureCounter))
// Set up test server
// Default to noOpHandlerFunc if it's not provided in test case
h := tc.handler
if h == nil {
h = noOpHandlerFunc
}
srv := httptest.NewServer(h)
t.Cleanup(srv.Close)
// Make request with the prometheus handling middleware
cl, err := httpclient.New(httpclient.Options{
Middlewares: []httpclient.Middleware{PrometheusMetricsMiddleware(metrics)},
})
require.NoError(t, err)
resp, err := cl.Get(srv.URL)
defer func() { _ = resp.Body.Close() }()
require.NoError(t, err)
require.NotNil(t, resp)
// Run test-case-specific assertions
tc.assert(t, metrics)
})
}
t.Run("in flight", func(t *testing.T) {
metrics := NewPrometheusMetricsMiddleware("test")
require.Equal(t, float64(0), testutil.ToFloat64(metrics.inFlightGauge))
srv := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
// Assert in-flight requests
require.Equal(t, float64(1), testutil.ToFloat64(metrics.inFlightGauge), "in flight should increase during request")
}))
t.Cleanup(srv.Close)
cl, err := httpclient.New(httpclient.Options{
Middlewares: []httpclient.Middleware{PrometheusMetricsMiddleware(metrics)},
})
require.NoError(t, err)
resp, err := cl.Get(srv.URL)
defer func() { _ = resp.Body.Close() }()
require.NoError(t, err)
require.NotNil(t, resp)
require.Equal(t, float64(0), testutil.ToFloat64(metrics.inFlightGauge), "in flight should decrease after response")
})
}