mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Add support for HTTP logger (#46578)
This commit is contained in:
parent
565430f297
commit
b5d57c45e3
@ -36,6 +36,10 @@ func New(cfg *setting.Cfg, validator models.PluginRequestValidator, tracer traci
|
||||
middlewares = append(middlewares, SigV4Middleware(cfg.SigV4VerboseLogging))
|
||||
}
|
||||
|
||||
if httpLoggingEnabled(cfg.PluginSettings) {
|
||||
middlewares = append(middlewares, HTTPLoggerMiddleware(cfg.PluginSettings))
|
||||
}
|
||||
|
||||
setDefaultTimeoutOptions(cfg)
|
||||
|
||||
return newProviderFunc(sdkhttpclient.ProviderOptions{
|
||||
|
@ -60,4 +60,30 @@ func TestHTTPClientProvider(t *testing.T) {
|
||||
require.Equal(t, ResponseLimitMiddlewareName, o.Middlewares[6].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, SigV4MiddlewareName, o.Middlewares[8].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
})
|
||||
|
||||
t.Run("When creating new provider and http logging is enabled for one plugin, it should apply expected middleware", func(t *testing.T) {
|
||||
origNewProviderFunc := newProviderFunc
|
||||
providerOpts := []sdkhttpclient.ProviderOptions{}
|
||||
newProviderFunc = func(opts ...sdkhttpclient.ProviderOptions) *sdkhttpclient.Provider {
|
||||
providerOpts = opts
|
||||
return nil
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
newProviderFunc = origNewProviderFunc
|
||||
})
|
||||
tracer := tracing.InitializeTracerForTest()
|
||||
_ = New(&setting.Cfg{PluginSettings: setting.PluginSettings{"example": {"har_log_enabled": "true"}}}, &validations.OSSPluginRequestValidator{}, tracer)
|
||||
require.Len(t, providerOpts, 1)
|
||||
o := providerOpts[0]
|
||||
require.Len(t, o.Middlewares, 9)
|
||||
require.Equal(t, TracingMiddlewareName, o.Middlewares[0].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, DataSourceMetricsMiddlewareName, o.Middlewares[1].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, SetUserAgentMiddlewareName, o.Middlewares[2].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, sdkhttpclient.BasicAuthenticationMiddlewareName, o.Middlewares[3].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, sdkhttpclient.CustomHeadersMiddlewareName, o.Middlewares[4].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, sdkhttpclient.ContextualMiddlewareName, o.Middlewares[5].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, ResponseLimitMiddlewareName, o.Middlewares[6].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, HostRedirectValidationMiddlewareName, o.Middlewares[7].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
require.Equal(t, HTTPLoggerMiddlewareName, o.Middlewares[8].(sdkhttpclient.MiddlewareName).MiddlewareName())
|
||||
})
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package httpclientprovider
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
httplogger "github.com/grafana/grafana-plugin-sdk-go/experimental/http_logger"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const HTTPLoggerMiddlewareName = "http-logger"
|
||||
|
||||
func HTTPLoggerMiddleware(cfg setting.PluginSettings) sdkhttpclient.Middleware {
|
||||
return sdkhttpclient.NamedMiddlewareFunc(HTTPLoggerMiddlewareName, func(opts sdkhttpclient.Options, next http.RoundTripper) http.RoundTripper {
|
||||
datasourceType, exists := opts.Labels["datasource_type"]
|
||||
if !exists {
|
||||
return next
|
||||
}
|
||||
|
||||
enabled, path := getLoggerSettings(datasourceType, cfg)
|
||||
if !enabled {
|
||||
return next
|
||||
}
|
||||
|
||||
return httplogger.NewHTTPLogger(datasourceType, next, httplogger.Options{
|
||||
Path: path,
|
||||
EnabledFn: func() bool { return true },
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func httpLoggingEnabled(cfg setting.PluginSettings) bool {
|
||||
for _, settings := range cfg {
|
||||
if enabled := settings["har_log_enabled"]; enabled == "true" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getLoggerSettings(datasourceType string, cfg setting.PluginSettings) (enabled bool, path string) {
|
||||
settings, ok := cfg[datasourceType]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if e, ok := settings["har_log_enabled"]; ok {
|
||||
enabled = e == "true"
|
||||
}
|
||||
if p, ok := settings["har_log_path"]; ok {
|
||||
path = p
|
||||
}
|
||||
return
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package httpclientprovider
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/experimental/e2e/storage"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHTTPLoggerMiddleware(t *testing.T) {
|
||||
t.Run("Should return middleware name", func(t *testing.T) {
|
||||
mw := HTTPLoggerMiddleware(setting.PluginSettings{})
|
||||
middlewareName, ok := mw.(httpclient.MiddlewareName)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, HTTPLoggerMiddlewareName, middlewareName.MiddlewareName())
|
||||
})
|
||||
|
||||
t.Run("Should return next http.RoundTripper if not enabled", func(t *testing.T) {
|
||||
tempPath := path.Join(os.TempDir(), fmt.Sprintf("http_logger_test_%d.har", time.Now().UnixMilli()))
|
||||
ctx := &testContext{}
|
||||
finalRoundTripper := ctx.createRoundTripper("finalrt")
|
||||
mw := HTTPLoggerMiddleware(setting.PluginSettings{"example-datasource": {"har_log_enabled": "false", "har_log_path": tempPath}})
|
||||
rt := mw.CreateMiddleware(httpclient.Options{Labels: map[string]string{"datasource_type": "example-datasource"}}, finalRoundTripper)
|
||||
require.NotNil(t, rt)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://", nil)
|
||||
require.NoError(t, err)
|
||||
res, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
if res.Body != nil {
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
_, err = os.Stat(tempPath)
|
||||
require.Equal(t, true, errors.Is(err, os.ErrNotExist))
|
||||
})
|
||||
|
||||
t.Run("Should add HTTP logger if enabled", func(t *testing.T) {
|
||||
f, err := os.CreateTemp("", "example_*.har")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := os.Remove(f.Name())
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
ctx := &testContext{}
|
||||
finalRoundTripper := ctx.createRoundTripper("finalrt")
|
||||
mw := HTTPLoggerMiddleware(setting.PluginSettings{"example-datasource": {"har_log_enabled": "true", "har_log_path": f.Name()}})
|
||||
rt := mw.CreateMiddleware(httpclient.Options{Labels: map[string]string{"datasource_type": "example-datasource"}}, finalRoundTripper)
|
||||
require.NotNil(t, rt)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://", nil)
|
||||
require.NoError(t, err)
|
||||
res, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
if res.Body != nil {
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
har := storage.NewHARStorage(f.Name())
|
||||
require.Equal(t, 1, len(har.Entries()))
|
||||
require.Equal(t, "http:", har.Entries()[0].Request.URL.String())
|
||||
})
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package httpclientprovider
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
@ -13,6 +15,10 @@ type testContext struct {
|
||||
func (c *testContext) createRoundTripper(name string) http.RoundTripper {
|
||||
return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
||||
c.callChain = append(c.callChain, name)
|
||||
return &http.Response{StatusCode: http.StatusOK, Request: req}, nil
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Request: req,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString("")),
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ func ModelToInstanceSettings(ds *datasources.DataSource, decryptFn func(ds *data
|
||||
}
|
||||
|
||||
return &backend.DataSourceInstanceSettings{
|
||||
Type: ds.Type,
|
||||
ID: ds.Id,
|
||||
Name: ds.Name,
|
||||
URL: ds.Url,
|
||||
|
@ -387,6 +387,7 @@ func (s *Service) httpClientOptions(ctx context.Context, ds *datasources.DataSou
|
||||
Timeouts: timeouts,
|
||||
Headers: s.getCustomHeaders(ds.JsonData, decryptedValues),
|
||||
Labels: map[string]string{
|
||||
"datasource_type": ds.Type,
|
||||
"datasource_name": ds.Name,
|
||||
"datasource_uid": ds.Uid,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user