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
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/fs"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache" "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/infra/usagestats"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
@@ -64,7 +65,6 @@ func loggedInUserScenarioWithRole(t *testing.T, desc string, method string, url
case "DELETE": case "DELETE":
sc.m.Delete(routePattern, sc.defaultHandler) sc.m.Delete(routePattern, sc.defaultHandler)
} }
fn(sc) fn(sc)
}) })
} }
@@ -177,7 +177,9 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
userAuthTokenSvc := auth.NewFakeUserAuthTokenService() userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{} renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService() 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 return ctxHdlr
} }

View File

@@ -111,7 +111,7 @@ type HTTPServer struct {
SecretsService secrets.Service SecretsService secrets.Service
DataSourcesService *datasources.Service DataSourcesService *datasources.Service
cleanUpService *cleanup.CleanUpService cleanUpService *cleanup.CleanUpService
tracingService tracing.Tracer tracer tracing.Tracer
updateChecker *updatechecker.Service updateChecker *updatechecker.Service
searchUsersService searchusers.Service searchUsersService searchusers.Service
teamGuardian teamguardian.TeamGuardian teamGuardian teamguardian.TeamGuardian
@@ -138,7 +138,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
contextHandler *contexthandler.ContextHandler, contextHandler *contexthandler.ContextHandler,
schemaService *schemaloader.SchemaLoaderService, alertNG *ngalert.AlertNG, schemaService *schemaloader.SchemaLoaderService, alertNG *ngalert.AlertNG,
libraryPanelService librarypanels.Service, libraryElementService libraryelements.Service, 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, encryptionService encryption.Internal, updateChecker *updatechecker.Service, searchUsersService searchusers.Service,
dataSourcesService *datasources.Service, secretsService secrets.Service, queryDataService *query.Service, dataSourcesService *datasources.Service, secretsService secrets.Service, queryDataService *query.Service,
teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service) (*HTTPServer, error) { teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service) (*HTTPServer, error) {
@@ -183,7 +183,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
LibraryPanelService: libraryPanelService, LibraryPanelService: libraryPanelService,
LibraryElementService: libraryElementService, LibraryElementService: libraryElementService,
QuotaService: quotaService, QuotaService: quotaService,
tracingService: tracingService, tracer: tracer,
log: log.New("http.server"), log: log.New("http.server"),
web: m, web: m,
Listener: opts.Listener, Listener: opts.Listener,
@@ -407,7 +407,7 @@ func (hs *HTTPServer) applyRoutes() {
func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() { func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m := hs.web m := hs.web
m.Use(middleware.RequestTracing()) m.Use(middleware.RequestTracing(hs.tracer))
m.Use(middleware.Logger(hs.Cfg)) 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/api/datasource"
"github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/httpclient"
glog "github.com/grafana/grafana/pkg/infra/log" 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/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
@@ -23,7 +24,7 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/proxyutil" "github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/opentracing/opentracing-go" "go.opentelemetry.io/otel/attribute"
) )
var ( var (
@@ -42,6 +43,7 @@ type DataSourceProxy struct {
clientProvider httpclient.Provider clientProvider httpclient.Provider
oAuthTokenService oauthtoken.OAuthTokenService oAuthTokenService oauthtoken.OAuthTokenService
dataSourcesService *datasources.Service dataSourcesService *datasources.Service
tracer tracing.Tracer
} }
type handleResponseTransport struct { type handleResponseTransport struct {
@@ -75,7 +77,8 @@ func (lw *logWrapper) Write(p []byte) (n int, err error) {
// NewDataSourceProxy creates a new Datasource proxy // NewDataSourceProxy creates a new Datasource proxy
func NewDataSourceProxy(ds *models.DataSource, pluginRoutes []*plugins.Route, ctx *models.ReqContext, func NewDataSourceProxy(ds *models.DataSource, pluginRoutes []*plugins.Route, ctx *models.ReqContext,
proxyPath string, cfg *setting.Cfg, clientProvider httpclient.Provider, 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) targetURL, err := datasource.ValidateURL(ds.Type, ds.Url)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -91,6 +94,7 @@ func NewDataSourceProxy(ds *models.DataSource, pluginRoutes []*plugins.Route, ct
clientProvider: clientProvider, clientProvider: clientProvider,
oAuthTokenService: oAuthTokenService, oAuthTokenService: oAuthTokenService,
dataSourcesService: dsService, dataSourcesService: dsService,
tracer: tracer,
}, nil }, nil
} }
@@ -147,35 +151,29 @@ func (proxy *DataSourceProxy) HandleRequest() {
} }
proxy.logRequest() proxy.logRequest()
ctx, span := proxy.tracer.Start(proxy.ctx.Req.Context(), "datasource reverse proxy")
span, ctx := opentracing.StartSpanFromContext(proxy.ctx.Req.Context(), "datasource reverse proxy") defer span.End()
defer span.Finish()
proxy.ctx.Req = proxy.ctx.Req.WithContext(ctx) proxy.ctx.Req = proxy.ctx.Req.WithContext(ctx)
span.SetTag("datasource_name", proxy.ds.Name) span.SetAttributes("datasource_name", proxy.ds.Name, attribute.Key("datasource_name").String(proxy.ds.Name))
span.SetTag("datasource_type", proxy.ds.Type) span.SetAttributes("datasource_type", proxy.ds.Type, attribute.Key("datasource_type").String(proxy.ds.Type))
span.SetTag("user", proxy.ctx.SignedInUser.Login) span.SetAttributes("user", proxy.ds.Type, attribute.Key("user").String(proxy.ds.Type))
span.SetTag("org_id", proxy.ctx.SignedInUser.OrgId) 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-Panel-Id", "panel_id")
proxy.addTraceFromHeaderValue(span, "X-Dashboard-Id", "dashboard_id") proxy.addTraceFromHeaderValue(span, "X-Dashboard-Id", "dashboard_id")
if err := opentracing.GlobalTracer().Inject( proxy.tracer.Inject(ctx, proxy.ctx.Req.Header, span)
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(proxy.ctx.Req.Header)); err != nil {
logger.Error("Failed to inject span context instance", "err", err)
}
reverseProxy.ServeHTTP(proxy.ctx.Resp, proxy.ctx.Req) 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) panelId := proxy.ctx.Req.Header.Get(headerName)
dashId, err := strconv.Atoi(panelId) dashId, err := strconv.Atoi(panelId)
if err == nil { 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/bus"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient" "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/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
@@ -33,6 +34,8 @@ import (
func TestDataSourceProxy_routeRule(t *testing.T) { func TestDataSourceProxy_routeRule(t *testing.T) {
httpClientProvider := httpclient.NewProvider() httpClientProvider := httpclient.NewProvider()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
t.Run("Plugin with routes", func(t *testing.T) { t.Run("Plugin with routes", func(t *testing.T) {
routes := []*plugins.Route{ routes := []*plugins.Route{
@@ -128,7 +131,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{})
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider,
&oauthtoken.Service{}, dsService) &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
proxy.matchedRoute = routes[0] proxy.matchedRoute = routes[0]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg) 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) { t.Run("When matching route path and has dynamic url", func(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
proxy.matchedRoute = routes[3] proxy.matchedRoute = routes[3]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg) 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) { t.Run("When matching route path with no url", func(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
proxy.matchedRoute = routes[4] proxy.matchedRoute = routes[4]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg) 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) { t.Run("When matching route path and has dynamic body", func(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
proxy.matchedRoute = routes[5] proxy.matchedRoute = routes[5]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.matchedRoute, dsInfo, cfg) 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) { t.Run("plugin route with valid role", func(t *testing.T) {
ctx, _ := setUp() ctx, _ := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
err = proxy.validateRequest() err = proxy.validateRequest()
require.NoError(t, err) 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) { t.Run("plugin route with admin role and user is editor", func(t *testing.T) {
ctx, _ := setUp() ctx, _ := setUp()
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
err = proxy.validateRequest() err = proxy.validateRequest()
require.Error(t, err) require.Error(t, err)
@@ -196,7 +199,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
ctx, _ := setUp() ctx, _ := setUp()
ctx.SignedInUser.OrgRole = models.ROLE_ADMIN ctx.SignedInUser.OrgRole = models.ROLE_ADMIN
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
err = proxy.validateRequest() err = proxy.validateRequest()
require.NoError(t, err) require.NoError(t, err)
@@ -287,7 +290,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
} }
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg) 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) require.NoError(t, err)
client = newFakeHTTPClient(t, json2) client = newFakeHTTPClient(t, json2)
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[1], dsInfo, cfg) 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{}) client = newFakeHTTPClient(t, []byte{})
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg) 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()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
require.NoError(t, err) require.NoError(t, err)
@@ -368,7 +371,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) 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 var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
requestURL, err := url.Parse("http://grafana.com/sub") requestURL, err := url.Parse("http://grafana.com/sub")
@@ -420,7 +423,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var pluginRoutes []*plugins.Route var pluginRoutes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
requestURL, err := url.Parse("http://grafana.com/sub") requestURL, err := url.Parse("http://grafana.com/sub")
@@ -443,7 +446,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
req.Header.Set("Origin", "grafana.com") req.Header.Set("Origin", "grafana.com")
@@ -507,7 +510,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
require.NoError(t, err) require.NoError(t, err)
@@ -632,12 +635,15 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
}, ds }, 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) { t.Run("When response header Set-Cookie is not set should remove proxied Set-Cookie header", func(t *testing.T) {
ctx, ds := setUp(t) ctx, ds := setUp(t)
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
proxy.HandleRequest() proxy.HandleRequest()
@@ -655,7 +661,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
proxy.HandleRequest() proxy.HandleRequest()
@@ -677,7 +683,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
proxy.HandleRequest() proxy.HandleRequest()
@@ -702,7 +708,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
proxy.HandleRequest() proxy.HandleRequest()
@@ -711,6 +717,29 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
require.NotNil(t, req) require.NotNil(t, req)
require.Equal(t, "/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", req.RequestURI) 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) { func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
@@ -723,10 +752,12 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
Url: "://host/root", Url: "://host/root",
} }
cfg := setting.Cfg{} cfg := setting.Cfg{}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.Error(t, err)
assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`)) 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", Url: "127.0.01:5432",
} }
cfg := setting.Cfg{} cfg := setting.Cfg{}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
} }
@@ -756,6 +789,9 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
Context: &web.Context{}, Context: &web.Context{},
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR}, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR},
} }
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
tcs := []struct { tcs := []struct {
description string description string
url string url string
@@ -785,7 +821,7 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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 { if tc.err == nil {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &url.URL{ assert.Equal(t, &url.URL{
@@ -806,11 +842,13 @@ func getDatasourceProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *sett
Type: "custom", Type: "custom",
Url: "http://host/root/", Url: "http://host/root/",
} }
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route var routes []*plugins.Route
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
require.NoError(t, err) 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) { func runDatasourceAuthTest(t *testing.T, secretsService secrets.Service, test *testCase) {
ctx := &models.ReqContext{} ctx := &models.ReqContext{}
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
var routes []*plugins.Route var routes []*plugins.Route
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
@@ -957,6 +998,8 @@ func Test_PathCheck(t *testing.T) {
Method: http.MethodGet, Method: http.MethodGet,
}, },
} }
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
setUp := func() (*models.ReqContext, *http.Request) { setUp := func() (*models.ReqContext, *http.Request) {
req, err := http.NewRequest("GET", "http://localhost/asd", nil) req, err := http.NewRequest("GET", "http://localhost/asd", nil)
@@ -970,7 +1013,7 @@ func Test_PathCheck(t *testing.T) {
ctx, _ := setUp() ctx, _ := setUp()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
dsService := datasources.ProvideService(bus.New(), nil, secretsService, &acmock.Mock{}) 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.NoError(t, err)
require.Nil(t, proxy.validateRequest()) require.Nil(t, proxy.validateRequest())

View File

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

View File

@@ -5,6 +5,7 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -15,6 +16,9 @@ type testQuery struct {
func TestDispatch(t *testing.T) { func TestDispatch(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -23,7 +27,8 @@ func TestDispatch(t *testing.T) {
return nil return nil
}) })
err := bus.Dispatch(context.Background(), &testQuery{}) require.NoError(t, err)
err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err) require.NoError(t, err)
require.True(t, invoked, "expected handler to be called") require.True(t, invoked, "expected handler to be called")
@@ -31,14 +36,20 @@ func TestDispatch(t *testing.T) {
func TestDispatch_NoRegisteredHandler(t *testing.T) { func TestDispatch_NoRegisteredHandler(t *testing.T) {
bus := New() 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, require.Equal(t, err, ErrHandlerNotFound,
"expected bus to return HandlerNotFound since no handler is registered") "expected bus to return HandlerNotFound since no handler is registered")
} }
func TestDispatch_ContextHandler(t *testing.T) { func TestDispatch_ContextHandler(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -47,7 +58,7 @@ func TestDispatch_ContextHandler(t *testing.T) {
return nil return nil
}) })
err := bus.Dispatch(context.Background(), &testQuery{}) err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err) require.NoError(t, err)
require.True(t, invoked, "expected handler to be called") require.True(t, invoked, "expected handler to be called")
@@ -55,6 +66,9 @@ func TestDispatch_ContextHandler(t *testing.T) {
func TestDispatchCtx(t *testing.T) { func TestDispatchCtx(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -63,7 +77,7 @@ func TestDispatchCtx(t *testing.T) {
return nil return nil
}) })
err := bus.Dispatch(context.Background(), &testQuery{}) err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err) require.NoError(t, err)
require.True(t, invoked, "expected handler to be called") require.True(t, invoked, "expected handler to be called")
@@ -71,6 +85,9 @@ func TestDispatchCtx(t *testing.T) {
func TestDispatchCtx_NoContextHandler(t *testing.T) { func TestDispatchCtx_NoContextHandler(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -79,7 +96,7 @@ func TestDispatchCtx_NoContextHandler(t *testing.T) {
return nil return nil
}) })
err := bus.Dispatch(context.Background(), &testQuery{}) err = bus.Dispatch(context.Background(), &testQuery{})
require.NoError(t, err) require.NoError(t, err)
require.True(t, invoked, "expected handler to be called") 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) { func TestDispatchCtx_NoRegisteredHandler(t *testing.T) {
bus := New() 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, require.Equal(t, err, ErrHandlerNotFound,
"expected bus to return HandlerNotFound since no handler is registered") "expected bus to return HandlerNotFound since no handler is registered")
} }
func TestQuery(t *testing.T) { func TestQuery(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
want := "hello from handler" want := "hello from handler"
@@ -105,7 +128,7 @@ func TestQuery(t *testing.T) {
q := &testQuery{} q := &testQuery{}
err := bus.Dispatch(context.Background(), q) err = bus.Dispatch(context.Background(), q)
require.NoError(t, err, "unable to dispatch query") require.NoError(t, err, "unable to dispatch query")
require.Equal(t, want, q.Resp) require.Equal(t, want, q.Resp)
@@ -113,17 +136,23 @@ func TestQuery(t *testing.T) {
func TestQuery_HandlerReturnsError(t *testing.T) { func TestQuery_HandlerReturnsError(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
bus.AddHandler(func(ctx context.Context, query *testQuery) error { bus.AddHandler(func(ctx context.Context, query *testQuery) error {
return errors.New("handler 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") require.Error(t, err, "expected error but got none")
} }
func TestEventPublish(t *testing.T) { func TestEventPublish(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -132,7 +161,7 @@ func TestEventPublish(t *testing.T) {
return nil return nil
}) })
err := bus.Publish(context.Background(), &testQuery{}) err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event") require.NoError(t, err, "unable to publish event")
require.True(t, invoked) require.True(t, invoked)
@@ -140,13 +169,19 @@ func TestEventPublish(t *testing.T) {
func TestEventPublish_NoRegisteredListener(t *testing.T) { func TestEventPublish_NoRegisteredListener(t *testing.T) {
bus := New() 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") require.NoError(t, err, "unable to publish event")
} }
func TestEventCtxPublishCtx(t *testing.T) { func TestEventCtxPublishCtx(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -155,7 +190,7 @@ func TestEventCtxPublishCtx(t *testing.T) {
return nil return nil
}) })
err := bus.Publish(context.Background(), &testQuery{}) err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event") require.NoError(t, err, "unable to publish event")
require.True(t, invoked) require.True(t, invoked)
@@ -163,13 +198,19 @@ func TestEventCtxPublishCtx(t *testing.T) {
func TestEventPublishCtx_NoRegisteredListener(t *testing.T) { func TestEventPublishCtx_NoRegisteredListener(t *testing.T) {
bus := New() 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") require.NoError(t, err, "unable to publish event")
} }
func TestEventPublishCtx(t *testing.T) { func TestEventPublishCtx(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -178,7 +219,7 @@ func TestEventPublishCtx(t *testing.T) {
return nil return nil
}) })
err := bus.Publish(context.Background(), &testQuery{}) err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event") require.NoError(t, err, "unable to publish event")
require.True(t, invoked) require.True(t, invoked)
@@ -186,6 +227,9 @@ func TestEventPublishCtx(t *testing.T) {
func TestEventCtxPublish(t *testing.T) { func TestEventCtxPublish(t *testing.T) {
bus := New() bus := New()
tracer, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
bus.tracer = tracer
var invoked bool var invoked bool
@@ -194,7 +238,7 @@ func TestEventCtxPublish(t *testing.T) {
return nil return nil
}) })
err := bus.Publish(context.Background(), &testQuery{}) err = bus.Publish(context.Background(), &testQuery{})
require.NoError(t, err, "unable to publish event") require.NoError(t, err, "unable to publish event")
require.True(t, invoked) 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/runner"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services" "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" "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"
"github.com/grafana/grafana/pkg/services/sqlstore/migrations" "github.com/grafana/grafana/pkg/services/sqlstore/migrations"
"github.com/grafana/grafana/pkg/setting" "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) 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 { if err != nil {
return errutil.Wrap("failed to initialize SQL store", err) 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/api/routing"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/localcache" "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/infra/usagestats"
"github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets"
secretsDatabase "github.com/grafana/grafana/pkg/services/secrets/database" secretsDatabase "github.com/grafana/grafana/pkg/services/secrets/database"
@@ -22,6 +23,7 @@ import (
var wireSet = wire.NewSet( var wireSet = wire.NewSet(
New, New,
localcache.ProvideService, localcache.ProvideService,
tracing.ProvideService,
bus.ProvideBus, bus.ProvideBus,
wire.Bind(new(bus.Bus), new(*bus.InProcBus)), wire.Bind(new(bus.Bus), new(*bus.InProcBus)),
sqlstore.ProvideService, sqlstore.ProvideService,

View File

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

View File

@@ -4,6 +4,7 @@ import (
"testing" "testing"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" 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/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -19,7 +20,9 @@ func TestHTTPClientProvider(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
newProviderFunc = origNewProviderFunc 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) require.Len(t, providerOpts, 1)
o := providerOpts[0] o := providerOpts[0]
require.Len(t, o.Middlewares, 6) require.Len(t, o.Middlewares, 6)
@@ -41,7 +44,9 @@ func TestHTTPClientProvider(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
newProviderFunc = origNewProviderFunc 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) require.Len(t, providerOpts, 1)
o := providerOpts[0] o := providerOpts[0]
require.Len(t, o.Middlewares, 7) require.Len(t, o.Middlewares, 7)

View File

@@ -1,12 +1,16 @@
package httpclientprovider package httpclientprovider
import ( import (
"fmt"
"net/http" "net/http"
"strconv"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/opentracing/opentracing-go" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/opentracing/opentracing-go/ext" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
) )
const ( const (
@@ -14,32 +18,26 @@ const (
httpContentLengthTagKey = "http.content_length" 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.NamedMiddlewareFunc(TracingMiddlewareName, func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper {
return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
span, ctx := opentracing.StartSpanFromContext(req.Context(), "HTTP Outgoing Request") ctx, span := tracer.Start(req.Context(), "HTTP Outgoing Request", trace.WithSpanKind(trace.SpanKindClient))
defer span.Finish() defer span.End()
req = req.WithContext(ctx) req = req.WithContext(ctx)
for k, v := range opts.Labels { for k, v := range opts.Labels {
span.SetTag(k, v) span.SetAttributes(k, v, attribute.Key(k).String(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)
} }
tracer.Inject(ctx, req.Header, span)
res, err := next.RoundTrip(req) res, err := next.RoundTrip(req)
ext.HTTPUrl.Set(span, req.URL.String()) span.SetAttributes("http.url", req.URL.String(), attribute.String("http.url", req.URL.String()))
ext.HTTPMethod.Set(span, req.Method) span.SetAttributes("http.method", req.Method, attribute.String("http.method", req.Method))
ext.SpanKind.Set(span, ext.SpanKindRPCClientEnum) // ext.SpanKind.Set(span, ext.SpanKindRPCClientEnum)
if err != nil { if err != nil {
ext.Error.Set(span, true) span.RecordError(err)
return res, 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 // we avoid measuring contentlength less than zero because it indicates
// that the content size is unknown. https://godoc.org/github.com/badu/http#Response // that the content size is unknown. https://godoc.org/github.com/badu/http#Response
if res.ContentLength > 0 { 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 { 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-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/opentracing/opentracing-go" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/opentracing/opentracing-go/ext"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
jaeger "github.com/uber/jaeger-client-go"
) )
func TestTracingMiddleware(t *testing.T) { 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) { 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) { finalRoundTripper := httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusOK, Request: req}, nil 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{ rt := mw.CreateMiddleware(httpclient.Options{
Labels: map[string]string{ Labels: map[string]string{
"l1": "v1", "l1": "v1",
@@ -44,24 +43,8 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close()) require.NoError(t, res.Body.Close())
} }
sp := opentracing.SpanFromContext(res.Request.Context()) _, sp := tracer.Start(ctx, "test")
require.NotNil(t, sp) 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) { 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 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{ rt := mw.CreateMiddleware(httpclient.Options{
Labels: map[string]string{ Labels: map[string]string{
"l1": "v1", "l1": "v1",
@@ -91,25 +74,8 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close()) require.NoError(t, res.Body.Close())
} }
sp := opentracing.SpanFromContext(res.Request.Context()) _, sp := tracer.Start(res.Request.Context(), "test")
require.NotNil(t, sp) 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) { 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 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{ rt := mw.CreateMiddleware(httpclient.Options{
Labels: map[string]string{ Labels: map[string]string{
"l1": "v1", "l1": "v1",
@@ -139,35 +105,7 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close()) require.NoError(t, res.Body.Close())
} }
sp := opentracing.SpanFromContext(res.Request.Context()) _, sp := tracer.Start(res.Request.Context(), "test")
require.NotNil(t, sp) 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 ( import (
"context" "context"
"net/http"
"time" "time"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace" tracesdk "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0" semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
@@ -16,25 +19,27 @@ import (
) )
type Tracer interface { type Tracer interface {
Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span)
Run(context.Context) error 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 { type Span interface {
End() 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 ( type Opentelemetry struct {
GlobalTracer trace.Tracer
)
type OpentelemetryTracingService struct {
enabled bool enabled bool
address string address string
log log.Logger log log.Logger
tracerProvider *tracesdk.TracerProvider tracerProvider *tracesdk.TracerProvider
tracer trace.Tracer
Cfg *setting.Cfg Cfg *setting.Cfg
} }
@@ -43,7 +48,12 @@ type OpentelemetrySpan struct {
span trace.Span 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") section, err := ots.Cfg.Raw.GetSection("tracing.opentelemetry.jaeger")
if err != nil { if err != nil {
return err return err
@@ -57,7 +67,7 @@ func (ots *OpentelemetryTracingService) parseSettingsOpentelemetry() error {
return nil return nil
} }
func (ots *OpentelemetryTracingService) initTracerProvider() (*tracesdk.TracerProvider, error) { func (ots *Opentelemetry) initTracerProvider() (*tracesdk.TracerProvider, error) {
// Create the Jaeger exporter // Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(ots.address))) exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(ots.address)))
if err != nil { if err != nil {
@@ -76,7 +86,7 @@ func (ots *OpentelemetryTracingService) initTracerProvider() (*tracesdk.TracerPr
return tp, nil return tp, nil
} }
func (ots *OpentelemetryTracingService) initOpentelemetryTracer() error { func (ots *Opentelemetry) initOpentelemetryTracer() error {
tp, err := ots.initTracerProvider() tp, err := ots.initTracerProvider()
if err != nil { if err != nil {
return err return err
@@ -89,15 +99,18 @@ func (ots *OpentelemetryTracingService) initOpentelemetryTracer() error {
} }
ots.tracerProvider = tp ots.tracerProvider = tp
GlobalTracer = otel.GetTracerProvider().Tracer("component-main") ots.tracer = otel.GetTracerProvider().Tracer("component-main")
return nil return nil
} }
func (ots *OpentelemetryTracingService) Run(ctx context.Context) error { func (ots *Opentelemetry) Run(ctx context.Context) error {
<-ctx.Done() <-ctx.Done()
ots.log.Info("Closing tracing") ots.log.Info("Closing tracing")
if ots.tracerProvider == nil {
return nil
}
ctxShutdown, cancel := context.WithTimeout(ctx, time.Second*5) ctxShutdown, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel() defer cancel()
@@ -108,16 +121,47 @@ func (ots *OpentelemetryTracingService) Run(ctx context.Context) error {
return nil return nil
} }
func (ots *OpentelemetryTracingService) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) { func (ots *Opentelemetry) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
ctx, span := GlobalTracer.Start(ctx, spanName) ctx, span := ots.tracer.Start(ctx, spanName)
oSpan := OpentelemetrySpan{span: span} opentelemetrySpan := OpentelemetrySpan{
return ctx, oSpan 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() { func (s OpentelemetrySpan) End() {
s.span.End() s.span.End()
} }
func (s OpentelemetrySpan) SetAttributes(kv ...attribute.KeyValue) { func (s OpentelemetrySpan) SetAttributes(key string, value interface{}, kv attribute.KeyValue) {
s.span.SetAttributes(kv...) 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" "context"
"fmt" "fmt"
"io" "io"
"net/http"
"os" "os"
"strings" "strings"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"go.opentelemetry.io/otel/attribute"
trace "go.opentelemetry.io/otel/trace"
opentracing "github.com/opentracing/opentracing-go" 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" jaegercfg "github.com/uber/jaeger-client-go/config"
"github.com/uber/jaeger-client-go/zipkin" "github.com/uber/jaeger-client-go/zipkin"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
trace "go.opentelemetry.io/otel/trace"
) )
const ( const (
@@ -23,7 +27,7 @@ const (
) )
func ProvideService(cfg *setting.Cfg) (Tracer, error) { func ProvideService(cfg *setting.Cfg) (Tracer, error) {
ts := &TracingService{ ts := &Opentracing{
Cfg: cfg, Cfg: cfg,
log: log.New("tracing"), log: log.New("tracing"),
} }
@@ -36,7 +40,7 @@ func ProvideService(cfg *setting.Cfg) (Tracer, error) {
return ts, ts.initGlobalTracer() return ts, ts.initGlobalTracer()
} }
ots := &OpentelemetryTracingService{ ots := &Opentelemetry{
Cfg: cfg, Cfg: cfg,
log: log.New("tracing"), log: log.New("tracing"),
} }
@@ -48,7 +52,7 @@ func ProvideService(cfg *setting.Cfg) (Tracer, error) {
return ots, ots.initOpentelemetryTracer() return ots, ots.initOpentelemetryTracer()
} }
type TracingService struct { type Opentracing struct {
enabled bool enabled bool
address string address string
customTags map[string]string customTags map[string]string
@@ -67,7 +71,7 @@ type OpentracingSpan struct {
span opentracing.Span span opentracing.Span
} }
func (ts *TracingService) parseSettings() error { func (ts *Opentracing) parseSettings() error {
var section, err = ts.Cfg.Raw.GetSection("tracing.jaeger") var section, err = ts.Cfg.Raw.GetSection("tracing.jaeger")
if err != nil { if err != nil {
return err return err
@@ -94,7 +98,7 @@ func (ts *TracingService) parseSettings() error {
return nil return nil
} }
func (ts *TracingService) initJaegerCfg() (jaegercfg.Configuration, error) { func (ts *Opentracing) initJaegerCfg() (jaegercfg.Configuration, error) {
cfg := jaegercfg.Configuration{ cfg := jaegercfg.Configuration{
ServiceName: "grafana", ServiceName: "grafana",
Disabled: !ts.enabled, Disabled: !ts.enabled,
@@ -116,7 +120,7 @@ func (ts *TracingService) initJaegerCfg() (jaegercfg.Configuration, error) {
return cfg, nil return cfg, nil
} }
func (ts *TracingService) initGlobalTracer() error { func (ts *Opentracing) initGlobalTracer() error {
cfg, err := ts.initJaegerCfg() cfg, err := ts.initJaegerCfg()
if err != nil { if err != nil {
return err return err
@@ -151,11 +155,10 @@ func (ts *TracingService) initGlobalTracer() error {
opentracing.SetGlobalTracer(tracer) opentracing.SetGlobalTracer(tracer)
ts.closer = closer ts.closer = closer
return nil return nil
} }
func (ts *TracingService) Run(ctx context.Context) error { func (ts *Opentracing) Run(ctx context.Context) error {
<-ctx.Done() <-ctx.Done()
if ts.closer != nil { if ts.closer != nil {
@@ -166,22 +169,60 @@ func (ts *TracingService) Run(ctx context.Context) error {
return nil 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) span, ctx := opentracing.StartSpanFromContext(ctx, spanName)
oSpan := OpentracingSpan{ opentracingSpan := OpentracingSpan{span: span}
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() { func (s OpentracingSpan) End() {
s.span.Finish() s.span.Finish()
} }
func (s OpentracingSpan) SetAttributes(kv ...attribute.KeyValue) { func (s OpentracingSpan) SetAttributes(key string, value interface{}, kv attribute.KeyValue) {
for k, v := range kv { s.span.SetTag(key, value)
s.span.SetTag(fmt.Sprint(k), v) }
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 { func splitTagSettings(input string) map[string]string {

View File

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

View File

@@ -62,6 +62,7 @@ func TestValidateLoginAttempts(t *testing.T) {
withLoginAttempts(t, tc.loginAttempts) withLoginAttempts(t, tc.loginAttempts)
query := &models.LoginUserQuery{Username: "user", Cfg: tc.cfg} query := &models.LoginUserQuery{Username: "user", Cfg: tc.cfg}
err := validateLoginAttempts(context.Background(), query) err := validateLoginAttempts(context.Background(), query)
require.Equal(t, tc.expected, err) 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) { middlewareScenario(t, "ReqSignIn true and unauthenticated request", func(t *testing.T, sc *scenarioContext) {
sc.m.Get("/secure", reqSignIn, sc.defaultHandler) sc.m.Get("/secure", reqSignIn, sc.defaultHandler)
sc.fakeReq("GET", "/secure").exec() sc.fakeReq("GET", "/secure").exec()
assert.Equal(t, 302, sc.resp.Code) 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/fs"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache" "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/login"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/auth"
@@ -731,7 +732,9 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
userAuthTokenSvc := auth.NewFakeUserAuthTokenService() userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{} renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService() 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 { type fakeRenderService struct {

View File

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

View File

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

View File

@@ -12,6 +12,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/encryption/ossencryption"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@@ -21,7 +22,9 @@ import (
func TestEngineTimeouts(t *testing.T) { func TestEngineTimeouts(t *testing.T) {
usMock := &usagestats.UsageStatsMock{T: 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.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3 setting.AlertingMaxAttempts = 3
engine.resultHandler = &FakeResultHandler{} engine.resultHandler = &FakeResultHandler{}

View File

@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson" "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/infra/usagestats"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/encryption/ossencryption"
@@ -45,7 +46,9 @@ func (handler *FakeResultHandler) handle(evalContext *EvalContext) error {
func TestEngineProcessJob(t *testing.T) { func TestEngineProcessJob(t *testing.T) {
bus := bus.New() bus := bus.New()
usMock := &usagestats.UsageStatsMock{T: t} 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.AlertingEvaluationTimeout = 30 * time.Second
setting.AlertingNotificationTimeout = 30 * time.Second setting.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3 setting.AlertingMaxAttempts = 3

View File

@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache" "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/models"
"github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy" "github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
@@ -102,6 +103,8 @@ func getContextHandler(t *testing.T) *ContextHandler {
userAuthTokenSvc := auth.NewFakeUserAuthTokenService() userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{} renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService() 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/log"
"github.com/grafana/grafana/pkg/infra/network" "github.com/grafana/grafana/pkg/infra/network"
"github.com/grafana/grafana/pkg/infra/remotecache" "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/middleware/cookies"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy" "github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
@@ -23,8 +24,6 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
"github.com/opentracing/opentracing-go"
ol "github.com/opentracing/opentracing-go/log"
cw "github.com/weaveworks/common/tracing" cw "github.com/weaveworks/common/tracing"
) )
@@ -36,7 +35,8 @@ const (
const ServiceName = "ContextHandler" const ServiceName = "ContextHandler"
func ProvideService(cfg *setting.Cfg, tokenService models.UserTokenService, jwtService models.JWTService, 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{ return &ContextHandler{
Cfg: cfg, Cfg: cfg,
AuthTokenService: tokenService, AuthTokenService: tokenService,
@@ -44,6 +44,7 @@ func ProvideService(cfg *setting.Cfg, tokenService models.UserTokenService, jwtS
RemoteCache: remoteCache, RemoteCache: remoteCache,
RenderService: renderService, RenderService: renderService,
SQLStore: sqlStore, SQLStore: sqlStore,
tracer: tracer,
} }
} }
@@ -55,7 +56,7 @@ type ContextHandler struct {
RemoteCache *remotecache.RemoteCache RemoteCache *remotecache.RemoteCache
RenderService rendering.Service RenderService rendering.Service
SQLStore *sqlstore.SQLStore SQLStore *sqlstore.SQLStore
tracer tracing.Tracer
// GetTime returns the current time. // GetTime returns the current time.
// Stubbable by tests. // Stubbable by tests.
GetTime func() time.Time GetTime func() time.Time
@@ -73,8 +74,8 @@ func FromContext(c context.Context) *models.ReqContext {
// Middleware provides a middleware to initialize the Macaron context. // Middleware provides a middleware to initialize the Macaron context.
func (h *ContextHandler) Middleware(mContext *web.Context) { func (h *ContextHandler) Middleware(mContext *web.Context) {
span, _ := opentracing.StartSpanFromContext(mContext.Req.Context(), "Auth - Middleware") _, span := h.tracer.Start(mContext.Req.Context(), "Auth - Middleware")
defer span.Finish() defer span.End()
reqContext := &models.ReqContext{ reqContext := &models.ReqContext{
Context: mContext, 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) reqContext.Logger = log.New("context", "userId", reqContext.UserId, "orgId", reqContext.OrgId, "uname", reqContext.Login)
span.AddEvents(
span.LogFields( []string{"uname", "orgId", "userId"},
ol.String("uname", reqContext.Login), []tracing.EventValue{
ol.Int64("orgId", reqContext.OrgId), {Str: reqContext.Login},
ol.Int64("userId", reqContext.UserId)) {Num: reqContext.OrgId},
{Num: reqContext.UserId}},
)
mContext.Map(reqContext) mContext.Map(reqContext)
@@ -157,8 +160,8 @@ func (h *ContextHandler) initContextWithAnonymousUser(reqContext *models.ReqCont
return false return false
} }
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithAnonymousUser") _, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAnonymousUser")
defer span.Finish() defer span.End()
org, err := h.SQLStore.GetOrgByName(h.Cfg.AnonymousOrgName) org, err := h.SQLStore.GetOrgByName(h.Cfg.AnonymousOrgName)
if err != nil { if err != nil {
@@ -192,8 +195,8 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
return false return false
} }
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithAPIKey") _, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAPIKey")
defer span.Finish() defer span.End()
// base64 decode key // base64 decode key
decoded, err := apikeygen.Decode(keyString) decoded, err := apikeygen.Decode(keyString)
@@ -272,8 +275,8 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
return false return false
} }
span, ctx := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithBasicAuth") ctx, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
defer span.Finish() defer span.End()
username, password, err := util.DecodeBasicAuthHeader(header) username, password, err := util.DecodeBasicAuthHeader(header)
if err != nil { if err != nil {
@@ -328,8 +331,8 @@ func (h *ContextHandler) initContextWithToken(reqContext *models.ReqContext, org
return false return false
} }
span, ctx := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithToken") ctx, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithToken")
defer span.Finish() defer span.End()
token, err := h.AuthTokenService.LookupToken(ctx, rawToken) token, err := h.AuthTokenService.LookupToken(ctx, rawToken)
if err != nil { if err != nil {
@@ -369,8 +372,8 @@ func (h *ContextHandler) rotateEndOfRequestFunc(reqContext *models.ReqContext, a
return return
} }
span, ctx := opentracing.StartSpanFromContext(reqContext.Req.Context(), "rotateEndOfRequestFunc") ctx, span := h.tracer.Start(reqContext.Req.Context(), "rotateEndOfRequestFunc")
defer span.Finish() defer span.End()
addr := reqContext.RemoteAddr() addr := reqContext.RemoteAddr()
ip, err := network.GetIPFromAddress(addr) ip, err := network.GetIPFromAddress(addr)
@@ -396,8 +399,8 @@ func (h *ContextHandler) initContextWithRenderAuth(reqContext *models.ReqContext
return false return false
} }
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithRenderAuth") _, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithRenderAuth")
defer span.Finish() defer span.End()
renderUser, exists := h.RenderService.GetRenderUser(reqContext.Req.Context(), key) renderUser, exists := h.RenderService.GetRenderUser(reqContext.Req.Context(), key)
if !exists { if !exists {
@@ -466,8 +469,8 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
return false return false
} }
span, _ := opentracing.StartSpanFromContext(reqContext.Req.Context(), "initContextWithAuthProxy") _, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAuthProxy")
defer span.Finish() defer span.End()
// Check if allowed to continue with this IP // Check if allowed to continue with this IP
if err := auth.IsAllowedIP(); err != nil { 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/api/pluginproxy"
"github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/metrics" "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/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
@@ -21,7 +22,7 @@ import (
func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator models.PluginRequestValidator, func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator models.PluginRequestValidator,
pluginStore plugins.Store, cfg *setting.Cfg, httpClientProvider httpclient.Provider, 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{ return &DataSourceProxyService{
DataSourceCache: dataSourceCache, DataSourceCache: dataSourceCache,
PluginRequestValidator: plugReqValidator, PluginRequestValidator: plugReqValidator,
@@ -30,6 +31,7 @@ func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator m
HTTPClientProvider: httpClientProvider, HTTPClientProvider: httpClientProvider,
OAuthTokenService: oauthTokenService, OAuthTokenService: oauthTokenService,
DataSourcesService: dsService, DataSourcesService: dsService,
tracer: tracer,
} }
} }
@@ -41,6 +43,7 @@ type DataSourceProxyService struct {
HTTPClientProvider httpclient.Provider HTTPClientProvider httpclient.Provider
OAuthTokenService *oauthtoken.Service OAuthTokenService *oauthtoken.Service
DataSourcesService *datasources.Service DataSourcesService *datasources.Service
tracer tracing.Tracer
} }
func (p *DataSourceProxyService) ProxyDataSourceRequest(c *models.ReqContext) { func (p *DataSourceProxyService) ProxyDataSourceRequest(c *models.ReqContext) {
@@ -84,7 +87,7 @@ func (p *DataSourceProxyService) ProxyDatasourceRequestWithID(c *models.ReqConte
proxyPath := getProxyPath(c) proxyPath := getProxyPath(c)
proxy, err := pluginproxy.NewDataSourceProxy(ds, plugin.Routes, c, proxyPath, p.Cfg, p.HTTPClientProvider, 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 err != nil {
if errors.Is(err, datasource.URLValidationError{}) { if errors.Is(err, datasource.URLValidationError{}) {
c.JsonApiErr(http.StatusBadRequest, fmt.Sprintf("Invalid data source URL: %q", ds.Url), err) 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", Name: "User In DB",
Login: userInDbName, Login: userInDbName,
} }
_, err := sqlStore.CreateUser(context.Background(), cmd) _, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err) 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", Name: "User In DB",
Login: userInDbName, Login: userInDbName,
} }
_, err := sqlStore.CreateUser(context.Background(), cmd) _, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err) require.NoError(t, err)

View File

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

View File

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

View File

@@ -11,11 +11,10 @@ import (
"github.com/gchaincl/sqlhooks" "github.com/gchaincl/sqlhooks"
"github.com/go-sql-driver/mysql" "github.com/go-sql-driver/mysql"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
"github.com/opentracing/opentracing-go"
ol "github.com/opentracing/opentracing-go/log"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
cw "github.com/weaveworks/common/tracing" cw "github.com/weaveworks/common/tracing"
"xorm.io/core" "xorm.io/core"
@@ -39,7 +38,7 @@ func init() {
// WrapDatabaseDriverWithHooks creates a fake database driver that // WrapDatabaseDriverWithHooks creates a fake database driver that
// executes pre and post functions which we use to gather metrics about // executes pre and post functions which we use to gather metrics about
// database queries. It also registers the metrics. // 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{ drivers := map[string]driver.Driver{
migrator.SQLite: &sqlite3.SQLiteDriver{}, migrator.SQLite: &sqlite3.SQLiteDriver{},
migrator.MySQL: &mysql.MySQLDriver{}, migrator.MySQL: &mysql.MySQLDriver{},
@@ -52,7 +51,7 @@ func WrapDatabaseDriverWithHooks(dbType string) string {
} }
driverWithHooks := dbType + "WithHooks" 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}) core.RegisterDriver(driverWithHooks, &databaseQueryWrapperDriver{dbType: dbType})
return driverWithHooks return driverWithHooks
} }
@@ -60,7 +59,8 @@ func WrapDatabaseDriverWithHooks(dbType string) string {
// databaseQueryWrapper satisfies the sqlhook.databaseQueryWrapper interface // databaseQueryWrapper satisfies the sqlhook.databaseQueryWrapper interface
// which allow us to wrap all SQL queries with a `Before` & `After` hook. // which allow us to wrap all SQL queries with a `Before` & `After` hook.
type databaseQueryWrapper struct { type databaseQueryWrapper struct {
log log.Logger log log.Logger
tracer tracing.Tracer
} }
// databaseQueryWrapperKey is used as key to save values in `context.Context` // 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()) histogram.Observe(elapsed.Seconds())
} }
span, _ := opentracing.StartSpanFromContext(ctx, "database query") _, span := h.tracer.Start(ctx, "database query")
defer span.Finish() defer span.End()
span.LogFields( span.AddEvents([]string{"query", "status"}, []tracing.EventValue{{Str: query}, {Str: status}})
ol.String("query", query),
ol.String("status", status))
if err != nil { 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) 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/fs"
"github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log" "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/models"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/annotations" "github.com/grafana/grafana/pkg/services/annotations"
@@ -51,14 +52,16 @@ type SQLStore struct {
Dialect migrator.Dialect Dialect migrator.Dialect
skipEnsureDefaultOrgAndUser bool skipEnsureDefaultOrgAndUser bool
migrations registry.DatabaseMigrator 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 // This change will make xorm use an empty default schema for postgres and
// by that mimic the functionality of how it was functioning before // by that mimic the functionality of how it was functioning before
// xorm's changes above. // xorm's changes above.
xorm.DefaultPostgresSchema = "" xorm.DefaultPostgresSchema = ""
s, err := newSQLStore(cfg, cacheService, bus, nil, migrations) s, err := newSQLStore(cfg, cacheService, bus, nil, migrations, tracer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -70,7 +73,7 @@ func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, bus
if err := s.Reset(); err != nil { if err := s.Reset(); err != nil {
return nil, err return nil, err
} }
s.tracer = tracer
return s, nil 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, 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{ ss := &SQLStore{
Cfg: cfg, Cfg: cfg,
Bus: bus, Bus: bus,
@@ -87,6 +90,7 @@ func newSQLStore(cfg *setting.Cfg, cacheService *localcache.CacheService, bus bu
log: log.New("sqlstore"), log: log.New("sqlstore"),
skipEnsureDefaultOrgAndUser: false, skipEnsureDefaultOrgAndUser: false,
migrations: migrations, migrations: migrations,
tracer: tracer,
} }
for _, opt := range opts { for _, opt := range opts {
if !opt.EnsureDefaultOrgAndUser { if !opt.EnsureDefaultOrgAndUser {
@@ -323,7 +327,7 @@ func (ss *SQLStore) initEngine(engine *xorm.Engine) error {
} }
if ss.Cfg.IsDatabaseMetricsEnabled() { 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) 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.DatabaseTZ = time.UTC
engine.TZLocation = 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 { if err != nil {
return nil, err return nil, err
} }

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/teamguardian/database" "github.com/grafana/grafana/pkg/services/teamguardian/database"
"github.com/stretchr/testify/mock" "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("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) { t.Run("Should not be able to update the team", func(t *testing.T) {
_, err := tracing.InitializeTracerForTest()
require.NoError(t, err)
ctx := context.Background() ctx := context.Background()
store.On("GetTeamMembers", ctx, mock.Anything).Return([]*models.TeamMemberDTO{}, nil).Once() 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) require.Equal(t, models.ErrNotAllowedToUpdateTeam, err)
}) })
}) })

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,8 +14,9 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go" "go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp" "golang.org/x/net/context/ctxhttp"
) )
@@ -48,7 +49,8 @@ func (e *ApplicationInsightsDatasource) resourceRequest(rw http.ResponseWriter,
} }
func (e *ApplicationInsightsDatasource) executeTimeSeriesQuery(ctx context.Context, 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() result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries) queries, err := e.buildQueries(originalQueries)
@@ -57,7 +59,7 @@ func (e *ApplicationInsightsDatasource) executeTimeSeriesQuery(ctx context.Conte
} }
for _, query := range queries { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -128,7 +130,7 @@ func (e *ApplicationInsightsDatasource) buildQueries(queries []backend.DataQuery
return applicationInsightsQueries, nil 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) { backend.DataResponse, error) {
dataResponse := backend.DataResponse{} 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.Path = path.Join(req.URL.Path, query.ApiURL)
req.URL.RawQuery = query.Params.Encode() req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "application insights query") ctx, span := tracer.Start(ctx, "application insights query")
span.SetTag("target", query.Target) span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond)) span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetTag("until", query.TimeRange.To.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.SetTag("datasource_id", dsInfo.DatasourceID) span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetTag("org_id", dsInfo.OrgID) span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish() defer span.End()
err = opentracing.GlobalTracer().Inject( tracer.Inject(ctx, req.Header, span)
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
if err != nil {
azlog.Warn("failed to inject global tracer")
}
azlog.Debug("ApplicationInsights", "Request URL", req.URL.String()) azlog.Debug("ApplicationInsights", "Request URL", req.URL.String())
res, err := ctxhttp.Do(ctx, client, req) 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/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go" "go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp" "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 // 1. build the AzureMonitor url and querystring for each query
// 2. executes each query by calling the Azure Monitor API // 2. executes each query by calling the Azure Monitor API
// 3. parses the responses for each query into data frames // 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() result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries, dsInfo) queries, err := e.buildQueries(originalQueries, dsInfo)
@@ -55,7 +57,7 @@ func (e *AzureLogAnalyticsDatasource) executeTimeSeriesQuery(ctx context.Context
} }
for _, query := range queries { 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 return result, nil
@@ -125,7 +127,8 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(queries []backend.DataQuery,
return azureLogAnalyticsQueries, nil 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{} dataResponse := backend.DataResponse{}
dataResponseErrorWithExecuted := func(err error) 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.Path = path.Join(req.URL.Path, query.URL)
req.URL.RawQuery = query.Params.Encode() req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "azure log analytics query") ctx, span := tracer.Start(ctx, "azure log analytics query")
span.SetTag("target", query.Target) span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond)) span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetTag("until", query.TimeRange.To.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.SetTag("datasource_id", dsInfo.DatasourceID) span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetTag("org_id", dsInfo.OrgID) span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish() defer span.End()
if err := opentracing.GlobalTracer().Inject( tracer.Inject(ctx, req.Header, span)
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
return dataResponseErrorWithExecuted(err)
}
azlog.Debug("AzureLogAnalytics", "Request ApiURL", req.URL.String()) azlog.Debug("AzureLogAnalytics", "Request ApiURL", req.URL.String())
res, err := ctxhttp.Do(ctx, client, req) res, err := ctxhttp.Do(ctx, client, req)

View File

@@ -11,6 +11,7 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -228,7 +229,9 @@ func Test_executeQueryErrorWithDifferentLogAnalyticsCreds(t *testing.T) {
Params: url.Values{}, Params: url.Values{},
TimeRange: backend.TimeRange{}, 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 { if res.Error == nil {
t.Fatal("expecting an error") 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/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/components/simplejson" "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/setting"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go" "go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp" "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 // 1. builds the AzureMonitor url and querystring for each query
// 2. executes each query by calling the Azure Monitor API // 2. executes each query by calling the Azure Monitor API
// 3. parses the responses for each query into data frames // 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{ result := &backend.QueryDataResponse{
Responses: map[string]backend.DataResponse{}, Responses: map[string]backend.DataResponse{},
} }
@@ -59,7 +61,7 @@ func (e *AzureResourceGraphDatasource) executeTimeSeriesQuery(ctx context.Contex
} }
for _, query := range queries { 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 return result, nil
@@ -101,7 +103,8 @@ func (e *AzureResourceGraphDatasource) buildQueries(queries []backend.DataQuery,
return azureResourceGraphQueries, nil 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{} dataResponse := backend.DataResponse{}
params := url.Values{} 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.Path = path.Join(req.URL.Path, argQueryProviderName)
req.URL.RawQuery = params.Encode() req.URL.RawQuery = params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "azure resource graph query") ctx, span := tracer.Start(ctx, "azure resource graph query")
span.SetTag("interpolated_query", query.InterpolatedQuery) span.SetAttributes("interpolated_query", query.InterpolatedQuery, attribute.Key("interpolated_query").String(query.InterpolatedQuery))
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond)) span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetTag("until", query.TimeRange.To.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.SetTag("datasource_id", dsInfo.DatasourceID) span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetTag("org_id", dsInfo.OrgID) span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish() defer span.End()
if err := opentracing.GlobalTracer().Inject( tracer.Inject(ctx, req.Header, span)
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
return dataResponseErrorWithExecuted(err)
}
azlog.Debug("AzureResourceGraph", "Request ApiURL", req.URL.String()) azlog.Debug("AzureResourceGraph", "Request ApiURL", req.URL.String())
res, err := ctxhttp.Do(ctx, client, req) 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/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "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/setting"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
opentracing "github.com/opentracing/opentracing-go" "go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp" "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 // 1. build the AzureMonitor url and querystring for each query
// 2. executes each query by calling the Azure Monitor API // 2. executes each query by calling the Azure Monitor API
// 3. parses the responses for each query into data frames // 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() result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries, dsInfo) queries, err := e.buildQueries(originalQueries, dsInfo)
@@ -52,7 +54,7 @@ func (e *AzureMonitorDatasource) executeTimeSeriesQuery(ctx context.Context, ori
} }
for _, query := range queries { 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 return result, nil
@@ -147,7 +149,8 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
return azureMonitorQueries, nil 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{} dataResponse := backend.DataResponse{}
req, err := e.createRequest(ctx, dsInfo, url) 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.Path = path.Join(req.URL.Path, query.URL)
req.URL.RawQuery = query.Params.Encode() req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "azuremonitor query") ctx, span := tracer.Start(ctx, "azuremonitor query")
span.SetTag("target", query.Target) span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetTag("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond)) span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond)))
span.SetTag("until", query.TimeRange.To.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.SetTag("datasource_id", dsInfo.DatasourceID) span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetTag("org_id", dsInfo.OrgID) span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish() defer span.End()
tracer.Inject(ctx, req.Header, span)
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
dataResponse.Error = err
return dataResponse
}
azlog.Debug("AzureMonitor", "Request ApiURL", req.URL.String()) azlog.Debug("AzureMonitor", "Request ApiURL", req.URL.String())
azlog.Debug("AzureMonitor", "Target", query.Target) 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-plugin-sdk-go/backend/resource/httpadapter"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log" "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"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@@ -30,7 +31,7 @@ var (
legendKeyFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`) 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{} proxy := &httpServiceProxy{}
executors := map[string]azDatasourceExecutor{ executors := map[string]azDatasourceExecutor{
azureMonitor: &AzureMonitorDatasource{proxy: proxy}, azureMonitor: &AzureMonitorDatasource{proxy: proxy},
@@ -45,6 +46,7 @@ func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, p
Cfg: cfg, Cfg: cfg,
im: im, im: im,
executors: executors, executors: executors,
tracer: tracer,
} }
mux := s.newMux() mux := s.newMux()
@@ -71,6 +73,7 @@ type Service struct {
Cfg *setting.Cfg Cfg *setting.Cfg
im instancemgmt.InstanceManager im instancemgmt.InstanceManager
executors map[string]azDatasourceExecutor executors map[string]azDatasourceExecutor
tracer tracing.Tracer
} }
type azureMonitorSettings struct { type azureMonitorSettings struct {
@@ -162,7 +165,7 @@ func NewInstanceSettings(cfg *setting.Cfg, clientProvider httpclient.Provider, e
} }
type azDatasourceExecutor interface { 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) resourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client)
} }
@@ -194,7 +197,7 @@ func (s *Service) newMux() *datasource.QueryTypeMux {
if !ok { if !ok {
return nil, fmt.Errorf("missing service for %s", dst) 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 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"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "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/setting"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials"
"github.com/stretchr/testify/require" "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) 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 { if client == nil {
f.t.Errorf("The HTTP client for %s is missing", f.queryType) f.t.Errorf("The HTTP client for %s is missing", f.queryType)
} else { } else {

View File

@@ -12,8 +12,9 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
"github.com/opentracing/opentracing-go" "go.opentelemetry.io/otel/attribute"
"golang.org/x/net/context/ctxhttp" "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, 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() result := backend.NewQueryDataResponse()
queries, err := e.buildQueries(originalQueries, dsInfo) queries, err := e.buildQueries(originalQueries, dsInfo)
@@ -47,7 +49,7 @@ func (e *InsightsAnalyticsDatasource) executeTimeSeriesQuery(ctx context.Context
} }
for _, query := range queries { 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 return result, nil
@@ -86,7 +88,8 @@ func (e *InsightsAnalyticsDatasource) buildQueries(queries []backend.DataQuery,
return iaQueries, nil 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{} dataResponse := backend.DataResponse{}
dataResponseError := func(err error) 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.Path = path.Join(req.URL.Path, "query")
req.URL.RawQuery = query.Params.Encode() req.URL.RawQuery = query.Params.Encode()
span, ctx := opentracing.StartSpanFromContext(ctx, "application insights analytics query") ctx, span := tracer.Start(ctx, "application insights analytics query")
span.SetTag("target", query.Target) span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
span.SetTag("datasource_id", dsInfo.DatasourceID) span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID))
span.SetTag("org_id", dsInfo.OrgID) span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID))
defer span.Finish() defer span.End()
tracer.Inject(ctx, req.Header, span)
err = opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
if err != nil { if err != nil {
azlog.Warn("failed to inject global tracer") 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 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 { if err != nil {
return resp, err return resp, err
} }

View File

@@ -24,6 +24,7 @@ import (
"github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log" "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"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
@@ -72,12 +73,13 @@ const (
) )
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store, func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store,
dsService *datasources.Service) *Service { dsService *datasources.Service, tracer tracing.Tracer) *Service {
s := &Service{ s := &Service{
httpClientProvider: httpClientProvider, httpClientProvider: httpClientProvider,
cfg: cfg, cfg: cfg,
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)), im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
dsService: dsService, dsService: dsService,
tracer: tracer,
} }
mux := http.NewServeMux() mux := http.NewServeMux()
@@ -139,6 +141,7 @@ type Service struct {
cfg *setting.Cfg cfg *setting.Cfg
im instancemgmt.InstanceManager im instancemgmt.InstanceManager
dsService *datasources.Service dsService *datasources.Service
tracer tracing.Tracer
} }
type QueryModel struct { type QueryModel struct {
@@ -263,7 +266,7 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query
} }
for _, queryExecutor := range queryExecutors { 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 { if err != nil {
return resp, err 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/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "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, 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{} dr := &backend.DataResponse{}
projectName := timeSeriesFilter.ProjectName projectName := timeSeriesFilter.ProjectName
if projectName == "" { if projectName == "" {
@@ -55,22 +56,15 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) run(ctx context.Context
} }
} }
span, ctx := opentracing.StartSpanFromContext(ctx, "cloudMonitoring query") ctx, span := tracer.Start(ctx, "cloudMonitoring query")
span.SetTag("target", timeSeriesFilter.Target) span.SetAttributes("target", timeSeriesFilter.Target, attribute.Key("target").String(timeSeriesFilter.Target))
span.SetTag("from", req.Queries[0].TimeRange.From) span.SetAttributes("from", req.Queries[0].TimeRange.From, attribute.Key("from").String(req.Queries[0].TimeRange.From.String()))
span.SetTag("until", req.Queries[0].TimeRange.To) span.SetAttributes("until", req.Queries[0].TimeRange.To, attribute.Key("until").String(req.Queries[0].TimeRange.To.String()))
span.SetTag("datasource_id", dsInfo.id) span.SetAttributes("datasource_id", dsInfo.id, attribute.Key("datasource_id").Int64(dsInfo.id))
span.SetTag("org_id", req.PluginContext.OrgID) span.SetAttributes("org_id", req.PluginContext.OrgID, attribute.Key("org_id").Int64(req.PluginContext.OrgID))
defer span.Finish() defer span.End()
tracer.Inject(ctx, r.Header, span)
if err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header)); err != nil {
dr.Error = err
return dr, cloudMonitoringResponse{}, "", nil
}
r = r.WithContext(ctx) r = r.WithContext(ctx)
res, err := dsInfo.services[cloudMonitor].client.Do(r) res, err := dsInfo.services[cloudMonitor].client.Do(r)

View File

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

View File

@@ -6,11 +6,12 @@ import (
"time" "time"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/infra/tracing"
) )
type ( type (
cloudMonitoringQueryExecutor interface { 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) *backend.DataResponse, cloudMonitoringResponse, string, error)
parseResponse(dr *backend.DataResponse, data cloudMonitoringResponse, executedQueryString string) error parseResponse(dr *backend.DataResponse, data cloudMonitoringResponse, executedQueryString string) error
parseToAnnotations(dr *backend.DataResponse, data cloudMonitoringResponse, title, text string) error parseToAnnotations(dr *backend.DataResponse, data cloudMonitoringResponse, title, text string) error

View File

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

View File

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

View File

@@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/grafana/grafana-plugin-sdk-go/experimental" "github.com/grafana/grafana-plugin-sdk-go/experimental"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/prometheus/client_golang/api" "github.com/prometheus/client_golang/api"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1" apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -109,7 +110,10 @@ func testScenario(t *testing.T, name string) {
api, err := makeMockedApi(responseBytes) api, err := makeMockedApi(responseBytes)
require.NoError(t, err) 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.NoError(t, err)
require.Len(t, result.Responses, 1) 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-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log" "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"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@@ -32,15 +33,17 @@ const pluginID = "prometheus"
type Service struct { type Service struct {
intervalCalculator intervalv2.Calculator intervalCalculator intervalv2.Calculator
im instancemgmt.InstanceManager 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") plog.Debug("initializing")
im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)) im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider))
s := &Service{ s := &Service{
intervalCalculator: intervalv2.NewCalculator(), intervalCalculator: intervalv2.NewCalculator(),
im: im, im: im,
tracer: tracer,
} }
factory := coreplugin.New(backend.ServeOpts{ 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/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/intervalv2" "github.com/grafana/grafana/pkg/tsdb/intervalv2"
"github.com/opentracing/opentracing-go"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1" apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"go.opentelemetry.io/otel/attribute"
) )
//Internal interval and range variables //Internal interval and range variables
@@ -47,7 +47,7 @@ const (
ExemplarQueryType TimeSeriesQueryType = "exemplar" 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{ result := backend.QueryDataResponse{
Responses: backend.Responses{}, Responses: backend.Responses{},
} }
@@ -55,11 +55,11 @@ func runQueries(ctx context.Context, client apiv1.API, queries []*PrometheusQuer
for _, query := range queries { for _, query := range queries {
plog.Debug("Sending query", "start", query.Start, "end", query.End, "step", query.Step, "query", query.Expr) plog.Debug("Sending query", "start", query.Start, "end", query.End, "step", query.Step, "query", query.Expr)
span, ctx := opentracing.StartSpanFromContext(ctx, "datasource.prometheus") ctx, span := s.tracer.Start(ctx, "datasource.prometheus")
span.SetTag("expr", query.Expr) span.SetAttributes("expr", query.Expr, attribute.Key("expr").String(query.Expr))
span.SetTag("start_unixnano", query.Start.UnixNano()) span.SetAttributes("start_unixnano", query.Start, attribute.Key("start_unixnano").Int64(query.Start.UnixNano()))
span.SetTag("stop_unixnano", query.End.UnixNano()) span.SetAttributes("stop_unixnano", query.End, attribute.Key("stop_unixnano").Int64(query.End.UnixNano()))
defer span.Finish() defer span.End()
response := make(map[TimeSeriesQueryType]interface{}) response := make(map[TimeSeriesQueryType]interface{})
@@ -128,7 +128,7 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query
return &result, err return &result, err
} }
return runQueries(ctx, client, queries) return s.runQueries(ctx, client, queries)
} }
func formatLegend(metric model.Metric, query *PrometheusQuery) string { func formatLegend(metric model.Metric, query *PrometheusQuery) string {