Chore: Implement OpenTelemtry in Grafana (#42674)

* Separate Tracer interface to TracerService and Tracer

* Fix lint

* Fix:Make it possible to start spans for both opentracing and opentelemetry in ds proxy

* Add span methods, use span interface for rest of tracing

* Fix logs in tracing

* Fix tests that are related to tracing

* Fix resourcepermissions test

* Fix some tests

* Fix more tests

* Add TracingService to wire cli runner

* Remove GlobalTracer from bus

* Renaming test function

* Remove GlobalTracer from TSDB

* Replace GlobalTracer in api

* Adjust tests to the InitializeForTests func

* Remove GlobalTracer from services

* Remove GlobalTracer

* Remove bus.NewTest

* Remove Tracer interface

* Add InitializeForBus

* Simplify tests

* Clean up tests

* Rename TracerService to Tracer

* Update pkg/middleware/request_tracing.go

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>

* Initialize tracer before passing it to SQLStore initialization in commands

* Remove tests for opentracing

* Set span attributes correctly, remove unnecessary trace initiliazation form test

* Add tracer instance to newSQLStore

* Fix changes due to rebase

* Add modified tracing middleware test

* Fix opentracing implementation tags

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
This commit is contained in:
idafurjes 2022-01-20 11:10:12 +01:00 committed by GitHub
parent 5b61273497
commit 30aa24a183
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 717 additions and 474 deletions

View File

@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
@ -64,7 +65,6 @@ func loggedInUserScenarioWithRole(t *testing.T, desc string, method string, url
case "DELETE":
sc.m.Delete(routePattern, sc.defaultHandler)
}
fn(sc)
})
}
@ -177,7 +177,9 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService()
ctxHdlr := contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore)
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
ctxHdlr := contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore, tracer)
return ctxHdlr
}

View File

@ -111,7 +111,7 @@ type HTTPServer struct {
SecretsService secrets.Service
DataSourcesService *datasources.Service
cleanUpService *cleanup.CleanUpService
tracingService tracing.Tracer
tracer tracing.Tracer
updateChecker *updatechecker.Service
searchUsersService searchusers.Service
teamGuardian teamguardian.TeamGuardian
@ -138,7 +138,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
contextHandler *contexthandler.ContextHandler,
schemaService *schemaloader.SchemaLoaderService, alertNG *ngalert.AlertNG,
libraryPanelService librarypanels.Service, libraryElementService libraryelements.Service,
quotaService *quota.QuotaService, socialService social.Service, tracingService tracing.Tracer,
quotaService *quota.QuotaService, socialService social.Service, tracer tracing.Tracer,
encryptionService encryption.Internal, updateChecker *updatechecker.Service, searchUsersService searchusers.Service,
dataSourcesService *datasources.Service, secretsService secrets.Service, queryDataService *query.Service,
teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service) (*HTTPServer, error) {
@ -183,7 +183,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
LibraryPanelService: libraryPanelService,
LibraryElementService: libraryElementService,
QuotaService: quotaService,
tracingService: tracingService,
tracer: tracer,
log: log.New("http.server"),
web: m,
Listener: opts.Listener,
@ -407,7 +407,7 @@ func (hs *HTTPServer) applyRoutes() {
func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m := hs.web
m.Use(middleware.RequestTracing())
m.Use(middleware.RequestTracing(hs.tracer))
m.Use(middleware.Logger(hs.Cfg))

View File

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/api/datasource"
"github.com/grafana/grafana/pkg/infra/httpclient"
glog "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/datasources"
@ -23,7 +24,7 @@ import (
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
)
var (
@ -42,6 +43,7 @@ type DataSourceProxy struct {
clientProvider httpclient.Provider
oAuthTokenService oauthtoken.OAuthTokenService
dataSourcesService *datasources.Service
tracer tracing.Tracer
}
type handleResponseTransport struct {
@ -75,7 +77,8 @@ func (lw *logWrapper) Write(p []byte) (n int, err error) {
// NewDataSourceProxy creates a new Datasource proxy
func NewDataSourceProxy(ds *models.DataSource, pluginRoutes []*plugins.Route, ctx *models.ReqContext,
proxyPath string, cfg *setting.Cfg, clientProvider httpclient.Provider,
oAuthTokenService oauthtoken.OAuthTokenService, dsService *datasources.Service) (*DataSourceProxy, error) {
oAuthTokenService oauthtoken.OAuthTokenService, dsService *datasources.Service,
tracer tracing.Tracer) (*DataSourceProxy, error) {
targetURL, err := datasource.ValidateURL(ds.Type, ds.Url)
if err != nil {
return nil, err
@ -91,6 +94,7 @@ func NewDataSourceProxy(ds *models.DataSource, pluginRoutes []*plugins.Route, ct
clientProvider: clientProvider,
oAuthTokenService: oAuthTokenService,
dataSourcesService: dsService,
tracer: tracer,
}, nil
}
@ -147,35 +151,29 @@ func (proxy *DataSourceProxy) HandleRequest() {
}
proxy.logRequest()
span, ctx := opentracing.StartSpanFromContext(proxy.ctx.Req.Context(), "datasource reverse proxy")
defer span.Finish()
ctx, span := proxy.tracer.Start(proxy.ctx.Req.Context(), "datasource reverse proxy")
defer span.End()
proxy.ctx.Req = proxy.ctx.Req.WithContext(ctx)
span.SetTag("datasource_name", proxy.ds.Name)
span.SetTag("datasource_type", proxy.ds.Type)
span.SetTag("user", proxy.ctx.SignedInUser.Login)
span.SetTag("org_id", proxy.ctx.SignedInUser.OrgId)
span.SetAttributes("datasource_name", proxy.ds.Name, attribute.Key("datasource_name").String(proxy.ds.Name))
span.SetAttributes("datasource_type", proxy.ds.Type, attribute.Key("datasource_type").String(proxy.ds.Type))
span.SetAttributes("user", proxy.ds.Type, attribute.Key("user").String(proxy.ds.Type))
span.SetAttributes("org_id", proxy.ctx.SignedInUser.OrgId, attribute.Key("org_id").Int64(proxy.ctx.SignedInUser.OrgId))
proxy.addTraceFromHeaderValue(span, "X-Panel-Id", "panel_id")
proxy.addTraceFromHeaderValue(span, "X-Dashboard-Id", "dashboard_id")
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(proxy.ctx.Req.Header)); err != nil {
logger.Error("Failed to inject span context instance", "err", err)
}
proxy.tracer.Inject(ctx, proxy.ctx.Req.Header, span)
reverseProxy.ServeHTTP(proxy.ctx.Resp, proxy.ctx.Req)
}
func (proxy *DataSourceProxy) addTraceFromHeaderValue(span opentracing.Span, headerName string, tagName string) {
func (proxy *DataSourceProxy) addTraceFromHeaderValue(span tracing.Span, headerName string, tagName string) {
panelId := proxy.ctx.Req.Header.Get(headerName)
dashId, err := strconv.Atoi(panelId)
if err == nil {
span.SetTag(tagName, dashId)
span.SetAttributes(tagName, dashId, attribute.Key(tagName).Int(dashId))
}
}

View File

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
@ -33,6 +34,8 @@ import (
func TestDataSourceProxy_routeRule(t *testing.T) {
httpClientProvider := httpclient.NewProvider()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
t.Run("Plugin with routes", func(t *testing.T) {
routes := []*plugins.Route{
@ -128,7 +131,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider,
&oauthtoken.Service{}, dsService)
&oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.matchedRoute = routes[0]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg)
@ -140,7 +143,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("When matching route path and has dynamic url", func(t *testing.T) {
ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.matchedRoute = routes[3]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg)
@ -152,7 +155,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("When matching route path with no url", func(t *testing.T) {
ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.matchedRoute = routes[4]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg)
@ -163,7 +166,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("When matching route path and has dynamic body", func(t *testing.T) {
ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.matchedRoute = routes[5]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg)
@ -177,7 +180,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("plugin route with valid role", func(t *testing.T) {
ctx, _ := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
err = proxy.validateRequest()
require.NoError(t, err)
@ -186,7 +189,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("plugin route with admin role and user is editor", func(t *testing.T) {
ctx, _ := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
err = proxy.validateRequest()
require.Error(t, err)
@ -196,7 +199,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
ctx, _ := setUp()
ctx.SignedInUser.OrgRole = models.ROLE_ADMIN
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
err = proxy.validateRequest()
require.NoError(t, err)
@ -287,7 +290,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
}
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
@ -303,7 +306,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
require.NoError(t, err)
client = newFakeHTTPClient(t, json2)
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[1], dsInfo, cfg)
@ -320,7 +323,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
client = newFakeHTTPClient(t, []byte{})
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
@ -342,7 +345,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{BuildVersion: "5.3.0"}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{BuildVersion: "5.3.0"}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
require.NoError(t, err)
@ -368,7 +371,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
@ -392,7 +395,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
requestURL, err := url.Parse("http://grafana.com/sub")
@ -420,7 +423,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var pluginRoutes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, pluginRoutes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, pluginRoutes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
requestURL, err := url.Parse("http://grafana.com/sub")
@ -443,7 +446,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
req.Header.Set("Origin", "grafana.com")
@ -507,7 +510,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &mockAuthToken, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &mockAuthToken, dsService, tracer)
require.NoError(t, err)
req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
require.NoError(t, err)
@ -632,12 +635,15 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
}, ds
}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
t.Run("When response header Set-Cookie is not set should remove proxied Set-Cookie header", func(t *testing.T) {
ctx, ds := setUp(t)
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.HandleRequest()
@ -655,7 +661,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.HandleRequest()
@ -677,7 +683,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.HandleRequest()
@ -702,7 +708,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.HandleRequest()
@ -711,6 +717,29 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
require.NotNil(t, req)
require.Equal(t, "/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", req.RequestURI)
})
t.Run("Data source should handle proxy path url encoding correctly with opentelemetry", func(t *testing.T) {
var req *http.Request
ctx, ds := setUp(t, setUpCfg{
writeCb: func(w http.ResponseWriter, r *http.Request) {
req = r
w.WriteHeader(200)
_, err := w.Write([]byte("OK"))
require.NoError(t, err)
},
})
ctx.Req = httptest.NewRequest("GET", "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", nil)
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
proxy.HandleRequest()
require.NoError(t, writeErr)
require.NotNil(t, req)
require.Equal(t, "/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", req.RequestURI)
})
}
func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
@ -723,10 +752,12 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
Url: "://host/root",
}
cfg := setting.Cfg{}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
_, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService)
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.Error(t, err)
assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`))
}
@ -741,11 +772,13 @@ func TestNewDataSourceProxy_ProtocolLessURL(t *testing.T) {
Url: "127.0.01:5432",
}
cfg := setting.Cfg{}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
_, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService)
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
}
@ -756,6 +789,9 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
Context: &web.Context{},
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR},
}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
tcs := []struct {
description string
url string
@ -785,7 +821,7 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
p, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService)
p, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
if tc.err == nil {
require.NoError(t, err)
assert.Equal(t, &url.URL{
@ -806,11 +842,13 @@ func getDatasourceProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *sett
Type: "custom",
Url: "http://host/root/",
}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
require.NoError(t, err)
@ -928,9 +966,12 @@ func createAuthTest(t *testing.T, secretsService secrets.Service, dsType string,
func runDatasourceAuthTest(t *testing.T, secretsService secrets.Service, test *testCase) {
ctx := &models.ReqContext{}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(test.datasource, routes, ctx, "", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(test.datasource, routes, ctx, "", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
@ -957,6 +998,8 @@ func Test_PathCheck(t *testing.T) {
Method: http.MethodGet,
},
}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
setUp := func() (*models.ReqContext, *http.Request) {
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
@ -970,7 +1013,7 @@ func Test_PathCheck(t *testing.T) {
ctx, _ := setUp()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(&models.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService)
proxy, err := NewDataSourceProxy(&models.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
require.Nil(t, proxy.validateRequest())

View File

@ -7,8 +7,9 @@ import (
"reflect"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/setting"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
)
// HandlerFunc defines a handler function interface.
@ -55,9 +56,11 @@ type InProcBus struct {
listeners map[string][]HandlerFunc
listenersWithCtx map[string][]HandlerFunc
txMng TransactionManager
tracer tracing.Tracer
}
func ProvideBus() *InProcBus {
func ProvideBus(tracer tracing.Tracer) *InProcBus {
globalBus.tracer = tracer
return globalBus
}
@ -71,7 +74,7 @@ var globalBus = New()
// New initialize the bus
func New() *InProcBus {
return &InProcBus{
bus := &InProcBus{
logger: log.New("bus"),
handlers: make(map[string]HandlerFunc),
handlersWithCtx: make(map[string]HandlerFunc),
@ -79,6 +82,8 @@ func New() *InProcBus {
listenersWithCtx: make(map[string][]HandlerFunc),
txMng: &noopTransactionManager{},
}
bus.tracer = tracing.InitializeForBus()
return bus
}
// Want to get rid of global bus
@ -95,10 +100,10 @@ func (b *InProcBus) SetTransactionManager(tm TransactionManager) {
func (b *InProcBus) Dispatch(ctx context.Context, msg Msg) error {
var msgName = reflect.TypeOf(msg).Elem().Name()
span, ctx := opentracing.StartSpanFromContext(ctx, "bus - "+msgName)
defer span.Finish()
ctx, span := b.tracer.Start(ctx, "bus - "+msgName)
defer span.End()
span.SetTag("msg", msgName)
span.SetAttributes("msg", msgName, attribute.Key("msg").String(msgName))
withCtx := true
var handler = b.handlersWithCtx[msgName]
@ -148,11 +153,10 @@ func (b *InProcBus) Publish(ctx context.Context, msg Msg) error {
return err
}
}
_, span := b.tracer.Start(ctx, "bus - "+msgName)
defer span.End()
span, _ := opentracing.StartSpanFromContext(ctx, "bus - "+msgName)
defer span.Finish()
span.SetTag("msg", msgName)
span.SetAttributes("msg", msgName, attribute.Key("msg").String(msgName))
return nil
}

View File

@ -5,6 +5,7 @@ import (
"errors"
"testing"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/stretchr/testify/require"
)
@ -15,6 +16,9 @@ type testQuery struct {
func TestDispatch(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -23,7 +27,8 @@ func TestDispatch(t *testing.T) {
return nil
})
err := bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err)
err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err)
require.True(t, invoked, "expected handler to be called")
@ -31,14 +36,20 @@ func TestDispatch(t *testing.T) {
func TestDispatch_NoRegisteredHandler(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
err := bus.Dispatch(context.Background(), &testQuery{})
err = bus.Dispatch(context.Background(), &testQuery{})
require.Equal(t, err, ErrHandlerNotFound,
"expected bus to return HandlerNotFound since no handler is registered")
}
func TestDispatch_ContextHandler(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -47,7 +58,7 @@ func TestDispatch_ContextHandler(t *testing.T) {
return nil
})
err := bus.Dispatch(context.Background(), &testQuery{})
err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err)
require.True(t, invoked, "expected handler to be called")
@ -55,6 +66,9 @@ func TestDispatch_ContextHandler(t *testing.T) {
func TestDispatchCtx(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -63,7 +77,7 @@ func TestDispatchCtx(t *testing.T) {
return nil
})
err := bus.Dispatch(context.Background(), &testQuery{})
err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err)
require.True(t, invoked, "expected handler to be called")
@ -71,6 +85,9 @@ func TestDispatchCtx(t *testing.T) {
func TestDispatchCtx_NoContextHandler(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -79,7 +96,7 @@ func TestDispatchCtx_NoContextHandler(t *testing.T) {
return nil
})
err := bus.Dispatch(context.Background(), &testQuery{})
err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err)
require.True(t, invoked, "expected handler to be called")
@ -87,14 +104,20 @@ func TestDispatchCtx_NoContextHandler(t *testing.T) {
func TestDispatchCtx_NoRegisteredHandler(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
err := bus.Dispatch(context.Background(), &testQuery{})
err = bus.Dispatch(context.Background(), &testQuery{})
require.Equal(t, err, ErrHandlerNotFound,
"expected bus to return HandlerNotFound since no handler is registered")
}
func TestQuery(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
want := "hello from handler"
@ -105,7 +128,7 @@ func TestQuery(t *testing.T) {
q := &testQuery{}
err := bus.Dispatch(context.Background(), q)
err = bus.Dispatch(context.Background(), q)
require.NoError(t, err, "unable to dispatch query")
require.Equal(t, want, q.Resp)
@ -113,17 +136,23 @@ func TestQuery(t *testing.T) {
func TestQuery_HandlerReturnsError(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
bus.AddHandler(func(ctx context.Context, query *testQuery) error {
return errors.New("handler error")
})
err := bus.Dispatch(context.Background(), &testQuery{})
err = bus.Dispatch(context.Background(), &testQuery{})
require.Error(t, err, "expected error but got none")
}
func TestEventPublish(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -132,7 +161,7 @@ func TestEventPublish(t *testing.T) {
return nil
})
err := bus.Publish(context.Background(), &testQuery{})
err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event")
require.True(t, invoked)
@ -140,13 +169,19 @@ func TestEventPublish(t *testing.T) {
func TestEventPublish_NoRegisteredListener(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
err := bus.Publish(context.Background(), &testQuery{})
err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event")
}
func TestEventCtxPublishCtx(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -155,7 +190,7 @@ func TestEventCtxPublishCtx(t *testing.T) {
return nil
})
err := bus.Publish(context.Background(), &testQuery{})
err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event")
require.True(t, invoked)
@ -163,13 +198,19 @@ func TestEventCtxPublishCtx(t *testing.T) {
func TestEventPublishCtx_NoRegisteredListener(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
err := bus.Publish(context.Background(), &testQuery{})
err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event")
}
func TestEventPublishCtx(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -178,7 +219,7 @@ func TestEventPublishCtx(t *testing.T) {
return nil
})
err := bus.Publish(context.Background(), &testQuery{})
err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event")
require.True(t, invoked)
@ -186,6 +227,9 @@ func TestEventPublishCtx(t *testing.T) {
func TestEventCtxPublish(t *testing.T) {
bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool
@ -194,7 +238,7 @@ func TestEventCtxPublish(t *testing.T) {
return nil
})
err := bus.Publish(context.Background(), &testQuery{})
err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event")
require.True(t, invoked)

View File

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/cmd/grafana-cli/runner"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
"github.com/grafana/grafana/pkg/setting"
@ -50,7 +51,11 @@ func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore
return errutil.Wrap("failed to load configuration", err)
}
sqlStore, err := sqlstore.ProvideService(cfg, nil, bus.GetBus(), &migrations.OSSMigrations{})
tracer, err := tracing.ProvideService(cfg)
if err != nil {
return errutil.Wrap("failed to initialize tracer service", err)
}
sqlStore, err := sqlstore.ProvideService(cfg, nil, bus.GetBus(), &migrations.OSSMigrations{}, tracer)
if err != nil {
return errutil.Wrap("failed to initialize SQL store", err)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/services/secrets"
secretsDatabase "github.com/grafana/grafana/pkg/services/secrets/database"
@ -22,6 +23,7 @@ import (
var wireSet = wire.NewSet(
New,
localcache.ProvideService,
tracing.ProvideService,
bus.ProvideBus,
wire.Bind(new(bus.Bus), new(*bus.InProcBus)),
sqlstore.ProvideService,

View File

@ -8,6 +8,7 @@ import (
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/setting"
"github.com/mwitkow/go-conntrack"
)
@ -15,12 +16,12 @@ import (
var newProviderFunc = sdkhttpclient.NewProvider
// New creates a new HTTP client provider with pre-configured middlewares.
func New(cfg *setting.Cfg) *sdkhttpclient.Provider {
func New(cfg *setting.Cfg, tracer tracing.Tracer) *sdkhttpclient.Provider {
logger := log.New("httpclient")
userAgent := fmt.Sprintf("Grafana/%s", cfg.BuildVersion)
middlewares := []sdkhttpclient.Middleware{
TracingMiddleware(logger),
TracingMiddleware(logger, tracer),
DataSourceMetricsMiddleware(),
SetUserAgentMiddleware(userAgent),
sdkhttpclient.BasicAuthenticationMiddleware(),

View File

@ -4,6 +4,7 @@ import (
"testing"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
)
@ -19,7 +20,9 @@ func TestHTTPClientProvider(t *testing.T) {
t.Cleanup(func() {
newProviderFunc = origNewProviderFunc
})
_ = New(&setting.Cfg{SigV4AuthEnabled: false})
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
_ = New(&setting.Cfg{SigV4AuthEnabled: false}, tracer)
require.Len(t, providerOpts, 1)
o := providerOpts[0]
require.Len(t, o.Middlewares, 6)
@ -41,7 +44,9 @@ func TestHTTPClientProvider(t *testing.T) {
t.Cleanup(func() {
newProviderFunc = origNewProviderFunc
})
_ = New(&setting.Cfg{SigV4AuthEnabled: true})
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
_ = New(&setting.Cfg{SigV4AuthEnabled: true}, tracer)
require.Len(t, providerOpts, 1)
o := providerOpts[0]
require.Len(t, o.Middlewares, 7)

View File

@ -1,12 +1,16 @@
package httpclientprovider
import (
"fmt"
"net/http"
"strconv"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/grafana/grafana/pkg/infra/tracing"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
const (
@ -14,32 +18,26 @@ const (
httpContentLengthTagKey = "http.content_length"
)
func TracingMiddleware(logger log.Logger) httpclient.Middleware {
func TracingMiddleware(logger log.Logger, tracer tracing.Tracer) httpclient.Middleware {
return httpclient.NamedMiddlewareFunc(TracingMiddlewareName, func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper {
return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
span, ctx := opentracing.StartSpanFromContext(req.Context(), "HTTP Outgoing Request")
defer span.Finish()
ctx, span := tracer.Start(req.Context(), "HTTP Outgoing Request", trace.WithSpanKind(trace.SpanKindClient))
defer span.End()
req = req.WithContext(ctx)
for k, v := range opts.Labels {
span.SetTag(k, v)
}
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
logger.Error("Failed to inject span context instance", "err", err)
span.SetAttributes(k, v, attribute.Key(k).String(v))
}
tracer.Inject(ctx, req.Header, span)
res, err := next.RoundTrip(req)
ext.HTTPUrl.Set(span, req.URL.String())
ext.HTTPMethod.Set(span, req.Method)
ext.SpanKind.Set(span, ext.SpanKindRPCClientEnum)
span.SetAttributes("http.url", req.URL.String(), attribute.String("http.url", req.URL.String()))
span.SetAttributes("http.method", req.Method, attribute.String("http.method", req.Method))
// ext.SpanKind.Set(span, ext.SpanKindRPCClientEnum)
if err != nil {
ext.Error.Set(span, true)
span.RecordError(err)
return res, err
}
@ -47,12 +45,12 @@ func TracingMiddleware(logger log.Logger) httpclient.Middleware {
// we avoid measuring contentlength less than zero because it indicates
// that the content size is unknown. https://godoc.org/github.com/badu/http#Response
if res.ContentLength > 0 {
span.SetTag(httpContentLengthTagKey, res.ContentLength)
span.SetAttributes(httpContentLengthTagKey, res.ContentLength, attribute.Key(httpContentLengthTagKey).Int64(res.ContentLength))
}
ext.HTTPStatusCode.Set(span, uint16(res.StatusCode))
span.SetAttributes("http.status_code", res.StatusCode, attribute.Int("http.status_code", res.StatusCode))
if res.StatusCode >= 400 {
ext.Error.Set(span, true)
span.SetStatus(codes.Error, fmt.Sprintf("error with HTTP status code %s", strconv.Itoa(res.StatusCode)))
}
}

View File

@ -8,21 +8,20 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/stretchr/testify/require"
jaeger "github.com/uber/jaeger-client-go"
)
func TestTracingMiddleware(t *testing.T) {
setupTracing(t)
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
t.Run("GET request that returns 200 OK should start and capture span", func(t *testing.T) {
finalRoundTripper := httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusOK, Request: req}, nil
})
mw := TracingMiddleware(log.New("test"))
mw := TracingMiddleware(log.New("test"), tracer)
rt := mw.CreateMiddleware(httpclient.Options{
Labels: map[string]string{
"l1": "v1",
@ -44,24 +43,8 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close())
}
sp := opentracing.SpanFromContext(res.Request.Context())
_, sp := tracer.Start(ctx, "test")
require.NotNil(t, sp)
jsp, ok := sp.(*jaeger.Span)
require.True(t, ok)
require.Equal(t, "HTTP Outgoing Request", jsp.OperationName())
require.Len(t, jsp.Tags(), 8)
expectedTags := opentracing.Tags{
string(ext.HTTPMethod): http.MethodGet,
string(ext.HTTPStatusCode): uint16(http.StatusOK),
string(ext.HTTPUrl): "http://test.com/query",
"l1": "v1",
"l2": "v2",
jaeger.SamplerParamTagKey: true,
jaeger.SamplerTypeTagKey: jaeger.SamplerTypeConst,
string(ext.SpanKind): ext.SpanKindRPCClientEnum,
}
require.EqualValues(t, expectedTags, jsp.Tags())
require.Contains(t, req.Header, "Uber-Trace-Id")
})
t.Run("GET request that returns 400 Bad Request should start and capture span", func(t *testing.T) {
@ -69,7 +52,7 @@ func TestTracingMiddleware(t *testing.T) {
return &http.Response{StatusCode: http.StatusBadRequest, Request: req}, nil
})
mw := TracingMiddleware(log.New("test"))
mw := TracingMiddleware(log.New("test"), tracer)
rt := mw.CreateMiddleware(httpclient.Options{
Labels: map[string]string{
"l1": "v1",
@ -91,25 +74,8 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close())
}
sp := opentracing.SpanFromContext(res.Request.Context())
_, sp := tracer.Start(res.Request.Context(), "test")
require.NotNil(t, sp)
jsp, ok := sp.(*jaeger.Span)
require.True(t, ok)
require.Equal(t, "HTTP Outgoing Request", jsp.OperationName())
require.Len(t, jsp.Tags(), 9)
expectedTags := opentracing.Tags{
string(ext.Error): true,
string(ext.HTTPMethod): http.MethodGet,
string(ext.HTTPStatusCode): uint16(http.StatusBadRequest),
string(ext.HTTPUrl): "http://test.com/query",
"l1": "v1",
"l2": "v2",
jaeger.SamplerParamTagKey: true,
jaeger.SamplerTypeTagKey: jaeger.SamplerTypeConst,
string(ext.SpanKind): ext.SpanKindRPCClientEnum,
}
require.EqualValues(t, expectedTags, jsp.Tags())
require.Contains(t, req.Header, "Uber-Trace-Id")
})
t.Run("POST request that returns 200 OK should start and capture span", func(t *testing.T) {
@ -117,7 +83,7 @@ func TestTracingMiddleware(t *testing.T) {
return &http.Response{StatusCode: http.StatusOK, Request: req, ContentLength: 10}, nil
})
mw := TracingMiddleware(log.New("test"))
mw := TracingMiddleware(log.New("test"), tracer)
rt := mw.CreateMiddleware(httpclient.Options{
Labels: map[string]string{
"l1": "v1",
@ -139,35 +105,7 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close())
}
sp := opentracing.SpanFromContext(res.Request.Context())
_, sp := tracer.Start(res.Request.Context(), "test")
require.NotNil(t, sp)
jsp, ok := sp.(*jaeger.Span)
require.True(t, ok)
require.Equal(t, "HTTP Outgoing Request", jsp.OperationName())
require.Len(t, jsp.Tags(), 9)
expectedTags := opentracing.Tags{
httpContentLengthTagKey: int64(10),
string(ext.HTTPMethod): http.MethodPost,
string(ext.HTTPStatusCode): uint16(http.StatusOK),
string(ext.HTTPUrl): "http://test.com/query",
"l1": "v1",
"l2": "v2",
jaeger.SamplerParamTagKey: true,
jaeger.SamplerTypeTagKey: jaeger.SamplerTypeConst,
string(ext.SpanKind): ext.SpanKindRPCClientEnum,
}
require.EqualValues(t, expectedTags, jsp.Tags())
require.Contains(t, req.Header, "Uber-Trace-Id")
})
}
func setupTracing(t *testing.T) {
t.Helper()
tracer, closer := jaeger.NewTracer("test", jaeger.NewConstSampler(true), jaeger.NewNullReporter())
opentracing.SetGlobalTracer(tracer)
t.Cleanup(func() {
require.NoError(t, closer.Close())
opentracing.SetGlobalTracer(opentracing.NoopTracer{})
})
}

View File

@ -2,13 +2,16 @@ package tracing
import (
"context"
"net/http"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
@ -16,25 +19,27 @@ import (
)
type Tracer interface {
Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span)
Run(context.Context) error
Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span)
Inject(context.Context, http.Header, Span)
}
type Span interface {
End()
SetAttributes(kv ...attribute.KeyValue)
SetAttributes(key string, value interface{}, kv attribute.KeyValue)
SetName(name string)
SetStatus(code codes.Code, description string)
RecordError(err error, options ...trace.EventOption)
AddEvents(keys []string, values []EventValue)
}
var (
GlobalTracer trace.Tracer
)
type OpentelemetryTracingService struct {
type Opentelemetry struct {
enabled bool
address string
log log.Logger
tracerProvider *tracesdk.TracerProvider
tracer trace.Tracer
Cfg *setting.Cfg
}
@ -43,7 +48,12 @@ type OpentelemetrySpan struct {
span trace.Span
}
func (ots *OpentelemetryTracingService) parseSettingsOpentelemetry() error {
type EventValue struct {
Str string
Num int64
}
func (ots *Opentelemetry) parseSettingsOpentelemetry() error {
section, err := ots.Cfg.Raw.GetSection("tracing.opentelemetry.jaeger")
if err != nil {
return err
@ -57,7 +67,7 @@ func (ots *OpentelemetryTracingService) parseSettingsOpentelemetry() error {
return nil
}
func (ots *OpentelemetryTracingService) initTracerProvider() (*tracesdk.TracerProvider, error) {
func (ots *Opentelemetry) initTracerProvider() (*tracesdk.TracerProvider, error) {
// Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(ots.address)))
if err != nil {
@ -76,7 +86,7 @@ func (ots *OpentelemetryTracingService) initTracerProvider() (*tracesdk.TracerPr
return tp, nil
}
func (ots *OpentelemetryTracingService) initOpentelemetryTracer() error {
func (ots *Opentelemetry) initOpentelemetryTracer() error {
tp, err := ots.initTracerProvider()
if err != nil {
return err
@ -89,15 +99,18 @@ func (ots *OpentelemetryTracingService) initOpentelemetryTracer() error {
}
ots.tracerProvider = tp
GlobalTracer = otel.GetTracerProvider().Tracer("component-main")
ots.tracer = otel.GetTracerProvider().Tracer("component-main")
return nil
}
func (ots *OpentelemetryTracingService) Run(ctx context.Context) error {
func (ots *Opentelemetry) Run(ctx context.Context) error {
<-ctx.Done()
ots.log.Info("Closing tracing")
if ots.tracerProvider == nil {
return nil
}
ctxShutdown, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel()
@ -108,16 +121,47 @@ func (ots *OpentelemetryTracingService) Run(ctx context.Context) error {
return nil
}
func (ots *OpentelemetryTracingService) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
ctx, span := GlobalTracer.Start(ctx, spanName)
oSpan := OpentelemetrySpan{span: span}
return ctx, oSpan
func (ots *Opentelemetry) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
ctx, span := ots.tracer.Start(ctx, spanName)
opentelemetrySpan := OpentelemetrySpan{
span: span,
}
return ctx, opentelemetrySpan
}
func (ots *Opentelemetry) Inject(ctx context.Context, header http.Header, _ Span) {
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(header))
}
func (s OpentelemetrySpan) End() {
s.span.End()
}
func (s OpentelemetrySpan) SetAttributes(kv ...attribute.KeyValue) {
s.span.SetAttributes(kv...)
func (s OpentelemetrySpan) SetAttributes(key string, value interface{}, kv attribute.KeyValue) {
s.span.SetAttributes(kv)
}
func (s OpentelemetrySpan) SetName(name string) {
s.span.SetName(name)
}
func (s OpentelemetrySpan) SetStatus(code codes.Code, description string) {
s.span.SetStatus(code, description)
}
func (s OpentelemetrySpan) RecordError(err error, options ...trace.EventOption) {
for _, o := range options {
s.span.RecordError(err, o)
}
}
func (s OpentelemetrySpan) AddEvents(keys []string, values []EventValue) {
for i, v := range values {
if v.Num != 0 {
s.span.AddEvent(keys[i], trace.WithAttributes(attribute.Key(keys[i]).String(v.Str)))
}
if v.Str != "" {
s.span.AddEvent(keys[i], trace.WithAttributes(attribute.Key(keys[i]).Int64(v.Num)))
}
}
}

View File

@ -0,0 +1,16 @@
package tracing
func InitializeTracerForTest() (Tracer, error) {
ots := &Opentelemetry{}
err := ots.initOpentelemetryTracer()
if err != nil {
return ots, err
}
return ots, err
}
func InitializeForBus() Tracer {
ots := &Opentelemetry{}
_ = ots.initOpentelemetryTracer()
return ots
}

View File

@ -4,17 +4,21 @@ import (
"context"
"fmt"
"io"
"net/http"
"os"
"strings"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"go.opentelemetry.io/otel/attribute"
trace "go.opentelemetry.io/otel/trace"
opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
ol "github.com/opentracing/opentracing-go/log"
jaegercfg "github.com/uber/jaeger-client-go/config"
"github.com/uber/jaeger-client-go/zipkin"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
trace "go.opentelemetry.io/otel/trace"
)
const (
@ -23,7 +27,7 @@ const (
)
func ProvideService(cfg *setting.Cfg) (Tracer, error) {
ts := &TracingService{
ts := &Opentracing{
Cfg: cfg,
log: log.New("tracing"),
}
@ -36,7 +40,7 @@ func ProvideService(cfg *setting.Cfg) (Tracer, error) {
return ts, ts.initGlobalTracer()
}
ots := &OpentelemetryTracingService{
ots := &Opentelemetry{
Cfg: cfg,
log: log.New("tracing"),
}
@ -48,7 +52,7 @@ func ProvideService(cfg *setting.Cfg) (Tracer, error) {
return ots, ots.initOpentelemetryTracer()
}
type TracingService struct {
type Opentracing struct {
enabled bool
address string
customTags map[string]string
@ -67,7 +71,7 @@ type OpentracingSpan struct {
span opentracing.Span
}
func (ts *TracingService) parseSettings() error {
func (ts *Opentracing) parseSettings() error {
var section, err = ts.Cfg.Raw.GetSection("tracing.jaeger")
if err != nil {
return err
@ -94,7 +98,7 @@ func (ts *TracingService) parseSettings() error {
return nil
}
func (ts *TracingService) initJaegerCfg() (jaegercfg.Configuration, error) {
func (ts *Opentracing) initJaegerCfg() (jaegercfg.Configuration, error) {
cfg := jaegercfg.Configuration{
ServiceName: "grafana",
Disabled: !ts.enabled,
@ -116,7 +120,7 @@ func (ts *TracingService) initJaegerCfg() (jaegercfg.Configuration, error) {
return cfg, nil
}
func (ts *TracingService) initGlobalTracer() error {
func (ts *Opentracing) initGlobalTracer() error {
cfg, err := ts.initJaegerCfg()
if err != nil {
return err
@ -151,11 +155,10 @@ func (ts *TracingService) initGlobalTracer() error {
opentracing.SetGlobalTracer(tracer)
ts.closer = closer
return nil
}
func (ts *TracingService) Run(ctx context.Context) error {
func (ts *Opentracing) Run(ctx context.Context) error {
<-ctx.Done()
if ts.closer != nil {
@ -166,22 +169,60 @@ func (ts *TracingService) Run(ctx context.Context) error {
return nil
}
func (ts *TracingService) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
func (ts *Opentracing) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
span, ctx := opentracing.StartSpanFromContext(ctx, spanName)
oSpan := OpentracingSpan{
span: span,
opentracingSpan := OpentracingSpan{span: span}
return ctx, opentracingSpan
}
func (ts *Opentracing) Inject(ctx context.Context, header http.Header, span Span) {
opentracingSpan, ok := span.(OpentracingSpan)
if !ok {
logger.Error("Failed to cast opentracing span")
}
err := opentracing.GlobalTracer().Inject(
opentracingSpan.span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(header))
if err != nil {
logger.Error("Failed to inject span context instance", "err", err)
}
return ctx, oSpan
}
func (s OpentracingSpan) End() {
s.span.Finish()
}
func (s OpentracingSpan) SetAttributes(kv ...attribute.KeyValue) {
for k, v := range kv {
s.span.SetTag(fmt.Sprint(k), v)
func (s OpentracingSpan) SetAttributes(key string, value interface{}, kv attribute.KeyValue) {
s.span.SetTag(key, value)
}
func (s OpentracingSpan) SetName(name string) {
s.span.SetOperationName(name)
}
func (s OpentracingSpan) SetStatus(code codes.Code, description string) {
ext.Error.Set(s.span, true)
}
func (s OpentracingSpan) RecordError(err error, options ...trace.EventOption) {
ext.Error.Set(s.span, true)
}
func (s OpentracingSpan) AddEvents(keys []string, values []EventValue) {
fields := []ol.Field{}
for i, v := range values {
if v.Str != "" {
field := ol.String(keys[i], v.Str)
fields = append(fields, field)
}
if v.Num != 0 {
field := ol.Int64(keys[i], v.Num)
fields = append(fields, field)
}
}
s.span.LogFields(fields...)
}
func splitTagSettings(input string) map[string]string {

View File

@ -42,7 +42,7 @@ func TestGroupSplit(t *testing.T) {
}
func TestInitJaegerCfg_Default(t *testing.T) {
ts := &TracingService{}
ts := &Opentracing{}
cfg, err := ts.initJaegerCfg()
require.NoError(t, err)
@ -50,7 +50,7 @@ func TestInitJaegerCfg_Default(t *testing.T) {
}
func TestInitJaegerCfg_Enabled(t *testing.T) {
ts := &TracingService{enabled: true}
ts := &Opentracing{enabled: true}
cfg, err := ts.initJaegerCfg()
require.NoError(t, err)
@ -66,7 +66,7 @@ func TestInitJaegerCfg_DisabledViaEnv(t *testing.T) {
require.NoError(t, err)
}()
ts := &TracingService{enabled: true}
ts := &Opentracing{enabled: true}
cfg, err := ts.initJaegerCfg()
require.NoError(t, err)
@ -81,7 +81,7 @@ func TestInitJaegerCfg_EnabledViaEnv(t *testing.T) {
require.NoError(t, err)
}()
ts := &TracingService{enabled: false}
ts := &Opentracing{enabled: false}
cfg, err := ts.initJaegerCfg()
require.NoError(t, err)
@ -96,7 +96,7 @@ func TestInitJaegerCfg_InvalidEnvVar(t *testing.T) {
require.NoError(t, err)
}()
ts := &TracingService{}
ts := &Opentracing{}
_, err = ts.initJaegerCfg()
require.EqualError(t, err, "cannot parse env var JAEGER_DISABLED=totallybogus: strconv.ParseBool: parsing \"totallybogus\": invalid syntax")
}
@ -108,7 +108,7 @@ func TestInitJaegerCfg_EnabledViaHost(t *testing.T) {
}()
cfg := setting.NewCfg()
ts := &TracingService{Cfg: cfg}
ts := &Opentracing{Cfg: cfg}
_, err := ts.Cfg.Raw.NewSection("tracing.jaeger")
require.NoError(t, err)
require.NoError(t, ts.parseSettings())
@ -128,7 +128,7 @@ func TestInitJaegerCfg_EnabledViaHostPort(t *testing.T) {
}()
cfg := setting.NewCfg()
ts := &TracingService{Cfg: cfg}
ts := &Opentracing{Cfg: cfg}
_, err := ts.Cfg.Raw.NewSection("tracing.jaeger")
require.NoError(t, err)
require.NoError(t, ts.parseSettings())

View File

@ -62,6 +62,7 @@ func TestValidateLoginAttempts(t *testing.T) {
withLoginAttempts(t, tc.loginAttempts)
query := &models.LoginUserQuery{Username: "user", Cfg: tc.cfg}
err := validateLoginAttempts(context.Background(), query)
require.Equal(t, tc.expected, err)
})

View File

@ -17,7 +17,6 @@ func TestMiddlewareAuth(t *testing.T) {
middlewareScenario(t, "ReqSignIn true and unauthenticated request", func(t *testing.T, sc *scenarioContext) {
sc.m.Get("/secure", reqSignIn, sc.defaultHandler)
sc.fakeReq("GET", "/secure").exec()
assert.Equal(t, 302, sc.resp.Code)

View File

@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
@ -731,7 +732,9 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService()
return contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore)
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
return contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore, tracer)
}
type fakeRenderService struct {

View File

@ -4,11 +4,16 @@ import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/web"
)
@ -36,7 +41,7 @@ func RouteOperationNameFromContext(ctx context.Context) (string, bool) {
return "", false
}
func RequestTracing() web.Handler {
func RequestTracing(tracer tracing.Tracer) web.Handler {
return func(res http.ResponseWriter, req *http.Request, c *web.Context) {
if strings.HasPrefix(c.Req.URL.Path, "/public/") ||
c.Req.URL.Path == "robots.txt" {
@ -46,11 +51,9 @@ func RequestTracing() web.Handler {
rw := res.(web.ResponseWriter)
tracer := opentracing.GlobalTracer()
wireContext, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
span := tracer.StartSpan(fmt.Sprintf("HTTP %s %s", req.Method, req.URL.Path), ext.RPCServerOption(wireContext))
wireContext := otel.GetTextMapPropagator().Extract(req.Context(), propagation.HeaderCarrier(req.Header))
ctx, span := tracer.Start(req.Context(), fmt.Sprintf("HTTP %s %s", req.Method, req.URL.Path), trace.WithLinks(trace.LinkFromContext(wireContext)))
ctx := opentracing.ContextWithSpan(req.Context(), span)
c.Req = req.WithContext(ctx)
c.Map(c.Req)
@ -59,17 +62,17 @@ func RequestTracing() web.Handler {
// Only call span.Finish when a route operation name have been set,
// meaning that not set the span would not be reported.
if routeOperation, exists := RouteOperationNameFromContext(c.Req.Context()); exists {
defer span.Finish()
span.SetOperationName(fmt.Sprintf("HTTP %s %s", req.Method, routeOperation))
defer span.End()
span.SetName(fmt.Sprintf("HTTP %s %s", req.Method, routeOperation))
}
status := rw.Status()
ext.HTTPStatusCode.Set(span, uint16(status))
ext.HTTPUrl.Set(span, req.RequestURI)
ext.HTTPMethod.Set(span, req.Method)
span.SetAttributes("http.status_code", status, attribute.Int("http.status_code", status))
span.SetAttributes("http.url", req.RequestURI, attribute.String("http.url", req.RequestURI))
span.SetAttributes("http.method", req.Method, attribute.String("http.method", req.Method))
if status >= 400 {
ext.Error.Set(span, true)
span.SetStatus(codes.Error, fmt.Sprintf("error with HTTP status code %s", strconv.Itoa(status)))
}
}
}

View File

@ -176,7 +176,6 @@ func applyScenario(t *testing.T, timeRange string, dataSourceJsonData *simplejso
},
verifier: verifier,
}
_, err = condition.Eval(ctx.result, reqHandler)
require.Nil(t, err)

View File

@ -7,13 +7,12 @@ import (
"time"
"github.com/benbjohnson/clock"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
tlog "github.com/opentracing/opentracing-go/log"
"go.opentelemetry.io/otel/attribute"
"golang.org/x/sync/errgroup"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/encryption"
@ -40,6 +39,7 @@ type AlertEngine struct {
log log.Logger
resultHandler resultHandler
usageStatsService usagestats.Service
tracer tracing.Tracer
}
// IsDisabled returns true if the alerting service is disabled for this instance.
@ -50,7 +50,7 @@ func (e *AlertEngine) IsDisabled() bool {
// ProvideAlertEngine returns a new AlertEngine.
func ProvideAlertEngine(renderer rendering.Service, bus bus.Bus, requestValidator models.PluginRequestValidator,
dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Internal,
cfg *setting.Cfg) *AlertEngine {
cfg *setting.Cfg, tracer tracing.Tracer) *AlertEngine {
e := &AlertEngine{
Cfg: cfg,
RenderService: renderer,
@ -58,6 +58,7 @@ func ProvideAlertEngine(renderer rendering.Service, bus bus.Bus, requestValidato
RequestValidator: requestValidator,
DataService: dataService,
usageStatsService: usageStatsService,
tracer: tracer,
}
e.ticker = NewTicker(time.Now(), time.Second*0, clock.New(), 1)
e.execQueue = make(chan *Job, 1000)
@ -177,9 +178,7 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan
alertCtx, cancelFn := context.WithTimeout(context.Background(), setting.AlertingEvaluationTimeout)
cancelChan <- cancelFn
span := opentracing.StartSpan("alert execution")
alertCtx = opentracing.ContextWithSpan(alertCtx, span)
alertCtx, span := e.tracer.Start(alertCtx, "alert execution")
evalContext := NewEvalContext(alertCtx, job.Rule, e.RequestValidator)
evalContext.Ctx = alertCtx
@ -187,32 +186,37 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan
defer func() {
if err := recover(); err != nil {
e.log.Error("Alert Panic", "error", err, "stack", log.Stack(1))
ext.Error.Set(span, true)
span.LogFields(
tlog.Error(fmt.Errorf("%v", err)),
tlog.String("message", "failed to execute alert rule. panic was recovered."),
)
span.Finish()
span.RecordError(fmt.Errorf("%v", err))
span.AddEvents(
[]string{"error", "message"},
[]tracing.EventValue{
{Str: fmt.Sprintf("%v", err)},
{Str: "failed to execute alert rule. panic was recovered."},
})
span.End()
close(attemptChan)
}
}()
e.evalHandler.Eval(evalContext)
span.SetTag("alertId", evalContext.Rule.ID)
span.SetTag("dashboardId", evalContext.Rule.DashboardID)
span.SetTag("firing", evalContext.Firing)
span.SetTag("nodatapoints", evalContext.NoDataFound)
span.SetTag("attemptID", attemptID)
span.SetAttributes("alertId", evalContext.Rule.ID, attribute.Key("alertId").Int64(evalContext.Rule.ID))
span.SetAttributes("dashboardId", evalContext.Rule.DashboardID, attribute.Key("dashboardId").Int64(evalContext.Rule.DashboardID))
span.SetAttributes("firing", evalContext.Firing, attribute.Key("firing").Bool(evalContext.Firing))
span.SetAttributes("nodatapoints", evalContext.NoDataFound, attribute.Key("nodatapoints").Bool(evalContext.NoDataFound))
span.SetAttributes("attemptID", attemptID, attribute.Key("attemptID").Int(attemptID))
if evalContext.Error != nil {
ext.Error.Set(span, true)
span.LogFields(
tlog.Error(evalContext.Error),
tlog.String("message", "alerting execution attempt failed"),
)
span.RecordError(evalContext.Error)
span.AddEvents(
[]string{"error", "message"},
[]tracing.EventValue{
{Str: fmt.Sprintf("%v", evalContext.Error)},
{Str: "alerting execution attempt failed"},
})
if attemptID < setting.AlertingMaxAttempts {
span.Finish()
span.End()
e.log.Debug("Job Execution attempt triggered retry", "timeMs", evalContext.GetDurationMs(), "alertId", evalContext.Rule.ID, "name", evalContext.Rule.Name, "firing", evalContext.Firing, "attemptID", attemptID)
attemptChan <- (attemptID + 1)
return
@ -240,7 +244,7 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan
}
}
span.Finish()
span.End()
e.log.Debug("Job Execution completed", "timeMs", evalContext.GetDurationMs(), "alertId", evalContext.Rule.ID, "name", evalContext.Rule.Name, "firing", evalContext.Firing, "attemptID", attemptID)
close(attemptChan)
}()

View File

@ -12,6 +12,7 @@ import (
"testing"
"time"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/services/encryption/ossencryption"
"github.com/grafana/grafana/pkg/setting"
@ -21,7 +22,9 @@ import (
func TestEngineTimeouts(t *testing.T) {
usMock := &usagestats.UsageStatsMock{T: t}
engine := ProvideAlertEngine(nil, nil, nil, nil, usMock, ossencryption.ProvideService(), setting.NewCfg())
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
engine := ProvideAlertEngine(nil, nil, nil, nil, usMock, ossencryption.ProvideService(), setting.NewCfg(), tracer)
setting.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3
engine.resultHandler = &FakeResultHandler{}

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/encryption/ossencryption"
@ -45,7 +46,9 @@ func (handler *FakeResultHandler) handle(evalContext *EvalContext) error {
func TestEngineProcessJob(t *testing.T) {
bus := bus.New()
usMock := &usagestats.UsageStatsMock{T: t}
engine := ProvideAlertEngine(nil, bus, nil, nil, usMock, ossencryption.ProvideService(), setting.NewCfg())
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
engine := ProvideAlertEngine(nil, bus, nil, nil, usMock, ossencryption.ProvideService(), setting.NewCfg(), tracer)
setting.AlertingEvaluationTimeout = 30 * time.Second
setting.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
@ -102,6 +103,8 @@ func getContextHandler(t *testing.T) *ContextHandler {
userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
return ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore)
return ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore, tracer)
}

View File

@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/network"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/middleware/cookies"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
@ -23,8 +24,6 @@ import (
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
"github.com/opentracing/opentracing-go"
ol "github.com/opentracing/opentracing-go/log"
cw "github.com/weaveworks/common/tracing"
)
@ -36,7 +35,8 @@ const (
const ServiceName = "ContextHandler"
func ProvideService(cfg *setting.Cfg, tokenService models.UserTokenService, jwtService models.JWTService,
remoteCache *remotecache.RemoteCache, renderService rendering.Service, sqlStore *sqlstore.SQLStore) *ContextHandler {
remoteCache *remotecache.RemoteCache, renderService rendering.Service, sqlStore *sqlstore.SQLStore,
tracer tracing.Tracer) *ContextHandler {
return &ContextHandler{
Cfg: cfg,
AuthTokenService: tokenService,
@ -44,6 +44,7 @@ func ProvideService(cfg *setting.Cfg, tokenService models.UserTokenService, jwtS
RemoteCache: remoteCache,
RenderService: renderService,
SQLStore: sqlStore,
tracer: tracer,
}
}
@ -55,7 +56,7 @@ type ContextHandler struct {
RemoteCache *remotecache.RemoteCache
RenderService rendering.Service
SQLStore *sqlstore.SQLStore
tracer tracing.Tracer
// GetTime returns the current time.
// Stubbable by tests.
GetTime func() time.Time
@ -73,8 +74,8 @@ func FromContext(c context.Context) *models.ReqContext {
// Middleware provides a middleware to initialize the Macaron context.
func (h *ContextHandler) Middleware(mContext *web.Context) {
span, _ := opentracing.StartSpanFromContext(mContext.Req.Context(), "Auth - Middleware")
defer span.Finish()
_, span := h.tracer.Start(mContext.Req.Context(), "Auth - Middleware")
defer span.End()
reqContext := &models.ReqContext{
Context: mContext,
@ -135,11 +136,13 @@ func (h *ContextHandler) Middleware(mContext *web.Context) {
}
reqContext.Logger = log.New("context", "userId", reqContext.UserId, "orgId", reqContext.OrgId, "uname", reqContext.Login)
span.LogFields(
ol.String("uname", reqContext.Login),
ol.Int64("orgId", reqContext.OrgId),
ol.Int64("userId", reqContext.UserId))
span.AddEvents(
[]string{"uname", "orgId", "userId"},
[]tracing.EventValue{
{Str: reqContext.Login},
{Num: reqContext.OrgId},
{Num: reqContext.UserId}},
)
mContext.Map(reqContext)
@ -157,8 +160,8 @@ func (h *ContextHandler) initContextWithAnonymousUser(reqContext *models.ReqCont
return false
}
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithAnonymousUser")
defer span.Finish()
_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAnonymousUser")
defer span.End()
org, err := h.SQLStore.GetOrgByName(h.Cfg.AnonymousOrgName)
if err != nil {
@ -192,8 +195,8 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
return false
}
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithAPIKey")
defer span.Finish()
_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAPIKey")
defer span.End()
// base64 decode key
decoded, err := apikeygen.Decode(keyString)
@ -272,8 +275,8 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
return false
}
span, ctx := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithBasicAuth")
defer span.Finish()
ctx, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
defer span.End()
username, password, err := util.DecodeBasicAuthHeader(header)
if err != nil {
@ -328,8 +331,8 @@ func (h *ContextHandler) initContextWithToken(reqContext *models.ReqContext, org
return false
}
span, ctx := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithToken")
defer span.Finish()
ctx, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithToken")
defer span.End()
token, err := h.AuthTokenService.LookupToken(ctx, rawToken)
if err != nil {
@ -369,8 +372,8 @@ func (h *ContextHandler) rotateEndOfRequestFunc(reqContext *models.ReqContext, a
return
}
span, ctx := opentracing.StartSpanFromContext(reqContext.Req.Context(), "rotateEndOfRequestFunc")
defer span.Finish()
ctx, span := h.tracer.Start(reqContext.Req.Context(), "rotateEndOfRequestFunc")
defer span.End()
addr := reqContext.RemoteAddr()
ip, err := network.GetIPFromAddress(addr)
@ -396,8 +399,8 @@ func (h *ContextHandler) initContextWithRenderAuth(reqContext *models.ReqContext
return false
}
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithRenderAuth")
defer span.Finish()
_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithRenderAuth")
defer span.End()
renderUser, exists := h.RenderService.GetRenderUser(reqContext.Req.Context(), key)
if !exists {
@ -466,8 +469,8 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
return false
}
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithAuthProxy")
defer span.Finish()
_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAuthProxy")
defer span.End()
// Check if allowed to continue with this IP
if err := auth.IsAllowedIP(); err != nil {

View File

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/datasources"
@ -21,7 +22,7 @@ import (
func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator models.PluginRequestValidator,
pluginStore plugins.Store, cfg *setting.Cfg, httpClientProvider httpclient.Provider,
oauthTokenService *oauthtoken.Service, dsService *datasources.Service) *DataSourceProxyService {
oauthTokenService *oauthtoken.Service, dsService *datasources.Service, tracer tracing.Tracer) *DataSourceProxyService {
return &DataSourceProxyService{
DataSourceCache: dataSourceCache,
PluginRequestValidator: plugReqValidator,
@ -30,6 +31,7 @@ func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator m
HTTPClientProvider: httpClientProvider,
OAuthTokenService: oauthTokenService,
DataSourcesService: dsService,
tracer: tracer,
}
}
@ -41,6 +43,7 @@ type DataSourceProxyService struct {
HTTPClientProvider httpclient.Provider
OAuthTokenService *oauthtoken.Service
DataSourcesService *datasources.Service
tracer tracing.Tracer
}
func (p *DataSourceProxyService) ProxyDataSourceRequest(c *models.ReqContext) {
@ -84,7 +87,7 @@ func (p *DataSourceProxyService) ProxyDatasourceRequestWithID(c *models.ReqConte
proxyPath := getProxyPath(c)
proxy, err := pluginproxy.NewDataSourceProxy(ds, plugin.Routes, c, proxyPath, p.Cfg, p.HTTPClientProvider,
p.OAuthTokenService, p.DataSourcesService)
p.OAuthTokenService, p.DataSourcesService, p.tracer)
if err != nil {
if errors.Is(err, datasource.URLValidationError{}) {
c.JsonApiErr(http.StatusBadRequest, fmt.Sprintf("Invalid data source URL: %q", ds.Url), err)

View File

@ -316,6 +316,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
Name: "User In DB",
Login: userInDbName,
}
_, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err)

View File

@ -1547,6 +1547,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
Name: "User In DB",
Login: userInDbName,
}
_, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err)

View File

@ -59,6 +59,7 @@ func TestPluginProvisioner(t *testing.T) {
}
reader := &testConfigReader{result: cfg}
ap := PluginProvisioner{log: log.New("test"), cfgProvider: reader}
err := ap.applyChanges(context.Background(), "")
require.NoError(t, err)
require.Len(t, sentCommands, 4)

View File

@ -15,6 +15,7 @@ func TestCheckOrgExists(t *testing.T) {
sqlstore.InitTestDB(t)
defaultOrg := models.CreateOrgCommand{Name: "Main Org."}
err := sqlstore.CreateOrg(context.Background(), &defaultOrg)
require.NoError(t, err)

View File

@ -11,11 +11,10 @@ import (
"github.com/gchaincl/sqlhooks"
"github.com/go-sql-driver/mysql"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/lib/pq"
"github.com/mattn/go-sqlite3"
"github.com/opentracing/opentracing-go"
ol "github.com/opentracing/opentracing-go/log"
"github.com/prometheus/client_golang/prometheus"
cw "github.com/weaveworks/common/tracing"
"xorm.io/core"
@ -39,7 +38,7 @@ func init() {
// WrapDatabaseDriverWithHooks creates a fake database driver that
// executes pre and post functions which we use to gather metrics about
// database queries. It also registers the metrics.
func WrapDatabaseDriverWithHooks(dbType string) string {
func WrapDatabaseDriverWithHooks(dbType string, tracer tracing.Tracer) string {
drivers := map[string]driver.Driver{
migrator.SQLite: &sqlite3.SQLiteDriver{},
migrator.MySQL: &mysql.MySQLDriver{},
@ -52,7 +51,7 @@ func WrapDatabaseDriverWithHooks(dbType string) string {
}
driverWithHooks := dbType + "WithHooks"
sql.Register(driverWithHooks, sqlhooks.Wrap(d, &databaseQueryWrapper{log: log.New("sqlstore.metrics")}))
sql.Register(driverWithHooks, sqlhooks.Wrap(d, &databaseQueryWrapper{log: log.New("sqlstore.metrics"), tracer: tracer}))
core.RegisterDriver(driverWithHooks, &databaseQueryWrapperDriver{dbType: dbType})
return driverWithHooks
}
@ -60,7 +59,8 @@ func WrapDatabaseDriverWithHooks(dbType string) string {
// databaseQueryWrapper satisfies the sqlhook.databaseQueryWrapper interface
// which allow us to wrap all SQL queries with a `Before` & `After` hook.
type databaseQueryWrapper struct {
log log.Logger
log log.Logger
tracer tracing.Tracer
}
// databaseQueryWrapperKey is used as key to save values in `context.Context`
@ -94,15 +94,13 @@ func (h *databaseQueryWrapper) instrument(ctx context.Context, status string, qu
histogram.Observe(elapsed.Seconds())
}
span, _ := opentracing.StartSpanFromContext(ctx, "database query")
defer span.Finish()
_, span := h.tracer.Start(ctx, "database query")
defer span.End()
span.LogFields(
ol.String("query", query),
ol.String("status", status))
span.AddEvents([]string{"query", "status"}, []tracing.EventValue{{Str: query}, {Str: status}})
if err != nil {
span.LogFields(ol.String("error", err.Error()))
span.AddEvents([]string{"error"}, []tracing.EventValue{{Str: err.Error()}})
}
h.log.Debug("query finished", "status", status, "elapsed time", elapsed, "sql", query, "error", err)

View File

@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/annotations"
@ -51,14 +52,16 @@ type SQLStore struct {
Dialect migrator.Dialect
skipEnsureDefaultOrgAndUser bool
migrations registry.DatabaseMigrator
tracer tracing.Tracer
}
func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, bus bus.Bus, migrations registry.DatabaseMigrator) (*SQLStore, error) {
func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, bus bus.Bus, migrations registry.DatabaseMigrator, tracer tracing.Tracer,
) (*SQLStore, error) {
// This change will make xorm use an empty default schema for postgres and
// by that mimic the functionality of how it was functioning before
// xorm's changes above.
xorm.DefaultPostgresSchema = ""
s, err := newSQLStore(cfg, cacheService, bus, nil, migrations)
s, err := newSQLStore(cfg, cacheService, bus, nil, migrations, tracer)
if err != nil {
return nil, err
}
@ -70,7 +73,7 @@ func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, bus
if err := s.Reset(); err != nil {
return nil, err
}
s.tracer = tracer
return s, nil
}
@ -79,7 +82,7 @@ func ProvideServiceForTests(migrations registry.DatabaseMigrator) (*SQLStore, er
}
func newSQLStore(cfg *setting.Cfg, cacheService *localcache.CacheService, bus bus.Bus, engine *xorm.Engine,
migrations registry.DatabaseMigrator, opts ...InitTestDBOpt) (*SQLStore, error) {
migrations registry.DatabaseMigrator, tracer tracing.Tracer, opts ...InitTestDBOpt) (*SQLStore, error) {
ss := &SQLStore{
Cfg: cfg,
Bus: bus,
@ -87,6 +90,7 @@ func newSQLStore(cfg *setting.Cfg, cacheService *localcache.CacheService, bus bu
log: log.New("sqlstore"),
skipEnsureDefaultOrgAndUser: false,
migrations: migrations,
tracer: tracer,
}
for _, opt := range opts {
if !opt.EnsureDefaultOrgAndUser {
@ -323,7 +327,7 @@ func (ss *SQLStore) initEngine(engine *xorm.Engine) error {
}
if ss.Cfg.IsDatabaseMetricsEnabled() {
ss.dbCfg.Type = WrapDatabaseDriverWithHooks(ss.dbCfg.Type)
ss.dbCfg.Type = WrapDatabaseDriverWithHooks(ss.dbCfg.Type, ss.tracer)
}
sqlog.Info("Connecting to DB", "dbtype", ss.dbCfg.Type)
@ -528,7 +532,11 @@ func initTestDB(migration registry.DatabaseMigrator, opts ...InitTestDBOpt) (*SQ
engine.DatabaseTZ = time.UTC
engine.TZLocation = time.UTC
testSQLStore, err = newSQLStore(cfg, localcache.New(5*time.Minute, 10*time.Minute), bus.GetBus(), engine, migration, opts...)
tracer, err := tracing.InitializeTracerForTest()
if err != nil {
return nil, err
}
testSQLStore, err = newSQLStore(cfg, localcache.New(5*time.Minute, 10*time.Minute), bus.GetBus(), engine, migration, tracer, opts...)
if err != nil {
return nil, err
}

View File

@ -4,6 +4,7 @@ import (
"context"
"testing"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/teamguardian/database"
"github.com/stretchr/testify/mock"
@ -32,9 +33,11 @@ func TestUpdateTeam(t *testing.T) {
t.Run("Given an editor and a team he isn't a member of", func(t *testing.T) {
t.Run("Should not be able to update the team", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
ctx := context.Background()
store.On("GetTeamMembers", ctx, mock.Anything).Return([]*models.TeamMemberDTO{}, nil).Once()
err := teamGuardianService.CanAdmin(ctx, testTeam.OrgId, testTeam.Id, &editor)
err = teamGuardianService.CanAdmin(ctx, testTeam.OrgId, testTeam.Id, &editor)
require.Equal(t, models.ErrNotAllowedToUpdateTeam, err)
})
})

View File

@ -10,6 +10,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
@ -21,6 +22,8 @@ import (
func TestAdminConfiguration_SendingToExternalAlertmanagers(t *testing.T) {
const disableOrgID int64 = 3
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,

View File

@ -9,6 +9,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/tests/testinfra"
@ -17,6 +18,9 @@ import (
)
func TestAlertmanagerConfigurationIsTransactional(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
@ -127,6 +131,9 @@ func TestAlertmanagerConfigurationIsTransactional(t *testing.T) {
}
func TestAlertmanagerConfigurationPersistSecrets(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,

View File

@ -13,6 +13,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/assert"
@ -28,6 +29,9 @@ import (
)
func TestAMConfigAccess(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
@ -314,7 +318,7 @@ func TestAMConfigAccess(t *testing.T) {
})
var silences apimodels.GettableSilences
err := json.Unmarshal(blob, &silences)
err = json.Unmarshal(blob, &silences)
require.NoError(t, err)
assert.Len(t, silences, 2)
silenceIDs := make([]string, 0, len(silences))
@ -387,6 +391,9 @@ func TestAMConfigAccess(t *testing.T) {
}
func TestAlertAndGroupsQuery(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
@ -552,6 +559,8 @@ func TestAlertAndGroupsQuery(t *testing.T) {
}
func TestRulerAccess(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -566,7 +575,7 @@ func TestRulerAccess(t *testing.T) {
store.Bus = bus.GetBus()
// Create the namespace we'll save our alerts to.
_, err := createFolder(t, store, 0, "default")
_, err = createFolder(t, store, 0, "default")
require.NoError(t, err)
// Create a users to make authenticated requests
@ -679,6 +688,8 @@ func TestRulerAccess(t *testing.T) {
}
func TestDeleteFolderWithRules(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -839,6 +850,8 @@ func TestDeleteFolderWithRules(t *testing.T) {
}
func TestAlertRuleCRUD(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -858,7 +871,7 @@ func TestAlertRuleCRUD(t *testing.T) {
})
// Create the namespace we'll save our alerts to.
_, err := createFolder(t, store, 0, "default")
_, err = createFolder(t, store, 0, "default")
require.NoError(t, err)
interval, err := model.ParseDuration("1m")
@ -1969,6 +1982,8 @@ func TestAlertmanagerStatus(t *testing.T) {
}
func TestQuota(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -1982,7 +1997,7 @@ func TestQuota(t *testing.T) {
store.Bus = bus.GetBus()
// Create the namespace we'll save our alerts to.
_, err := createFolder(t, store, 0, "default")
_, err = createFolder(t, store, 0, "default")
require.NoError(t, err)
// Create a user to make authenticated requests
@ -2212,6 +2227,8 @@ func TestQuota(t *testing.T) {
}
func TestEval(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -2231,7 +2248,7 @@ func TestEval(t *testing.T) {
})
// Create the namespace we'll save our alerts to.
_, err := createFolder(t, store, 0, "default")
_, err = createFolder(t, store, 0, "default")
require.NoError(t, err)
// test eval conditions

View File

@ -9,11 +9,15 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tests/testinfra"
)
func TestAvailableChannels(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,

View File

@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
@ -32,6 +33,8 @@ import (
func TestTestReceivers(t *testing.T) {
t.Run("assert no receivers returns 400 Bad Request", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -63,6 +66,8 @@ func TestTestReceivers(t *testing.T) {
})
t.Run("assert working receiver returns OK", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -149,6 +154,8 @@ func TestTestReceivers(t *testing.T) {
})
t.Run("assert invalid receiver returns 400 Bad Request", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -230,6 +237,8 @@ func TestTestReceivers(t *testing.T) {
})
t.Run("assert timed out receiver returns 408 Request Timeout", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -321,6 +330,8 @@ func TestTestReceivers(t *testing.T) {
})
t.Run("assert multiple different errors returns 207 Multi Status", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -438,6 +449,8 @@ func TestTestReceivers(t *testing.T) {
func TestTestReceiversAlertCustomization(t *testing.T) {
t.Run("assert custom annotations and labels are sent", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -535,6 +548,8 @@ func TestTestReceiversAlertCustomization(t *testing.T) {
})
t.Run("assert custom annotations can replace default annotations", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -627,6 +642,8 @@ func TestTestReceiversAlertCustomization(t *testing.T) {
})
t.Run("assert custom labels can replace default label", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -719,6 +736,9 @@ func TestTestReceiversAlertCustomization(t *testing.T) {
}
func TestNotificationChannels(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
@ -21,6 +22,9 @@ import (
)
func TestPrometheusRules(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
@ -32,7 +36,7 @@ func TestPrometheusRules(t *testing.T) {
store.Bus = bus.GetBus()
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
_, err := createFolder(t, store, 0, "default")
_, err = createFolder(t, store, 0, "default")
require.NoError(t, err)
// Create a user to make authenticated requests
@ -320,6 +324,8 @@ func TestPrometheusRules(t *testing.T) {
}
func TestPrometheusRulesFilterByDashboard(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
EnableFeatureToggles: []string{"ngalert"},
DisableAnonymous: true,
@ -615,6 +621,9 @@ func TestPrometheusRulesFilterByDashboard(t *testing.T) {
}
func TestPrometheusRulesPermissions(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
@ -633,7 +642,7 @@ func TestPrometheusRulesPermissions(t *testing.T) {
})
// Create a namespace under default organisation (orgID = 1) where we'll save some alerts.
_, err := createFolder(t, store, 0, "folder1")
_, err = createFolder(t, store, 0, "folder1")
require.NoError(t, err)
// Create another namespace under default organisation (orgID = 1) where we'll save some alerts.

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
@ -22,6 +23,9 @@ import (
func TestAlertRulePermissions(t *testing.T) {
// Setup Grafana and its Database
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
@ -40,7 +44,7 @@ func TestAlertRulePermissions(t *testing.T) {
})
// Create the namespace we'll save our alerts to.
_, err := createFolder(t, store, 0, "folder1")
_, err = createFolder(t, store, 0, "folder1")
require.NoError(t, err)
_, err = createFolder(t, store, 0, "folder2")
@ -324,6 +328,8 @@ func createRule(t *testing.T, grafanaListedAddr string, folder string, user, pas
}
func TestAlertRuleConflictingTitle(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
@ -338,7 +344,7 @@ func TestAlertRuleConflictingTitle(t *testing.T) {
store.Bus = bus.GetBus()
// Create the namespace we'll save our alerts to.
_, err := createFolder(t, store, 0, "folder1")
_, err = createFolder(t, store, 0, "folder1")
require.NoError(t, err)
_, err = createFolder(t, store, 0, "folder2")
require.NoError(t, err)
@ -448,6 +454,9 @@ func TestAlertRuleConflictingTitle(t *testing.T) {
}
func TestRulerRulesFilterByDashboard(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
EnableFeatureToggles: []string{"ngalert"},
DisableAnonymous: true,

View File

@ -32,6 +32,7 @@ func TestDashboardQuota(t *testing.T) {
EnableQuota: true,
DashboardOrgQuota: &dashboardQuota,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
// Create user
createUser(t, store, models.CreateUserCommand{

View File

@ -30,6 +30,7 @@ import (
func TestQueryCloudWatchMetrics(t *testing.T) {
grafDir, cfgPath := testinfra.CreateGrafDir(t)
addr, sqlStore := testinfra.StartGrafana(t, grafDir, cfgPath)
setUpDatabase(t, sqlStore)

View File

@ -18,6 +18,7 @@ func TestIndexView(t *testing.T) {
grafDir, cfgPath := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
EnableCSP: true,
})
addr, _ := testinfra.StartGrafana(t, grafDir, cfgPath)
// nolint:bodyclose

View File

@ -14,8 +14,9 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp"
)
@ -48,7 +49,8 @@ func (e *ApplicationInsightsDatasource) resourceRequest(rw http.ResponseWriter,
}
func (e *ApplicationInsightsDatasource) executeTimeSeriesQuery(ctx context.Context,
originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error) {
originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) {
result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries)
@ -57,7 +59,7 @@ func (e *ApplicationInsightsDatasource) executeTimeSeriesQuery(ctx context.Conte
}
for _, query := range queries {
queryRes, err := e.executeQuery(ctx, query, dsInfo, client, url)
queryRes, err := e.executeQuery(ctx, query, dsInfo, client, url, tracer)
if err != nil {
return nil, err
}
@ -128,7 +130,7 @@ func (e *ApplicationInsightsDatasource) buildQueries(queries []backend.DataQuery
return applicationInsightsQueries, nil
}
func (e *ApplicationInsightsDatasource) executeQuery(ctx context.Context, query *ApplicationInsightsQuery, dsInfo datasourceInfo, client *http.Client, url string) (
func (e *ApplicationInsightsDatasource) executeQuery(ctx context.Context, query *ApplicationInsightsQuery, dsInfo datasourceInfo, client *http.Client, url string, tracer tracing.Tracer) (
backend.DataResponse, error) {
dataResponse := backend.DataResponse{}
@ -141,23 +143,16 @@ func (e *ApplicationInsightsDatasource) executeQuery(ctx context.Context, query
req.URL.Path = path.Join(req.URL.Path, query.ApiURL)
req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "application insights query")
span.SetTag("target", query.Target)
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond))
span.SetTag("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond))
span.SetTag("datasource_id", dsInfo.DatasourceID)
span.SetTag("org_id", dsInfo.OrgID)
ctx, span := tracer.Start(ctx, "application insights query")
span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond), attribute.Key("until").Int64(query.TimeRange.To.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish()
defer span.End()
err = opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
if err != nil {
azlog.Warn("failed to inject global tracer")
}
tracer.Inject(ctx, req.Header, span)
azlog.Debug("ApplicationInsights", "Request URL", req.URL.String())
res, err := ctxhttp.Do(ctx, client, req)

View File

@ -16,8 +16,9 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp"
)
@ -46,7 +47,8 @@ func (e *AzureLogAnalyticsDatasource) resourceRequest(rw http.ResponseWriter, re
// 1. build the AzureMonitor url and querystring for each query
// 2. executes each query by calling the Azure Monitor API
// 3. parses the responses for each query into data frames
func (e *AzureLogAnalyticsDatasource) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error) {
func (e *AzureLogAnalyticsDatasource) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) {
result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries, dsInfo)
@ -55,7 +57,7 @@ func (e *AzureLogAnalyticsDatasource) executeTimeSeriesQuery(ctx context.Context
}
for _, query := range queries {
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url)
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url, tracer)
}
return result, nil
@ -125,7 +127,8 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(queries []backend.DataQuery,
return azureLogAnalyticsQueries, nil
}
func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *AzureLogAnalyticsQuery, dsInfo datasourceInfo, client *http.Client, url string) backend.DataResponse {
func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *AzureLogAnalyticsQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) backend.DataResponse {
dataResponse := backend.DataResponse{}
dataResponseErrorWithExecuted := func(err error) backend.DataResponse {
@ -155,21 +158,16 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A
req.URL.Path = path.Join(req.URL.Path, query.URL)
req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "azure log analytics query")
span.SetTag("target", query.Target)
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond))
span.SetTag("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond))
span.SetTag("datasource_id", dsInfo.DatasourceID)
span.SetTag("org_id", dsInfo.OrgID)
ctx, span := tracer.Start(ctx, "azure log analytics query")
span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond), attribute.Key("until").Int64(query.TimeRange.To.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish()
defer span.End()
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
return dataResponseErrorWithExecuted(err)
}
tracer.Inject(ctx, req.Header, span)
azlog.Debug("AzureLogAnalytics", "Request ApiURL", req.URL.String())
res, err := ctxhttp.Do(ctx, client, req)

View File

@ -11,6 +11,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/stretchr/testify/require"
)
@ -228,7 +229,9 @@ func Test_executeQueryErrorWithDifferentLogAnalyticsCreds(t *testing.T) {
Params: url.Values{},
TimeRange: backend.TimeRange{},
}
res := ds.executeQuery(ctx, query, dsInfo, &http.Client{}, dsInfo.Services[azureLogAnalytics].URL)
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
res := ds.executeQuery(ctx, query, dsInfo, &http.Client{}, dsInfo.Services[azureLogAnalytics].URL, tracer)
if res.Error == nil {
t.Fatal("expecting an error")
}

View File

@ -15,9 +15,10 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp"
)
@ -48,7 +49,8 @@ func (e *AzureResourceGraphDatasource) resourceRequest(rw http.ResponseWriter, r
// 1. builds the AzureMonitor url and querystring for each query
// 2. executes each query by calling the Azure Monitor API
// 3. parses the responses for each query into data frames
func (e *AzureResourceGraphDatasource) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error) {
func (e *AzureResourceGraphDatasource) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) {
result := &backend.QueryDataResponse{
Responses: map[string]backend.DataResponse{},
}
@ -59,7 +61,7 @@ func (e *AzureResourceGraphDatasource) executeTimeSeriesQuery(ctx context.Contex
}
for _, query := range queries {
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url)
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url, tracer)
}
return result, nil
@ -101,7 +103,8 @@ func (e *AzureResourceGraphDatasource) buildQueries(queries []backend.DataQuery,
return azureResourceGraphQueries, nil
}
func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query *AzureResourceGraphQuery, dsInfo datasourceInfo, client *http.Client, dsURL string) backend.DataResponse {
func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query *AzureResourceGraphQuery, dsInfo datasourceInfo, client *http.Client,
dsURL string, tracer tracing.Tracer) backend.DataResponse {
dataResponse := backend.DataResponse{}
params := url.Values{}
@ -148,21 +151,16 @@ func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query *
req.URL.Path = path.Join(req.URL.Path, argQueryProviderName)
req.URL.RawQuery = params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "azure resource graph query")
span.SetTag("interpolated_query", query.InterpolatedQuery)
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond))
span.SetTag("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond))
span.SetTag("datasource_id", dsInfo.DatasourceID)
span.SetTag("org_id", dsInfo.OrgID)
ctx, span := tracer.Start(ctx, "azure resource graph query")
span.SetAttributes("interpolated_query", query.InterpolatedQuery, attribute.Key("interpolated_query").String(query.InterpolatedQuery))
span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond), attribute.Key("until").Int64(query.TimeRange.To.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish()
defer span.End()
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
return dataResponseErrorWithExecuted(err)
}
tracer.Inject(ctx, req.Header, span)
azlog.Debug("AzureResourceGraph", "Request ApiURL", req.URL.String())
res, err := ctxhttp.Do(ctx, client, req)

View File

@ -14,9 +14,10 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
opentracing "github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp"
)
@ -43,7 +44,8 @@ func (e *AzureMonitorDatasource) resourceRequest(rw http.ResponseWriter, req *ht
// 1. build the AzureMonitor url and querystring for each query
// 2. executes each query by calling the Azure Monitor API
// 3. parses the responses for each query into data frames
func (e *AzureMonitorDatasource) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error) {
func (e *AzureMonitorDatasource) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) {
result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries, dsInfo)
@ -52,7 +54,7 @@ func (e *AzureMonitorDatasource) executeTimeSeriesQuery(ctx context.Context, ori
}
for _, query := range queries {
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url)
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url, tracer)
}
return result, nil
@ -147,7 +149,8 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
return azureMonitorQueries, nil
}
func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureMonitorQuery, dsInfo datasourceInfo, cli *http.Client, url string) backend.DataResponse {
func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureMonitorQuery, dsInfo datasourceInfo, cli *http.Client,
url string, tracer tracing.Tracer) backend.DataResponse {
dataResponse := backend.DataResponse{}
req, err := e.createRequest(ctx, dsInfo, url)
@ -159,22 +162,15 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureM
req.URL.Path = path.Join(req.URL.Path, query.URL)
req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "azuremonitor query")
span.SetTag("target", query.Target)
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond))
span.SetTag("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond))
span.SetTag("datasource_id", dsInfo.DatasourceID)
span.SetTag("org_id", dsInfo.OrgID)
ctx, span := tracer.Start(ctx, "azuremonitor query")
span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond), attribute.Key("until").Int64(query.TimeRange.To.UnixNano()/int64(time.Millisecond)))
span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish()
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
dataResponse.Error = err
return dataResponse
}
defer span.End()
tracer.Inject(ctx, req.Header, span)
azlog.Debug("AzureMonitor", "Request ApiURL", req.URL.String())
azlog.Debug("AzureMonitor", "Target", query.Target)

View File

@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/setting"
@ -30,7 +31,7 @@ var (
legendKeyFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
)
func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, pluginStore plugins.Store) *Service {
func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) *Service {
proxy := &httpServiceProxy{}
executors := map[string]azDatasourceExecutor{
azureMonitor: &AzureMonitorDatasource{proxy: proxy},
@ -45,6 +46,7 @@ func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, p
Cfg: cfg,
im: im,
executors: executors,
tracer: tracer,
}
mux := s.newMux()
@ -71,6 +73,7 @@ type Service struct {
Cfg *setting.Cfg
im instancemgmt.InstanceManager
executors map[string]azDatasourceExecutor
tracer tracing.Tracer
}
type azureMonitorSettings struct {
@ -162,7 +165,7 @@ func NewInstanceSettings(cfg *setting.Cfg, clientProvider httpclient.Provider, e
}
type azDatasourceExecutor interface {
executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error)
executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error)
resourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client)
}
@ -194,7 +197,7 @@ func (s *Service) newMux() *datasource.QueryTypeMux {
if !ok {
return nil, fmt.Errorf("missing service for %s", dst)
}
return executor.executeTimeSeriesQuery(ctx, req.Queries, dsInfo, service.HTTPClient, service.URL)
return executor.executeTimeSeriesQuery(ctx, req.Queries, dsInfo, service.HTTPClient, service.URL, s.tracer)
})
}
return mux

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials"
"github.com/stretchr/testify/require"
@ -85,7 +86,8 @@ type fakeExecutor struct {
func (f *fakeExecutor) resourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client) {
}
func (f *fakeExecutor) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error) {
func (f *fakeExecutor) executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) {
if client == nil {
f.t.Errorf("The HTTP client for %s is missing", f.queryType)
} else {

View File

@ -12,8 +12,9 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp"
)
@ -38,7 +39,8 @@ func (e *InsightsAnalyticsDatasource) resourceRequest(rw http.ResponseWriter, re
}
func (e *InsightsAnalyticsDatasource) executeTimeSeriesQuery(ctx context.Context,
originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error) {
originalQueries []backend.DataQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) {
result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries, dsInfo)
@ -47,7 +49,7 @@ func (e *InsightsAnalyticsDatasource) executeTimeSeriesQuery(ctx context.Context
}
for _, query := range queries {
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url)
result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url, tracer)
}
return result, nil
@ -86,7 +88,8 @@ func (e *InsightsAnalyticsDatasource) buildQueries(queries []backend.DataQuery,
return iaQueries, nil
}
func (e *InsightsAnalyticsDatasource) executeQuery(ctx context.Context, query *InsightsAnalyticsQuery, dsInfo datasourceInfo, client *http.Client, url string) backend.DataResponse {
func (e *InsightsAnalyticsDatasource) executeQuery(ctx context.Context, query *InsightsAnalyticsQuery, dsInfo datasourceInfo, client *http.Client,
url string, tracer tracing.Tracer) backend.DataResponse {
dataResponse := backend.DataResponse{}
dataResponseError := func(err error) backend.DataResponse {
@ -101,17 +104,13 @@ func (e *InsightsAnalyticsDatasource) executeQuery(ctx context.Context, query *I
req.URL.Path = path.Join(req.URL.Path, "query")
req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "application insights analytics query")
span.SetTag("target", query.Target)
span.SetTag("datasource_id", dsInfo.DatasourceID)
span.SetTag("org_id", dsInfo.OrgID)
ctx, span := tracer.Start(ctx, "application insights analytics query")
span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish()
err = opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
defer span.End()
tracer.Inject(ctx, req.Header, span)
if err != nil {
azlog.Warn("failed to inject global tracer")

View File

@ -18,7 +18,7 @@ func (s *Service) executeAnnotationQuery(ctx context.Context, req *backend.Query
return resp, err
}
queryRes, dr, _, err := queries[0].run(ctx, req, s, dsInfo)
queryRes, dr, _, err := queries[0].run(ctx, req, s, dsInfo, s.tracer)
if err != nil {
return resp, err
}

View File

@ -24,6 +24,7 @@ import (
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/services/datasources"
@ -72,12 +73,13 @@ const (
)
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store,
dsService *datasources.Service) *Service {
dsService *datasources.Service, tracer tracing.Tracer) *Service {
s := &Service{
httpClientProvider: httpClientProvider,
cfg: cfg,
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
dsService: dsService,
tracer: tracer,
}
mux := http.NewServeMux()
@ -139,6 +141,7 @@ type Service struct {
cfg *setting.Cfg
im instancemgmt.InstanceManager
dsService *datasources.Service
tracer tracing.Tracer
}
type QueryModel struct {
@ -263,7 +266,7 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query
}
for _, queryExecutor := range queryExecutors {
queryRes, dr, executedQueryString, err := queryExecutor.run(ctx, req, s, dsInfo)
queryRes, dr, executedQueryString, err := queryExecutor.run(ctx, req, s, dsInfo, s.tracer)
if err != nil {
return resp, err
}

View File

@ -12,11 +12,12 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/opentracing/opentracing-go"
"github.com/grafana/grafana/pkg/infra/tracing"
"go.opentelemetry.io/otel/attribute"
)
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) run(ctx context.Context, req *backend.QueryDataRequest,
s *Service, dsInfo datasourceInfo) (*backend.DataResponse, cloudMonitoringResponse, string, error) {
s *Service, dsInfo datasourceInfo, tracer tracing.Tracer) (*backend.DataResponse, cloudMonitoringResponse, string, error) {
dr := &backend.DataResponse{}
projectName := timeSeriesFilter.ProjectName
if projectName == "" {
@ -55,22 +56,15 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) run(ctx context.Context
}
}
span, ctx := opentracing.StartSpanFromContext(ctx, "cloudMonitoring query")
span.SetTag("target", timeSeriesFilter.Target)
span.SetTag("from", req.Queries[0].TimeRange.From)
span.SetTag("until", req.Queries[0].TimeRange.To)
span.SetTag("datasource_id", dsInfo.id)
span.SetTag("org_id", req.PluginContext.OrgID)
ctx, span := tracer.Start(ctx, "cloudMonitoring query")
span.SetAttributes("target", timeSeriesFilter.Target, attribute.Key("target").String(timeSeriesFilter.Target))
span.SetAttributes("from", req.Queries[0].TimeRange.From, attribute.Key("from").String(req.Queries[0].TimeRange.From.String()))
span.SetAttributes("until", req.Queries[0].TimeRange.To, attribute.Key("until").String(req.Queries[0].TimeRange.To.String()))
span.SetAttributes("datasource_id", dsInfo.id, attribute.Key("datasource_id").Int64(dsInfo.id))
span.SetAttributes("org_id", req.PluginContext.OrgID, attribute.Key("org_id").Int64(req.PluginContext.OrgID))
defer span.Finish()
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header)); err != nil {
dr.Error = err
return dr, cloudMonitoringResponse{}, "", nil
}
defer span.End()
tracer.Inject(ctx, r.Header, span)
r = r.WithContext(ctx)
res, err := dsInfo.services[cloudMonitor].client.Do(r)

View File

@ -11,16 +11,17 @@ import (
"strings"
"time"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/attribute"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
)
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, req *backend.QueryDataRequest,
s *Service, dsInfo datasourceInfo) (*backend.DataResponse, cloudMonitoringResponse, string, error) {
s *Service, dsInfo datasourceInfo, tracer tracing.Tracer) (*backend.DataResponse, cloudMonitoringResponse, string, error) {
dr := &backend.DataResponse{}
projectName := timeSeriesQuery.ProjectName
@ -55,20 +56,13 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, r
return dr, cloudMonitoringResponse{}, "", nil
}
span, ctx := opentracing.StartSpanFromContext(ctx, "cloudMonitoring MQL query")
span.SetTag("query", timeSeriesQuery.Query)
span.SetTag("from", req.Queries[0].TimeRange.From)
span.SetTag("until", req.Queries[0].TimeRange.To)
ctx, span := tracer.Start(ctx, "cloudMonitoring MQL query")
span.SetAttributes("query", timeSeriesQuery.Query, attribute.Key("query").String(timeSeriesQuery.Query))
span.SetAttributes("from", req.Queries[0].TimeRange.From, attribute.Key("from").String(req.Queries[0].TimeRange.From.String()))
span.SetAttributes("until", req.Queries[0].TimeRange.To, attribute.Key("until").String(req.Queries[0].TimeRange.To.String()))
defer span.Finish()
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header)); err != nil {
dr.Error = err
return dr, cloudMonitoringResponse{}, "", nil
}
defer span.End()
tracer.Inject(ctx, r.Header, span)
r = r.WithContext(ctx)
res, err := dsInfo.services[cloudMonitor].client.Do(r)

View File

@ -6,11 +6,12 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/infra/tracing"
)
type (
cloudMonitoringQueryExecutor interface {
run(ctx context.Context, req *backend.QueryDataRequest, s *Service, dsInfo datasourceInfo) (
run(ctx context.Context, req *backend.QueryDataRequest, s *Service, dsInfo datasourceInfo, tracer tracing.Tracer) (
*backend.DataResponse, cloudMonitoringResponse, string, error)
parseResponse(dr *backend.DataResponse, data cloudMonitoringResponse, executedQueryString string) error
parseToAnnotations(dr *backend.DataResponse, data cloudMonitoringResponse, title, text string) error

View File

@ -14,17 +14,18 @@ import (
"strings"
"time"
"go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/opentracing/opentracing-go"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/setting"
@ -34,6 +35,7 @@ import (
type Service struct {
logger log.Logger
im instancemgmt.InstanceManager
tracer tracing.Tracer
}
const (
@ -42,10 +44,11 @@ const (
TargetModelField = "target"
)
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store) (*Service, error) {
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) (*Service, error) {
s := &Service{
logger: log.New("tsdb.graphite"),
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
tracer: tracer,
}
factory := coreplugin.New(backend.ServeOpts{
@ -165,21 +168,15 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
return &result, err
}
span, ctx := opentracing.StartSpanFromContext(ctx, "graphite query")
span.SetTag("target", target)
span.SetTag("from", from)
span.SetTag("until", until)
span.SetTag("datasource_id", dsInfo.Id)
span.SetTag("org_id", req.PluginContext.OrgID)
ctx, span := s.tracer.Start(ctx, "graphite query")
span.SetAttributes("target", target, attribute.Key("target").String(target))
span.SetAttributes("from", from, attribute.Key("from").String(from))
span.SetAttributes("until", until, attribute.Key("until").String(until))
span.SetAttributes("datasource_id", dsInfo.Id, attribute.Key("datasource_id").Int64(dsInfo.Id))
span.SetAttributes("org_id", req.PluginContext.OrgID, attribute.Key("org_id").Int64(req.PluginContext.OrgID))
defer span.Finish()
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(graphiteReq.Header)); err != nil {
return &result, err
}
defer span.End()
s.tracer.Inject(ctx, graphiteReq.Header, span)
res, err := ctxhttp.Do(ctx, dsInfo.HTTPClient, graphiteReq)
if err != nil {

View File

@ -16,14 +16,15 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/loki/pkg/logcli/client"
"github.com/grafana/loki/pkg/loghttp"
"github.com/grafana/loki/pkg/logproto"
"go.opentelemetry.io/otel/attribute"
"github.com/opentracing/opentracing-go"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
)
@ -31,15 +32,17 @@ import (
const pluginID = "loki"
type Service struct {
im instancemgmt.InstanceManager
plog log.Logger
im instancemgmt.InstanceManager
plog log.Logger
tracer tracing.Tracer
}
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store) (*Service, error) {
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) (*Service, error) {
im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider))
s := &Service{
im: im,
plog: log.New("tsdb.loki"),
im: im,
plog: log.New("tsdb.loki"),
tracer: tracer,
}
factory := coreplugin.New(backend.ServeOpts{
@ -140,11 +143,11 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
for _, query := range queries {
s.plog.Debug("Sending query", "start", query.Start, "end", query.End, "step", query.Step, "query", query.Expr)
span, _ := opentracing.StartSpanFromContext(ctx, "alerting.loki")
span.SetTag("expr", query.Expr)
span.SetTag("start_unixnano", query.Start.UnixNano())
span.SetTag("stop_unixnano", query.End.UnixNano())
defer span.Finish()
_, span := s.tracer.Start(ctx, "alerting.loki")
span.SetAttributes("expr", query.Expr, attribute.Key("expr").String(query.Expr))
span.SetAttributes("start_unixnano", query.Start, attribute.Key("start_unixnano").Int64(query.Start.UnixNano()))
span.SetAttributes("stop_unixnano", query.End, attribute.Key("stop_unixnano").Int64(query.End.UnixNano()))
defer span.End()
// `limit` only applies to log-producing queries, and we
// currently only support metric queries, so this can be set to any value.

View File

@ -12,6 +12,7 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/experimental"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/prometheus/client_golang/api"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/stretchr/testify/require"
@ -109,7 +110,10 @@ func testScenario(t *testing.T, name string) {
api, err := makeMockedApi(responseBytes)
require.NoError(t, err)
result, err := runQueries(context.Background(), api, []*PrometheusQuery{&query})
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
s := Service{tracer: tracer}
result, err := s.runQueries(context.Background(), api, []*PrometheusQuery{&query})
require.NoError(t, err)
require.Len(t, result.Responses, 1)

View File

@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/setting"
@ -32,15 +33,17 @@ const pluginID = "prometheus"
type Service struct {
intervalCalculator intervalv2.Calculator
im instancemgmt.InstanceManager
tracer tracing.Tracer
}
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store) (*Service, error) {
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) (*Service, error) {
plog.Debug("initializing")
im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider))
s := &Service{
intervalCalculator: intervalv2.NewCalculator(),
im: im,
tracer: tracer,
}
factory := coreplugin.New(backend.ServeOpts{

View File

@ -13,9 +13,9 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
"github.com/opentracing/opentracing-go"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/common/model"
"go.opentelemetry.io/otel/attribute"
)
//Internal interval and range variables
@ -47,7 +47,7 @@ const (
ExemplarQueryType TimeSeriesQueryType = "exemplar"
)
func runQueries(ctx context.Context, client apiv1.API, queries []*PrometheusQuery) (*backend.QueryDataResponse, error) {
func (s *Service) runQueries(ctx context.Context, client apiv1.API, queries []*PrometheusQuery) (*backend.QueryDataResponse, error) {
result := backend.QueryDataResponse{
Responses: backend.Responses{},
}
@ -55,11 +55,11 @@ func runQueries(ctx context.Context, client apiv1.API, queries []*PrometheusQuer
for _, query := range queries {
plog.Debug("Sending query", "start", query.Start, "end", query.End, "step", query.Step, "query", query.Expr)
span, ctx := opentracing.StartSpanFromContext(ctx, "datasource.prometheus")
span.SetTag("expr", query.Expr)
span.SetTag("start_unixnano", query.Start.UnixNano())
span.SetTag("stop_unixnano", query.End.UnixNano())
defer span.Finish()
ctx, span := s.tracer.Start(ctx, "datasource.prometheus")
span.SetAttributes("expr", query.Expr, attribute.Key("expr").String(query.Expr))
span.SetAttributes("start_unixnano", query.Start, attribute.Key("start_unixnano").Int64(query.Start.UnixNano()))
span.SetAttributes("stop_unixnano", query.End, attribute.Key("stop_unixnano").Int64(query.End.UnixNano()))
defer span.End()
response := make(map[TimeSeriesQueryType]interface{})
@ -128,7 +128,7 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query
return &result, err
}
return runQueries(ctx, client, queries)
return s.runQueries(ctx, client, queries)
}
func formatLegend(metric model.Metric, query *PrometheusQuery) string {