From 15d41698138f250b85ccd423f2f252dea7cf3c93 Mon Sep 17 00:00:00 2001 From: Domas Date: Tue, 2 May 2023 12:10:56 +0300 Subject: [PATCH] Frontend logging: Remove Sentry javascript agent support (#67493) * remove Sentry * fix sourcemap resolve --- .github/renovate.json5 | 6 - conf/defaults.ini | 11 +- conf/sample.ini | 15 +- .../setup-grafana/configure-grafana/_index.md | 20 +- go.mod | 1 - go.sum | 4 - package.json | 3 - packages/grafana-data/src/types/config.ts | 13 - packages/grafana-runtime/package.json | 1 - packages/grafana-runtime/src/config.ts | 6 - .../grafana-runtime/src/services/EchoSrv.ts | 1 - packages/grafana-runtime/src/utils/logging.ts | 41 +-- packages/grafana-ui/package.json | 1 - .../ErrorBoundary/ErrorBoundary.test.tsx | 11 +- .../ErrorBoundary/ErrorBoundary.tsx | 2 - pkg/api/dtos/frontend_settings.go | 1 - pkg/api/dtos/index.go | 2 - pkg/api/frontend_logging.go | 76 +---- pkg/api/frontend_logging_test.go | 290 +----------------- .../grafana_javascript_agent.go | 2 + .../grafana_javascript_agent_sourcemaps.go | 30 +- pkg/api/frontendlogging/sentry.go | 108 ------- pkg/api/frontendlogging/source_maps.go | 9 +- pkg/api/frontendsettings.go | 1 - pkg/api/index.go | 1 - pkg/setting/setting.go | 4 - .../setting_grafana_javascript_agent.go | 21 +- pkg/setting/setting_sentry.go | 28 -- public/app/app.ts | 10 - public/app/core/services/echo/EchoSrv.ts | 8 +- .../backends/sentry/SentryBackend.test.ts | 143 --------- .../echo/backends/sentry/SentryBackend.ts | 66 ---- .../CustomEndpointTransport.test.ts | 133 -------- .../transports/CustomEndpointTransport.ts | 151 --------- .../sentry/transports/EchoSrvTransport.ts | 26 -- .../services/echo/backends/sentry/types.ts | 16 - .../features/alerting/unified/Analytics.ts | 2 +- yarn.lock | 71 +---- 38 files changed, 47 insertions(+), 1288 deletions(-) delete mode 100644 pkg/api/frontendlogging/sentry.go delete mode 100644 pkg/setting/setting_sentry.go delete mode 100644 public/app/core/services/echo/backends/sentry/SentryBackend.test.ts delete mode 100644 public/app/core/services/echo/backends/sentry/SentryBackend.ts delete mode 100644 public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.test.ts delete mode 100644 public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.ts delete mode 100644 public/app/core/services/echo/backends/sentry/transports/EchoSrvTransport.ts delete mode 100644 public/app/core/services/echo/backends/sentry/types.ts diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 83033b38060..bdb71164b90 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -16,12 +16,6 @@ "systemjs", "ts-loader", // we should remove ts-loader and use babel-loader instead "ora", // we should bump this once we move to esm modules - - // Sentry deprecated in favor of Grafana Faro for frontend logging. - // Major effort required to upgrade to latest Sentry, not worthwhile - "@sentry/browser", - "@sentry/types", - "@sentry/utils", ], "includePaths": ["package.json", "packages/**"], "ignorePaths": ["packages/grafana-toolkit/package.json", "emails/**", "plugins-bundled/**", "**/mocks/**"], diff --git a/conf/defaults.ini b/conf/defaults.ini index 3e585172afb..0176519d9e1 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -872,21 +872,12 @@ facility = tag = [log.frontend] -# Should Sentry javascript agent be initialized +# Should Faro javascript agent be initialized enabled = false -# Defines which provider to use sentry or grafana -provider = sentry - -# Sentry DSN if you want to send events to Sentry. -sentry_dsn = - # Custom HTTP endpoint to send events to. Default will log the events to stdout. custom_endpoint = -# Rate of events to be reported to Sentry between 0 (none) and 1 (all), float -sample_rate = 1.0 - # Requests per second limit enforced per an extended period, for Grafana backend log ingestion endpoint (/log). log_endpoint_requests_per_second_limit = 3 diff --git a/conf/sample.ini b/conf/sample.ini index ced98a3a829..29bf51e332b 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -844,20 +844,11 @@ ;tag = [log.frontend] -# Should Sentry javascript agent be initialized +# Should Faro javascript agent be initialized ;enabled = false -# Defines which provider to use, default is Sentry -;provider = sentry - -# Sentry DSN if you want to send events to Sentry. -;sentry_dsn = - -# Custom HTTP endpoint to send events captured by the Sentry agent to. Default will log the events to stdout. -;custom_endpoint = /log - -# Rate of events to be reported between 0 (none) and 1 (all), float -;sample_rate = 1.0 +# Custom HTTP endpoint to send events to. Default will log the events to stdout. +;custom_endpoint = /log-grafana-javascript-agent # Requests per second limit enforced an extended period, for Grafana backend log ingestion endpoint (/log). ;log_endpoint_requests_per_second_limit = 3 diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 13b190a52b9..7eb24453425 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1284,31 +1284,19 @@ Syslog tag. By default, the process's `argv[0]` is used. ### enabled -Sentry javascript agent is initialized. Default is `false`. - -### provider - -Defines which provider to use `sentry` or `grafana`. Default is `sentry` - -### sentry_dsn - -Sentry DSN if you want to send events to Sentry +Faro javascript agent is initialized. Default is `false`. ### custom_endpoint -Custom HTTP endpoint to send events captured by the Sentry agent to. Default, `/log`, will log the events to stdout. - -### sample_rate - -Rate of events to be reported between `0` (none) and `1` (all, default), float. +Custom HTTP endpoint to send events captured by the Faro agent to. Default, `/log-grafana-javascript-agent`, will log the events to stdout. ### log_endpoint_requests_per_second_limit -Requests per second limit enforced per an extended period, for Grafana backend log ingestion endpoint, `/log`. Default is `3`. +Requests per second limit enforced per an extended period, for Grafana backend log ingestion endpoint, `/log-grafana-javascript-agent`. Default is `3`. ### log_endpoint_burst_limit -Maximum requests accepted per short interval of time for Grafana backend log ingestion endpoint, `/log`. Default is `15`. +Maximum requests accepted per short interval of time for Grafana backend log ingestion endpoint, `/log-grafana-javascript-agent`. Default is `15`. ### instrumentations_errors_enabled diff --git a/go.mod b/go.mod index fe506135986..fb604764e12 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,6 @@ require ( github.com/crewjam/saml v0.4.12 github.com/fatih/color v1.15.0 github.com/gchaincl/sqlhooks v1.3.0 - github.com/getsentry/sentry-go v0.13.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-ldap/ldap/v3 v3.4.4 github.com/go-openapi/strfmt v0.21.7 diff --git a/go.sum b/go.sum index d60b20df894..a53bba85ff9 100644 --- a/go.sum +++ b/go.sum @@ -801,8 +801,6 @@ github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebK github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getkin/kin-openapi v0.115.0 h1:c8WHRLVY3G8m9jQTy0/DnIuljgRwTCB5twZytQS4JyU= github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= -github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo= -github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= @@ -822,7 +820,6 @@ github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -1962,7 +1959,6 @@ github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= diff --git a/package.json b/package.json index 0c1adddfd61..b3ac921b2f2 100644 --- a/package.json +++ b/package.json @@ -287,9 +287,6 @@ "@react-stately/tree": "3.3.1", "@reduxjs/toolkit": "1.9.3", "@remix-run/router": "^1.5.0", - "@sentry/browser": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", "@testing-library/react-hooks": "^8.0.1", "@types/react-resizable": "3.0.3", "@types/trusted-types": "2.0.3", diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index d7ac6a94760..7331280cc1a 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -46,18 +46,6 @@ export interface LicenseInfo { trialExpiry?: number; } -/** - * Describes Sentry integration config - * - * @public - */ -export interface SentryConfig { - enabled: boolean; - dsn: string; - customEndpoint: string; - sampleRate: number; -} - /** * Describes GrafanaJavascriptAgentConfig integration config * @@ -209,7 +197,6 @@ export interface GrafanaConfig { licenseInfo: LicenseInfo; http2Enabled: boolean; dateFormats?: SystemDateFormatSettings; - sentry: SentryConfig; grafanaJavascriptAgent: GrafanaJavascriptAgentConfig; customTheme?: any; geomapDefaultBaseLayer?: MapLayerOptions; diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index aa97f210716..8d8cd121b1c 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -41,7 +41,6 @@ "@grafana/e2e-selectors": "10.1.0-pre", "@grafana/faro-web-sdk": "1.0.2", "@grafana/ui": "10.1.0-pre", - "@sentry/browser": "6.19.7", "history": "4.10.1", "lodash": "4.17.21", "rxjs": "7.8.0", diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 0f232e2408f..39748fd3546 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -103,12 +103,6 @@ export class GrafanaBootConfig implements GrafanaConfig { supportBundlesEnabled = false; http2Enabled = false; dateFormats?: SystemDateFormatSettings; - sentry = { - enabled: false, - dsn: '', - customEndpoint: '', - sampleRate: 1, - }; grafanaJavascriptAgent = { enabled: false, customEndpoint: '', diff --git a/packages/grafana-runtime/src/services/EchoSrv.ts b/packages/grafana-runtime/src/services/EchoSrv.ts index 4bde55d82ac..d2dc93920d3 100644 --- a/packages/grafana-runtime/src/services/EchoSrv.ts +++ b/packages/grafana-runtime/src/services/EchoSrv.ts @@ -78,7 +78,6 @@ export interface EchoEvent { export enum EchoEventType { Performance = 'performance', MetaAnalytics = 'meta-analytics', - Sentry = 'sentry', Pageview = 'pageview', Interaction = 'interaction', ExperimentView = 'experimentview', diff --git a/packages/grafana-runtime/src/utils/logging.ts b/packages/grafana-runtime/src/utils/logging.ts index 5443f264cba..a6c26c717dd 100644 --- a/packages/grafana-runtime/src/utils/logging.ts +++ b/packages/grafana-runtime/src/utils/logging.ts @@ -1,76 +1,54 @@ -import { captureMessage, captureException, Severity as LogLevel } from '@sentry/browser'; - -import { faro, LogLevel as GrafanaLogLevel } from '@grafana/faro-web-sdk'; +import { faro, LogLevel } from '@grafana/faro-web-sdk'; import { config } from '../config'; export { LogLevel }; -// a bit stricter than what Sentry allows type Contexts = Record>>; /** - * Log a message at INFO level. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry - * + * Log a message at INFO level * @public */ export function logInfo(message: string, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushLog([message], { - level: GrafanaLogLevel.INFO, + level: LogLevel.INFO, context: contexts, }); } - if (config.sentry.enabled) { - captureMessage(message, { - level: LogLevel.Info, - contexts, - }); - } } /** - * Log a message at WARNING level. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry + * Log a message at WARNING level * * @public */ export function logWarning(message: string, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushLog([message], { - level: GrafanaLogLevel.WARN, + level: LogLevel.WARN, context: contexts, }); } - if (config.sentry.enabled) { - captureMessage(message, { - level: LogLevel.Warning, - contexts, - }); - } } /** - * Log a message at DEBUG level. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry + * Log a message at DEBUG level * * @public */ export function logDebug(message: string, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushLog([message], { - level: GrafanaLogLevel.DEBUG, + level: LogLevel.DEBUG, context: contexts, }); } - if (config.sentry.enabled) { - captureMessage(message, { - level: LogLevel.Debug, - contexts, - }); - } } /** - * Log an error. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry + * Log an error * * @public */ @@ -78,7 +56,4 @@ export function logError(err: Error, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushError(err); } - if (config.sentry.enabled) { - captureException(err, { contexts }); - } } diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index ccb8b3027f3..1acc87faa7e 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -63,7 +63,6 @@ "@react-aria/overlays": "3.10.1", "@react-aria/utils": "3.13.1", "@react-stately/menu": "3.4.1", - "@sentry/browser": "6.19.7", "ansicolor": "1.1.100", "calculate-size": "1.1.1", "classnames": "2.3.2", diff --git a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.test.tsx b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.test.tsx index df5241e46de..d2af63af611 100644 --- a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.test.tsx +++ b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.test.tsx @@ -1,4 +1,3 @@ -import { captureException } from '@sentry/browser'; import { render, screen } from '@testing-library/react'; import React, { FC } from 'react'; @@ -6,7 +5,6 @@ import { faro } from '@grafana/faro-web-sdk'; import { ErrorBoundary } from './ErrorBoundary'; -jest.mock('@sentry/browser'); jest.mock('@grafana/faro-web-sdk', () => ({ faro: { api: { @@ -31,7 +29,7 @@ describe('ErrorBoundary', () => { consoleSpy.mockRestore(); }); - it('should catch error and report it to sentry, including react component stack in context', async () => { + it('should catch error and report it to Faro', async () => { const problem = new Error('things went terribly wrong'); render( @@ -46,13 +44,6 @@ describe('ErrorBoundary', () => { ); await screen.findByText(problem.message); - expect(captureException).toHaveBeenCalledTimes(1); - const [error, context] = (captureException as jest.Mock).mock.calls[0]; - expect(error).toBe(problem); - expect(context).toHaveProperty('contexts'); - expect(context.contexts).toHaveProperty('react'); - expect(context.contexts.react).toHaveProperty('componentStack'); - expect(context.contexts.react.componentStack).toMatch(/^\s+at ErrorThrower (.*)\s+at ErrorBoundary (.*)\s*$/); expect(faro.api.pushError).toHaveBeenCalledTimes(1); expect((faro.api.pushError as jest.Mock).mock.calls[0][0]).toBe(problem); }); diff --git a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx index 2afe61423cc..808ed22c4ca 100644 --- a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -1,4 +1,3 @@ -import { captureException } from '@sentry/browser'; import React, { PureComponent, ReactNode, ComponentType } from 'react'; import { faro } from '@grafana/faro-web-sdk'; @@ -38,7 +37,6 @@ export class ErrorBoundary extends PureComponent { }; componentDidCatch(error: Error, errorInfo: ErrorInfo) { - captureException(error, { contexts: { react: { componentStack: errorInfo.componentStack } } }); faro?.api?.pushError(error); this.setState({ error, errorInfo }); diff --git a/pkg/api/dtos/frontend_settings.go b/pkg/api/dtos/frontend_settings.go index 38722a511e3..cf46d35bbed 100644 --- a/pkg/api/dtos/frontend_settings.go +++ b/pkg/api/dtos/frontend_settings.go @@ -185,7 +185,6 @@ type FrontendSettingsDTO struct { RendererVersion string `json:"rendererVersion"` SecretsManagerPluginEnabled bool `json:"secretsManagerPluginEnabled"` Http2Enabled bool `json:"http2Enabled"` - Sentry setting.Sentry `json:"sentry"` GrafanaJavascriptAgent setting.GrafanaJavascriptAgent `json:"grafanaJavascriptAgent"` PluginCatalogURL string `json:"pluginCatalogURL"` PluginAdminEnabled bool `json:"pluginAdminEnabled"` diff --git a/pkg/api/dtos/index.go b/pkg/api/dtos/index.go index 1a25f45a796..99ffc5de835 100644 --- a/pkg/api/dtos/index.go +++ b/pkg/api/dtos/index.go @@ -4,7 +4,6 @@ import ( "html/template" "github.com/grafana/grafana/pkg/services/navtree" - "github.com/grafana/grafana/pkg/setting" ) type IndexViewData struct { @@ -27,7 +26,6 @@ type IndexViewData struct { FavIcon template.URL AppleTouchIcon template.URL AppTitle string - Sentry *setting.Sentry ContentDeliveryURL string LoadingLogo template.URL CSPContent string diff --git a/pkg/api/frontend_logging.go b/pkg/api/frontend_logging.go index 4d69efc14be..470a77cb4e0 100644 --- a/pkg/api/frontend_logging.go +++ b/pkg/api/frontend_logging.go @@ -4,7 +4,6 @@ import ( "net/http" "time" - "github.com/getsentry/sentry-go" "golang.org/x/time/rate" "github.com/grafana/grafana/pkg/api/frontendlogging" @@ -16,51 +15,8 @@ var frontendLogger = log.New("frontend") type frontendLogMessageHandler func(hs *HTTPServer, c *web.Context) -const sentryLogEndpointPath = "/log" const grafanaJavascriptAgentEndpointPath = "/log-grafana-javascript-agent" -/** @deprecated will be removed in the next major version */ -func NewFrontendLogMessageHandler(store *frontendlogging.SourceMapStore) frontendLogMessageHandler { - return func(hs *HTTPServer, c *web.Context) { - event := frontendlogging.FrontendSentryEvent{} - if err := web.Bind(c.Req, &event); err != nil { - c.Resp.WriteHeader(http.StatusBadRequest) - _, err = c.Resp.Write([]byte("bad request data")) - if err != nil { - hs.log.Error("could not write to response", "err", err) - } - return - } - - var msg = "unknown" - - if len(event.Message) > 0 { - msg = event.Message - } else if event.Exception != nil && len(event.Exception.Values) > 0 { - msg = event.Exception.Values[0].FmtMessage() - } - - var ctx = event.ToLogContext(store) - - switch event.Level { - case sentry.LevelError: - frontendLogger.Error(msg, ctx...) - case sentry.LevelWarning: - frontendLogger.Warn(msg, ctx...) - case sentry.LevelDebug: - frontendLogger.Debug(msg, ctx...) - default: - frontendLogger.Info(msg, ctx...) - } - - c.Resp.WriteHeader(http.StatusAccepted) - _, err := c.Resp.Write([]byte("OK")) - if err != nil { - hs.log.Error("could not write to response", "err", err) - } - } -} - func GrafanaJavascriptAgentLogMessageHandler(store *frontendlogging.SourceMapStore) frontendLogMessageHandler { return func(hs *HTTPServer, c *web.Context) { event := frontendlogging.FrontendGrafanaJavascriptAgentEvent{} @@ -143,9 +99,9 @@ func GrafanaJavascriptAgentLogMessageHandler(store *frontendlogging.SourceMapSto // this is to avoid reporting errors in case config was changes but there are browser // sessions still open with older config func (hs *HTTPServer) frontendLogEndpoints() web.Handler { - if !(hs.Cfg.GrafanaJavascriptAgent.Enabled || hs.Cfg.Sentry.Enabled) { + if !(hs.Cfg.GrafanaJavascriptAgent.Enabled) { return func(ctx *web.Context) { - if ctx.Req.Method == http.MethodPost && (ctx.Req.URL.Path == sentryLogEndpointPath || ctx.Req.URL.Path == grafanaJavascriptAgentEndpointPath) { + if ctx.Req.Method == http.MethodPost && ctx.Req.URL.Path == grafanaJavascriptAgentEndpointPath { ctx.Resp.WriteHeader(http.StatusAccepted) _, err := ctx.Resp.Write([]byte("OK")) if err != nil { @@ -156,33 +112,11 @@ func (hs *HTTPServer) frontendLogEndpoints() web.Handler { } sourceMapStore := frontendlogging.NewSourceMapStore(hs.Cfg, hs.pluginStaticRouteResolver, frontendlogging.ReadSourceMapFromFS) - - var rateLimiter *rate.Limiter - var handler frontendLogMessageHandler - handlerEndpoint := "" - dummyEndpoint := "" - - if hs.Cfg.GrafanaJavascriptAgent.Enabled { - rateLimiter = rate.NewLimiter(rate.Limit(hs.Cfg.GrafanaJavascriptAgent.EndpointRPS), hs.Cfg.GrafanaJavascriptAgent.EndpointBurst) - handler = GrafanaJavascriptAgentLogMessageHandler(sourceMapStore) - handlerEndpoint = grafanaJavascriptAgentEndpointPath - dummyEndpoint = sentryLogEndpointPath - } else { - rateLimiter = rate.NewLimiter(rate.Limit(hs.Cfg.Sentry.EndpointRPS), hs.Cfg.Sentry.EndpointBurst) - handler = NewFrontendLogMessageHandler(sourceMapStore) - handlerEndpoint = sentryLogEndpointPath - dummyEndpoint = grafanaJavascriptAgentEndpointPath - } + rateLimiter := rate.NewLimiter(rate.Limit(hs.Cfg.GrafanaJavascriptAgent.EndpointRPS), hs.Cfg.GrafanaJavascriptAgent.EndpointBurst) + handler := GrafanaJavascriptAgentLogMessageHandler(sourceMapStore) return func(ctx *web.Context) { - if ctx.Req.Method == http.MethodPost && ctx.Req.URL.Path == dummyEndpoint { - ctx.Resp.WriteHeader(http.StatusAccepted) - _, err := ctx.Resp.Write([]byte("OK")) - if err != nil { - hs.log.Error("could not write to response", "err", err) - } - } - if ctx.Req.Method == http.MethodPost && ctx.Req.URL.Path == handlerEndpoint { + if ctx.Req.Method == http.MethodPost && ctx.Req.URL.Path == grafanaJavascriptAgentEndpointPath { if !rateLimiter.AllowN(time.Now(), 1) { ctx.Resp.WriteHeader(http.StatusTooManyRequests) return diff --git a/pkg/api/frontend_logging_test.go b/pkg/api/frontend_logging_test.go index 5771bb4465c..f20687481fd 100644 --- a/pkg/api/frontend_logging_test.go +++ b/pkg/api/frontend_logging_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "github.com/getsentry/sentry-go" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/stretchr/testify/assert" @@ -30,79 +29,6 @@ type SourceMapReadRecord struct { type logScenarioFunc func(c *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) -func logSentryEventScenario(t *testing.T, desc string, event frontendlogging.FrontendSentryEvent, fn logScenarioFunc) { - t.Run(desc, func(t *testing.T) { - var logcontent = make(map[string]interface{}) - logcontent["logger"] = "frontend" - newfrontendLogger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error { - for i := 0; i < len(keyvals); i += 2 { - logcontent[keyvals[i].(string)] = keyvals[i+1] - } - return nil - })) - - origHandler := frontendLogger.GetLogger() - frontendLogger.Swap(level.NewFilter(newfrontendLogger, level.AllowInfo())) - sourceMapReads := []SourceMapReadRecord{} - - t.Cleanup(func() { - frontendLogger.Swap(origHandler) - }) - - sc := setupScenarioContext(t, "/log") - - cdnRootURL, e := url.Parse("https://storage.googleapis.com/grafana-static-assets") - require.NoError(t, e) - - cfg := &setting.Cfg{ - StaticRootPath: "/staticroot", - CDNRootURL: cdnRootURL, - } - - readSourceMap := func(dir string, path string) ([]byte, error) { - sourceMapReads = append(sourceMapReads, SourceMapReadRecord{ - dir: dir, - path: path, - }) - if strings.Contains(path, "error") { - return nil, errors.New("epic hard drive failure") - } - if strings.HasSuffix(path, "foo.js.map") { - f, err := os.ReadFile("./frontendlogging/test-data/foo.js.map") - require.NoError(t, err) - return f, nil - } - return nil, os.ErrNotExist - } - - // fake plugin route so we will try to find a source map there - pm := fakePluginStaticRouteResolver{ - routes: []*plugins.StaticRoute{ - { - Directory: "/usr/local/telepathic-panel", - PluginID: "telepathic", - }, - }, - } - - sourceMapStore := frontendlogging.NewSourceMapStore(cfg, &pm, readSourceMap) - - loggingHandler := NewFrontendLogMessageHandler(sourceMapStore) - - handler := routing.Wrap(func(c *contextmodel.ReqContext) response.Response { - sc.context = c - c.Req.Body = mockRequestBody(event) - c.Req.Header.Add("Content-Type", "application/json") - loggingHandler(nil, c.Context) - return response.Success("ok") - }) - - sc.m.Post(sc.url, handler) - sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec() - fn(sc, logcontent, sourceMapReads) - }) -} - func logGrafanaJavascriptAgentEventScenario(t *testing.T, desc string, event frontendlogging.FrontendGrafanaJavascriptAgentEvent, fn logScenarioFunc) { t.Run(desc, func(t *testing.T) { var logcontent = make(map[string]interface{}) @@ -176,216 +102,6 @@ func logGrafanaJavascriptAgentEventScenario(t *testing.T, desc string, event fro }) } -func TestFrontendLoggingEndpointSentry(t *testing.T) { - ts, err := time.Parse("2006-01-02T15:04:05.000Z", "2020-10-22T06:29:29.078Z") - require.NoError(t, err) - - t.Run("FrontendLoggingEndpoint", func(t *testing.T) { - request := sentry.Request{ - URL: "http://localhost:3000/", - Headers: map[string]string{ - "User-Agent": "Chrome", - }, - } - - user := sentry.User{ - Email: "geralt@kaermorhen.com", - ID: "45", - } - - event := sentry.Event{ - EventID: "123", - Level: sentry.LevelError, - Request: &request, - Timestamp: ts, - } - - errorEvent := frontendlogging.FrontendSentryEvent{ - Event: &event, - Exception: &frontendlogging.FrontendSentryException{ - Values: []frontendlogging.FrontendSentryExceptionValue{ - { - Type: "UserError", - Value: "Please replace user and try again", - Stacktrace: sentry.Stacktrace{ - Frames: []sentry.Frame{ - { - Function: "foofn", - Filename: "foo.js", - Lineno: 123, - Colno: 23, - }, - { - Function: "barfn", - Filename: "bar.js", - Lineno: 113, - Colno: 231, - }, - }, - }, - }, - }, - }, - } - - logSentryEventScenario(t, "Should log received error event", errorEvent, - func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, http.StatusAccepted, sc.resp.Code) - assertContextContains(t, logs, "logger", "frontend") - assertContextContains(t, logs, "url", errorEvent.Request.URL) - assertContextContains(t, logs, "user_agent", errorEvent.Request.Headers["User-Agent"]) - assertContextContains(t, logs, "event_id", errorEvent.EventID) - assertContextContains(t, logs, "original_timestamp", errorEvent.Timestamp) - assertContextContains(t, logs, "stacktrace", `UserError: Please replace user and try again - at foofn (foo.js:123:23) - at barfn (bar.js:113:231)`) - assert.NotContains(t, logs, "context") - }) - - messageEvent := frontendlogging.FrontendSentryEvent{ - Event: &sentry.Event{ - EventID: "123", - Level: sentry.LevelInfo, - Request: &request, - Timestamp: ts, - Message: "hello world", - User: user, - }, - Exception: nil, - } - - logSentryEventScenario(t, "Should log received message event", messageEvent, - func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, http.StatusAccepted, sc.resp.Code) - assert.Len(t, logs, 10) - assertContextContains(t, logs, "logger", "frontend") - assertContextContains(t, logs, "msg", "hello world") - assertContextContains(t, logs, level.Key().(string), level.InfoValue()) - assertContextContains(t, logs, "logger", "frontend") - assertContextContains(t, logs, "url", messageEvent.Request.URL) - assertContextContains(t, logs, "user_agent", messageEvent.Request.Headers["User-Agent"]) - assertContextContains(t, logs, "event_id", messageEvent.EventID) - assertContextContains(t, logs, "original_timestamp", messageEvent.Timestamp) - assert.NotContains(t, logs, "stacktrace") - assert.NotContains(t, logs, "context") - assertContextContains(t, logs, "user_email", user.Email) - assertContextContains(t, logs, "user_id", user.ID) - }) - - eventWithContext := frontendlogging.FrontendSentryEvent{ - Event: &sentry.Event{ - EventID: "123", - Level: sentry.LevelInfo, - Request: &request, - Timestamp: ts, - Message: "hello world", - User: user, - Contexts: map[string]interface{}{ - "foo": map[string]interface{}{ - "one": "two", - "three": 4, - }, - "bar": "baz", - }, - }, - Exception: nil, - } - - logSentryEventScenario(t, "Should log event context", eventWithContext, - func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, http.StatusAccepted, sc.resp.Code) - assertContextContains(t, logs, "context_foo_one", "two") - assertContextContains(t, logs, "context_foo_three", "4") - assertContextContains(t, logs, "context_bar", "baz") - }) - - errorEventForSourceMapping := frontendlogging.FrontendSentryEvent{ - Event: &event, - Exception: &frontendlogging.FrontendSentryException{ - Values: []frontendlogging.FrontendSentryExceptionValue{ - { - Type: "UserError", - Value: "Please replace user and try again", - Stacktrace: sentry.Stacktrace{ - Frames: []sentry.Frame{ - { - Function: "foofn", - Filename: "http://localhost:3000/public/build/moo/foo.js", // source map found and mapped, core - Lineno: 2, - Colno: 5, - }, - { - Function: "foofn", - Filename: "http://localhost:3000/public/plugins/telepathic/foo.js", // plugin, source map found and mapped - Lineno: 3, - Colno: 10, - }, - { - Function: "explode", - Filename: "http://localhost:3000/public/build/error.js", // reading source map throws error - Lineno: 3, - Colno: 10, - }, - { - Function: "wat", - Filename: "http://localhost:3000/public/build/bar.js", // core, but source map not found on fs - Lineno: 3, - Colno: 10, - }, - { - Function: "nope", - Filename: "http://localhost:3000/baz.js", // not core or plugin, wont even attempt to get source map - Lineno: 3, - Colno: 10, - }, - { - Function: "fake", - Filename: "http://localhost:3000/public/build/../../secrets.txt", // path will be sanitized - Lineno: 3, - Colno: 10, - }, - { - Function: "cdn", - Filename: "https://storage.googleapis.com/grafana-static-assets/grafana-oss/pre-releases/7.5.0-11925pre/public/build/foo.js", // source map found and mapped - Lineno: 3, - Colno: 10, - }, - }, - }, - }, - }, - }, - } - - logSentryEventScenario(t, "Should load sourcemap and transform stacktrace line when possible", - errorEventForSourceMapping, func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, http.StatusAccepted, sc.resp.Code) - assert.Len(t, logs, 9) - assertContextContains(t, logs, "stacktrace", `UserError: Please replace user and try again - at ? (core|webpack:///./some_source.ts:2:2) - at ? (telepathic|webpack:///./some_source.ts:3:2) - at explode (http://localhost:3000/public/build/error.js:3:10) - at wat (http://localhost:3000/public/build/bar.js:3:10) - at nope (http://localhost:3000/baz.js:3:10) - at fake (http://localhost:3000/public/build/../../secrets.txt:3:10) - at ? (core|webpack:///./some_source.ts:3:2)`) - assert.Len(t, sourceMapReads, 6) - assert.Equal(t, "/staticroot", sourceMapReads[0].dir) - assert.Equal(t, "build/moo/foo.js.map", sourceMapReads[0].path) - assert.Equal(t, "/usr/local/telepathic-panel", sourceMapReads[1].dir) - assert.Equal(t, "/foo.js.map", sourceMapReads[1].path) - assert.Equal(t, "/staticroot", sourceMapReads[2].dir) - assert.Equal(t, "build/error.js.map", sourceMapReads[2].path) - assert.Equal(t, "/staticroot", sourceMapReads[3].dir) - assert.Equal(t, "build/bar.js.map", sourceMapReads[3].path) - assert.Equal(t, "/staticroot", sourceMapReads[4].dir) - assert.Equal(t, "secrets.txt.map", sourceMapReads[4].path) - assert.Equal(t, "/staticroot", sourceMapReads[5].dir) - assert.Equal(t, "build/foo.js.map", sourceMapReads[5].path) - }) - }) -} - func TestFrontendLoggingEndpointGrafanaJavascriptAgent(t *testing.T) { ts, err := time.Parse("2006-01-02T15:04:05.000Z", "2020-10-22T06:29:29.078Z") require.NoError(t, err) @@ -536,13 +252,13 @@ func TestFrontendLoggingEndpointGrafanaJavascriptAgent(t *testing.T) { func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) { assert.Equal(t, http.StatusAccepted, sc.resp.Code) assertContextContains(t, logs, "stacktrace", `UserError: Please replace user and try again - at ? (webpack:///./some_source.ts:2:2) - at ? (webpack:///./some_source.ts:3:2) + at ? (core|webpack:///./some_source.ts:2:2) + at ? (telepathic|webpack:///./some_source.ts:3:2) at explode (http://localhost:3000/public/build/error.js:3:10) at wat (http://localhost:3000/public/build/bar.js:3:10) at nope (http://localhost:3000/baz.js:3:10) at fake (http://localhost:3000/public/build/../../secrets.txt:3:10) - at ? (webpack:///./some_source.ts:3:2)`) + at ? (core|webpack:///./some_source.ts:3:2)`) assert.Len(t, sourceMapReads, 6) assert.Equal(t, "/staticroot", sourceMapReads[0].dir) assert.Equal(t, "build/moo/foo.js.map", sourceMapReads[0].path) diff --git a/pkg/api/frontendlogging/grafana_javascript_agent.go b/pkg/api/frontendlogging/grafana_javascript_agent.go index 4e00cf644c4..45c54d0562c 100644 --- a/pkg/api/frontendlogging/grafana_javascript_agent.go +++ b/pkg/api/frontendlogging/grafana_javascript_agent.go @@ -4,6 +4,8 @@ import ( "fmt" ) +type CtxVector []interface{} + type FrontendGrafanaJavascriptAgentEvent struct { Exceptions []Exception `json:"exceptions,omitempty"` Logs []Log `json:"logs,omitempty"` diff --git a/pkg/api/frontendlogging/grafana_javascript_agent_sourcemaps.go b/pkg/api/frontendlogging/grafana_javascript_agent_sourcemaps.go index e9de9f22970..5eb7d375899 100644 --- a/pkg/api/frontendlogging/grafana_javascript_agent_sourcemaps.go +++ b/pkg/api/frontendlogging/grafana_javascript_agent_sourcemaps.go @@ -1,33 +1,5 @@ package frontendlogging -// ResolveSourceLocation resolves minified source location to original source location -func ResolveSourceLocation(store *SourceMapStore, frame *Frame) (*Frame, error) { - smap, err := store.getSourceMap(frame.Filename) - if err != nil { - return nil, err - } - if smap == nil { - return nil, nil - } - - file, function, line, col, ok := smap.consumer.Source(frame.Lineno, frame.Colno) - if !ok { - return nil, nil - } - - // unfortunately in many cases go-sourcemap fails to determine the original function name. - // not a big issue as long as file, line and column are correct - if len(function) == 0 { - function = "?" - } - return &Frame{ - Filename: file, - Lineno: line, - Colno: col, - Function: function, - }, nil -} - // TransformException will attempt to resolved all monified source locations in the stacktrace with original source locations func TransformException(ex *Exception, store *SourceMapStore) *Exception { if ex.Stacktrace == nil { @@ -37,7 +9,7 @@ func TransformException(ex *Exception, store *SourceMapStore) *Exception { for _, frame := range ex.Stacktrace.Frames { frame := frame - mappedFrame, err := ResolveSourceLocation(store, &frame) + mappedFrame, err := store.resolveSourceLocation(frame) if err != nil { frames = append(frames, frame) } else if mappedFrame != nil { diff --git a/pkg/api/frontendlogging/sentry.go b/pkg/api/frontendlogging/sentry.go deleted file mode 100644 index 66b74c09c04..00000000000 --- a/pkg/api/frontendlogging/sentry.go +++ /dev/null @@ -1,108 +0,0 @@ -package frontendlogging - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/getsentry/sentry-go" - - "github.com/grafana/grafana/pkg/infra/log" -) - -type CtxVector []interface{} - -var logger = log.New("frontendlogging") - -type FrontendSentryExceptionValue struct { - Value string `json:"value,omitempty"` - Type string `json:"type,omitempty"` - Stacktrace sentry.Stacktrace `json:"stacktrace,omitempty"` -} - -type FrontendSentryException struct { - Values []FrontendSentryExceptionValue `json:"values,omitempty"` -} - -type FrontendSentryEvent struct { - *sentry.Event - Exception *FrontendSentryException `json:"exception,omitempty"` -} - -func (value *FrontendSentryExceptionValue) FmtMessage() string { - return fmt.Sprintf("%s: %s", value.Type, value.Value) -} - -func fmtLine(frame sentry.Frame) string { - module := "" - if len(frame.Module) > 0 { - module = frame.Module + "|" - } - return fmt.Sprintf("\n at %s (%s%s:%v:%v)", frame.Function, module, frame.Filename, frame.Lineno, frame.Colno) -} - -func (value *FrontendSentryExceptionValue) FmtStacktrace(store *SourceMapStore) string { - var stacktrace = value.FmtMessage() - for _, frame := range value.Stacktrace.Frames { - mappedFrame, err := store.resolveSourceLocation(frame) - if err != nil { - logger.Error("Error resolving stack trace frame source location", "err", err) - stacktrace += fmtLine(frame) // even if reading source map fails for unexpected reason, still better to log compiled location than nothing at all - } else { - if mappedFrame != nil { - stacktrace += fmtLine(*mappedFrame) - } else { - stacktrace += fmtLine(frame) - } - } - } - return stacktrace -} - -func (exception *FrontendSentryException) FmtStacktraces(store *SourceMapStore) string { - stacktraces := make([]string, 0, len(exception.Values)) - for _, value := range exception.Values { - stacktraces = append(stacktraces, value.FmtStacktrace(store)) - } - return strings.Join(stacktraces, "\n\n") -} - -func addEventContextToLogContext(rootPrefix string, logCtx *CtxVector, eventCtx map[string]interface{}) { - for key, element := range eventCtx { - prefix := fmt.Sprintf("%s_%s", rootPrefix, key) - switch v := element.(type) { - case map[string]interface{}: - addEventContextToLogContext(prefix, logCtx, v) - default: - *logCtx = append(*logCtx, prefix, fmt.Sprintf("%v", v)) - } - } -} - -func (event *FrontendSentryEvent) ToLogContext(store *SourceMapStore) []interface{} { - var ctx = CtxVector{"url", event.Request.URL, "user_agent", event.Request.Headers["User-Agent"], - "event_id", event.EventID, "original_timestamp", event.Timestamp} - - if event.Exception != nil { - ctx = append(ctx, "stacktrace", event.Exception.FmtStacktraces(store)) - } - addEventContextToLogContext("context", &ctx, event.Contexts) - if len(event.User.Email) > 0 { - ctx = append(ctx, "user_email", event.User.Email, "user_id", event.User.ID) - } - - return ctx -} - -func (event *FrontendSentryEvent) MarshalJSON() ([]byte, error) { - eventJSON, err := json.Marshal(event.Event) - if err != nil { - return nil, err - } - exceptionJSON, err := json.Marshal(map[string]interface{}{"exception": event.Exception}) - if err != nil { - return nil, err - } - exceptionJSON[0] = ',' - return append(eventJSON[:len(eventJSON)-1], exceptionJSON...), nil -} diff --git a/pkg/api/frontendlogging/source_maps.go b/pkg/api/frontendlogging/source_maps.go index 95a4802a2ab..d811b107364 100644 --- a/pkg/api/frontendlogging/source_maps.go +++ b/pkg/api/frontendlogging/source_maps.go @@ -9,13 +9,14 @@ import ( "strings" "sync" - "github.com/getsentry/sentry-go" sourcemap "github.com/go-sourcemap/sourcemap" - + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/setting" ) +var logger = log.New("frontendlogging") + type sourceMapLocation struct { dir string path string @@ -136,7 +137,7 @@ func (store *SourceMapStore) getSourceMap(sourceURL string) (*sourceMap, error) return smap, nil } -func (store *SourceMapStore) resolveSourceLocation(frame sentry.Frame) (*sentry.Frame, error) { +func (store *SourceMapStore) resolveSourceLocation(frame Frame) (*Frame, error) { smap, err := store.getSourceMap(frame.Filename) if err != nil { return nil, err @@ -157,7 +158,7 @@ func (store *SourceMapStore) resolveSourceLocation(frame sentry.Frame) (*sentry. if len(smap.pluginID) > 0 { module = smap.pluginID } - return &sentry.Frame{ + return &Frame{ Filename: file, Lineno: line, Colno: col, diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 23e7957796a..505004492b7 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -184,7 +184,6 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro RendererVersion: hs.RenderService.Version(), SecretsManagerPluginEnabled: secretsManagerPluginEnabled, Http2Enabled: hs.Cfg.Protocol == setting.HTTP2Scheme, - Sentry: hs.Cfg.Sentry, GrafanaJavascriptAgent: hs.Cfg.GrafanaJavascriptAgent, PluginCatalogURL: hs.Cfg.PluginCatalogURL, PluginAdminEnabled: hs.Cfg.PluginAdminEnabled, diff --git a/pkg/api/index.go b/pkg/api/index.go index 6f88ca1bc40..067c5828683 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -136,7 +136,6 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV AppleTouchIcon: "public/img/apple-touch-icon.png", AppTitle: "Grafana", NavTree: navTree, - Sentry: &hs.Cfg.Sentry, Nonce: c.RequestNonce, ContentDeliveryURL: hs.Cfg.GetContentDeliveryURL(hs.License.ContentDeliveryPrefix()), LoadingLogo: "public/img/grafana_icon.svg", diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 3f0e65a1711..122d3ef2bf0 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -363,9 +363,6 @@ type Cfg struct { DashboardAnnotationCleanupSettings AnnotationCleanupSettings APIAnnotationCleanupSettings AnnotationCleanupSettings - // Sentry config - Sentry Sentry - // GrafanaJavascriptAgent config GrafanaJavascriptAgent GrafanaJavascriptAgent @@ -1187,7 +1184,6 @@ func (cfg *Cfg) Load(args CommandLineArgs) error { cfg.GeomapEnableCustomBaseLayers = geomapSection.Key("enable_custom_baselayers").MustBool(true) cfg.readDateFormats() - cfg.readSentryConfig() cfg.readGrafanaJavascriptAgentConfig() if err := cfg.readLiveSettings(iniFile); err != nil { diff --git a/pkg/setting/setting_grafana_javascript_agent.go b/pkg/setting/setting_grafana_javascript_agent.go index e5c51894f83..cfc04d5136d 100644 --- a/pkg/setting/setting_grafana_javascript_agent.go +++ b/pkg/setting/setting_grafana_javascript_agent.go @@ -13,17 +13,14 @@ type GrafanaJavascriptAgent struct { func (cfg *Cfg) readGrafanaJavascriptAgentConfig() { raw := cfg.Raw.Section("log.frontend") - provider := raw.Key("provider").MustString("sentry") - if provider == "grafana" { - cfg.GrafanaJavascriptAgent = GrafanaJavascriptAgent{ - Enabled: raw.Key("enabled").MustBool(true), - CustomEndpoint: raw.Key("custom_endpoint").MustString("/log-grafana-javascript-agent"), - EndpointRPS: raw.Key("log_endpoint_requests_per_second_limit").MustInt(3), - EndpointBurst: raw.Key("log_endpoint_burst_limit").MustInt(15), - ErrorInstrumentalizationEnabled: raw.Key("instrumentations_errors_enabled").MustBool(true), - ConsoleInstrumentalizationEnabled: raw.Key("instrumentations_console_enabled").MustBool(true), - WebVitalsInstrumentalizationEnabled: raw.Key("instrumentations_webvitals_enabled").MustBool(true), - ApiKey: raw.Key("api_key").String(), - } + cfg.GrafanaJavascriptAgent = GrafanaJavascriptAgent{ + Enabled: raw.Key("enabled").MustBool(true), + CustomEndpoint: raw.Key("custom_endpoint").MustString("/log-grafana-javascript-agent"), + EndpointRPS: raw.Key("log_endpoint_requests_per_second_limit").MustInt(3), + EndpointBurst: raw.Key("log_endpoint_burst_limit").MustInt(15), + ErrorInstrumentalizationEnabled: raw.Key("instrumentations_errors_enabled").MustBool(true), + ConsoleInstrumentalizationEnabled: raw.Key("instrumentations_console_enabled").MustBool(true), + WebVitalsInstrumentalizationEnabled: raw.Key("instrumentations_webvitals_enabled").MustBool(true), + ApiKey: raw.Key("api_key").String(), } } diff --git a/pkg/setting/setting_sentry.go b/pkg/setting/setting_sentry.go deleted file mode 100644 index 49aee537023..00000000000 --- a/pkg/setting/setting_sentry.go +++ /dev/null @@ -1,28 +0,0 @@ -package setting - -import "github.com/go-kit/log/level" - -type Sentry struct { - Enabled bool `json:"enabled"` - DSN string `json:"dsn"` - CustomEndpoint string `json:"customEndpoint"` - SampleRate float64 `json:"sampleRate"` - EndpointRPS int `json:"-"` - EndpointBurst int `json:"-"` -} - -func (cfg *Cfg) readSentryConfig() { - raw := cfg.Raw.Section("log.frontend") - provider := raw.Key("provider").MustString("sentry") - if provider == "sentry" || provider != "grafana" { - _ = level.Warn(cfg.Logger).Log("msg", "\"sentry\" frontend logging provider is deprecated and will be removed in the next major version. Use \"grafana\" provider instead.") - cfg.Sentry = Sentry{ - Enabled: raw.Key("enabled").MustBool(true), - DSN: raw.Key("sentry_dsn").String(), - CustomEndpoint: raw.Key("custom_endpoint").MustString("/log"), - SampleRate: raw.Key("sample_rate").MustFloat64(), - EndpointRPS: raw.Key("log_endpoint_requests_per_second_limit").MustInt(3), - EndpointBurst: raw.Key("log_endpoint_burst_limit").MustInt(15), - } - } -} diff --git a/public/app/app.ts b/public/app/app.ts index f2f9eb799e8..56c10d5d92f 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -66,7 +66,6 @@ import { GA4EchoBackend } from './core/services/echo/backends/analytics/GA4Backe import { GAEchoBackend } from './core/services/echo/backends/analytics/GABackend'; import { RudderstackBackend } from './core/services/echo/backends/analytics/RudderstackBackend'; import { GrafanaJavascriptAgentBackend } from './core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend'; -import { SentryEchoBackend } from './core/services/echo/backends/sentry/SentryBackend'; import { KeybindingSrv } from './core/services/keybindingSrv'; import { initDevFeatures } from './dev'; import { getTimeSrv } from './features/dashboard/services/TimeSrv'; @@ -259,15 +258,6 @@ function initEchoSrv() { registerEchoBackend(new PerformanceBackend({})); } - if (config.sentry.enabled) { - registerEchoBackend( - new SentryEchoBackend({ - ...config.sentry, - user: config.bootData.user, - buildInfo: config.buildInfo, - }) - ); - } if (config.grafanaJavascriptAgent.enabled) { registerEchoBackend( new GrafanaJavascriptAgentBackend({ diff --git a/public/app/core/services/echo/EchoSrv.ts b/public/app/core/services/echo/EchoSrv.ts index 689517b32d5..54ec330c414 100644 --- a/public/app/core/services/echo/EchoSrv.ts +++ b/public/app/core/services/echo/EchoSrv.ts @@ -1,5 +1,4 @@ -import { captureException } from '@sentry/browser'; - +import { faro } from '@grafana/faro-web-sdk'; import { getEchoSrv, EchoEventType } from '@grafana/runtime'; import { PerformanceEvent } from './backends/PerformanceBackend'; @@ -14,6 +13,5 @@ export const reportPerformance = (metric: string, value: number) => { }); }; -// Sentry will process the error, adding its own metadata, applying any sampling rules, -// then push it to EchoSrv as SentryEvent -export const reportError = (error: Error) => captureException(error); +// Farp will process the error, then push it to EchoSrv as GrafanaJavascriptAgent event +export const reportError = (error: Error) => faro?.api?.pushError(error); diff --git a/public/app/core/services/echo/backends/sentry/SentryBackend.test.ts b/public/app/core/services/echo/backends/sentry/SentryBackend.test.ts deleted file mode 100644 index 62b1cd7c5ae..00000000000 --- a/public/app/core/services/echo/backends/sentry/SentryBackend.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { init as initSentry, setUser as sentrySetUser, Event as SentryEvent } from '@sentry/browser'; -import { FetchTransport } from '@sentry/browser/dist/transports'; -import { waitFor } from '@testing-library/react'; - -import { BuildInfo } from '@grafana/data'; -import { GrafanaEdition } from '@grafana/data/src/types/config'; -import { EchoBackend, EchoEventType, EchoMeta, setEchoSrv } from '@grafana/runtime'; - -import { Echo } from '../../Echo'; - -import { SentryEchoBackend, SentryEchoBackendOptions } from './SentryBackend'; -import { CustomEndpointTransport } from './transports/CustomEndpointTransport'; -import { EchoSrvTransport } from './transports/EchoSrvTransport'; -import { SentryEchoEvent } from './types'; - -jest.mock('@sentry/browser'); - -describe('SentryEchoBackend', () => { - beforeEach(() => { - jest.resetAllMocks(); - window.fetch = jest.fn(); - }); - - const buildInfo: BuildInfo = { - version: '1.0', - commit: 'abcd123', - env: 'production', - edition: GrafanaEdition.OpenSource, - latestVersion: 'ba', - hasUpdate: false, - hideVersion: false, - }; - - const options: SentryEchoBackendOptions = { - enabled: true, - buildInfo, - dsn: 'https://examplePublicKey@o0.ingest.testsentry.io/0', - sampleRate: 1, - customEndpoint: '', - user: { - email: 'darth.vader@sith.glx', - id: 504, - orgId: 1, - }, - }; - - it('will set up sentry`s FetchTransport if DSN is provided', async () => { - const backend = new SentryEchoBackend(options); - expect(backend.transports.length).toEqual(1); - expect(backend.transports[0]).toBeInstanceOf(FetchTransport); - expect((backend.transports[0] as FetchTransport).options.dsn).toEqual(options.dsn); - }); - - it('will set up custom endpoint transport if custom endpoint is provided', async () => { - const backend = new SentryEchoBackend({ - ...options, - dsn: '', - customEndpoint: '/log', - }); - expect(backend.transports.length).toEqual(1); - expect(backend.transports[0]).toBeInstanceOf(CustomEndpointTransport); - expect((backend.transports[0] as CustomEndpointTransport).options.endpoint).toEqual('/log'); - }); - - it('will initialize sentry and set user', async () => { - new SentryEchoBackend(options); - expect(initSentry).toHaveBeenCalledTimes(1); - expect(initSentry).toHaveBeenCalledWith({ - release: buildInfo.version, - environment: buildInfo.env, - dsn: options.dsn, - sampleRate: options.sampleRate, - transport: EchoSrvTransport, - ignoreErrors: [ - 'ResizeObserver loop limit exceeded', - 'ResizeObserver loop completed', - 'Non-Error exception captured with keys', - ], - }); - expect(sentrySetUser).toHaveBeenCalledWith({ - email: options.user?.email, - id: String(options.user?.id), - }); - }); - - it('will forward events to transports', async () => { - const backend = new SentryEchoBackend(options); - backend.transports = [{ sendEvent: jest.fn() }, { sendEvent: jest.fn() }]; - const event: SentryEchoEvent = { - type: EchoEventType.Sentry, - payload: { foo: 'bar' } as unknown as SentryEvent, - meta: {} as unknown as EchoMeta, - }; - backend.addEvent(event); - backend.transports.forEach((transport) => { - expect(transport.sendEvent).toHaveBeenCalledTimes(1); - expect(transport.sendEvent).toHaveBeenCalledWith(event.payload); - }); - }); - - it('integration test with EchoSrv, Sentry and CustomFetchTransport', async () => { - // sets up the whole thing between window.onerror and backend endpoint call, checks that error is reported - - // use actual sentry & mock window.fetch - const sentry = jest.requireActual('@sentry/browser'); - (initSentry as jest.Mock).mockImplementation(sentry.init); - (sentrySetUser as jest.Mock).mockImplementation(sentry.setUser); - const fetchSpy = (window.fetch = jest.fn()); - fetchSpy.mockResolvedValue({ status: 200 } as Response); - - // set up echo srv & sentry backend - const echo = new Echo({ debug: true }); - setEchoSrv(echo); - const sentryBackend = new SentryEchoBackend({ - ...options, - dsn: '', - customEndpoint: '/log', - }); - echo.addBackend(sentryBackend); - - // lets add another echo backend for sentry events for good measure - const myCustomErrorBackend: EchoBackend = { - supportedEvents: [EchoEventType.Sentry], - flush: () => {}, - options: {}, - addEvent: jest.fn(), - }; - echo.addBackend(myCustomErrorBackend); - - // fire off an error using global error handler, Sentry should pick it up - const error = new Error('test error'); - window.onerror!(error.message, undefined, undefined, undefined, error); - - // check that error was reported to backend - await waitFor(() => expect(fetchSpy).toHaveBeenCalledTimes(1)); - const [url, reqInit]: [string, RequestInit] = fetchSpy.mock.calls[0]; - expect(url).toEqual('/log'); - expect((JSON.parse(reqInit.body as string) as SentryEvent).exception!.values![0].value).toEqual('test error'); - - // check that our custom backend got it too - expect(myCustomErrorBackend.addEvent).toHaveBeenCalledTimes(1); - }); -}); diff --git a/public/app/core/services/echo/backends/sentry/SentryBackend.ts b/public/app/core/services/echo/backends/sentry/SentryBackend.ts deleted file mode 100644 index dc63851da2e..00000000000 --- a/public/app/core/services/echo/backends/sentry/SentryBackend.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { BrowserOptions, init as initSentry, setUser as sentrySetUser } from '@sentry/browser'; -import { FetchTransport } from '@sentry/browser/dist/transports'; - -import { BuildInfo } from '@grafana/data'; -import { SentryConfig } from '@grafana/data/src/types/config'; -import { EchoBackend, EchoEventType } from '@grafana/runtime'; - -import { CustomEndpointTransport } from './transports/CustomEndpointTransport'; -import { EchoSrvTransport } from './transports/EchoSrvTransport'; -import { SentryEchoEvent, User, BaseTransport } from './types'; - -export interface SentryEchoBackendOptions extends SentryConfig { - user?: User; - buildInfo: BuildInfo; -} - -export class SentryEchoBackend implements EchoBackend { - supportedEvents = [EchoEventType.Sentry]; - - transports: BaseTransport[]; - - constructor(public options: SentryEchoBackendOptions) { - // set up transports to post events to grafana backend and/or Sentry - this.transports = []; - if (options.dsn) { - this.transports.push(new FetchTransport({ dsn: options.dsn }, fetch)); - } - if (options.customEndpoint) { - this.transports.push(new CustomEndpointTransport({ endpoint: options.customEndpoint })); - } - - // initialize Sentry so it can set up its hooks and start collecting errors - const sentryOptions: BrowserOptions = { - release: options.buildInfo.version, - environment: options.buildInfo.env, - // seems Sentry won't attempt to send events to transport unless a valid DSN is defined :shrug: - dsn: options.dsn || 'https://examplePublicKey@o0.ingest.sentry.io/0', - sampleRate: options.sampleRate, - transport: EchoSrvTransport, // will dump errors to EchoSrv - ignoreErrors: [ - 'ResizeObserver loop limit exceeded', - 'ResizeObserver loop completed', - 'Non-Error exception captured with keys', - ], - }; - - if (options.user) { - sentrySetUser({ - email: options.user.email, - id: String(options.user.id), - }); - } - - initSentry(sentryOptions); - } - - addEvent = (e: SentryEchoEvent) => { - this.transports.forEach((t) => t.sendEvent(e.payload)); - }; - - // backend will log events to stdout, and at least in case of hosted grafana they will be - // ingested into Loki. Due to Loki limitations logs cannot be backdated, - // so not using buffering for this backend to make sure that events are logged as close - // to their context as possible - flush = () => {}; -} diff --git a/public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.test.ts b/public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.test.ts deleted file mode 100644 index 6428907219f..00000000000 --- a/public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Event, Severity } from '@sentry/browser'; - -import { CustomEndpointTransport } from './CustomEndpointTransport'; - -describe('CustomEndpointTransport', () => { - const fetchSpy = (window.fetch = jest.fn()); - let consoleSpy: jest.SpyInstance; - - beforeEach(() => { - jest.resetAllMocks(); - // The code logs a warning to console - // Let's stub this out so we don't pollute the test output - consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); - }); - - afterEach(() => { - consoleSpy.mockRestore(); - }); - const now = new Date(); - - const event: Event = { - level: Severity.Error, - breadcrumbs: [], - exception: { - values: [ - { - type: 'SomeError', - value: 'foo', - }, - ], - }, - timestamp: now.getTime() / 1000, - }; - - it('will send received event to backend using window.fetch', async () => { - fetchSpy.mockResolvedValue({ status: 200 }); - const transport = new CustomEndpointTransport({ endpoint: '/log' }); - await transport.sendEvent(event); - expect(fetchSpy).toHaveBeenCalledTimes(1); - const [url, reqInit]: [string, RequestInit] = fetchSpy.mock.calls[0]; - expect(url).toEqual('/log'); - expect(reqInit.method).toEqual('POST'); - expect(reqInit.headers).toEqual({ - 'Content-Type': 'application/json', - }); - expect(JSON.parse(reqInit.body!.toString())).toEqual({ - ...event, - timestamp: now.toISOString(), - }); - }); - - it('will back off if backend returns Retry-After', async () => { - const rateLimiterResponse = { - status: 429, - ok: false, - headers: new Headers({ - 'Retry-After': '1', // 1 second - }), - } as Response; - fetchSpy.mockResolvedValueOnce(rateLimiterResponse).mockResolvedValueOnce({ status: 200 }); - const transport = new CustomEndpointTransport({ endpoint: '/log' }); - - // first call - backend is called, rejected because of 429 - await expect(transport.sendEvent(event)).rejects.toEqual(rateLimiterResponse); - expect(fetchSpy).toHaveBeenCalledTimes(1); - - // second immediate call - shot circuited because retry-after time has not expired, backend not called - await expect(transport.sendEvent(event)).resolves.toHaveProperty('status', 'skipped'); - expect(fetchSpy).toHaveBeenCalledTimes(1); - - // wait out the retry-after and call again - great success - await new Promise((resolve) => setTimeout(() => resolve(null), 1001)); - await expect(transport.sendEvent(event)).resolves.toBeTruthy(); - expect(fetchSpy).toHaveBeenCalledTimes(2); - }); - - it('will back off if backend returns Retry-After', async () => { - const rateLimiterResponse = { - status: 429, - ok: false, - headers: new Headers({ - 'Retry-After': '1', // 1 second - }), - } as Response; - fetchSpy.mockResolvedValueOnce(rateLimiterResponse).mockResolvedValueOnce({ status: 200 }); - const transport = new CustomEndpointTransport({ endpoint: '/log' }); - - // first call - backend is called, rejected because of 429 - await expect(transport.sendEvent(event)).rejects.toHaveProperty('status', 429); - expect(fetchSpy).toHaveBeenCalledTimes(1); - - // second immediate call - shot circuited because retry-after time has not expired, backend not called - await expect(transport.sendEvent(event)).resolves.toHaveProperty('status', 'skipped'); - expect(fetchSpy).toHaveBeenCalledTimes(1); - - // wait out the retry-after and call again - great success - await new Promise((resolve) => setTimeout(() => resolve(null), 1001)); - await expect(transport.sendEvent(event)).resolves.toBeTruthy(); - expect(fetchSpy).toHaveBeenCalledTimes(2); - }); - - it('will drop events and log a warning to console if max concurrency is reached', async () => { - const calls: Array<(value: unknown) => void> = []; - fetchSpy.mockImplementation( - () => - new Promise((resolve) => { - calls.push(resolve); - }) - ); - - const transport = new CustomEndpointTransport({ endpoint: '/log', maxConcurrentRequests: 2 }); - - // first two requests are accepted - transport.sendEvent(event); - const event2 = transport.sendEvent(event); - expect(calls).toHaveLength(2); - - // third is skipped because too many requests in flight - await expect(transport.sendEvent(event)).resolves.toHaveProperty('status', 'skipped'); - - expect(calls).toHaveLength(2); - - // after resolving in flight requests, next request is accepted as well - calls.forEach((call) => { - call({ status: 200 }); - }); - await event2; - const event3 = transport.sendEvent(event); - expect(calls).toHaveLength(3); - calls[2]({ status: 200 }); - await event3; - }); -}); diff --git a/public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.ts b/public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.ts deleted file mode 100644 index ea15b2622d6..00000000000 --- a/public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { Event, Severity } from '@sentry/browser'; -import { Response } from '@sentry/types'; -import { - logger, - makePromiseBuffer, - parseRetryAfterHeader, - PromiseBuffer, - supportsReferrerPolicy, - SyncPromise, -} from '@sentry/utils'; - -import { BaseTransport } from '../types'; - -export interface CustomEndpointTransportOptions { - endpoint: string; - fetchParameters?: Partial; - maxConcurrentRequests?: number; -} - -const DEFAULT_MAX_CONCURRENT_REQUESTS = 3; - -const DEFAULT_RATE_LIMIT_TIMEOUT_MS = 5000; - -/** - * This is a copy of sentry's FetchTransport, edited to be able to push to any custom url - * instead of using Sentry-specific endpoint logic. - * Also transforms some of the payload values to be parseable by go. - * Sends events sequentially and implements back-off in case of rate limiting. - */ - -export class CustomEndpointTransport implements BaseTransport { - /** Locks transport after receiving 429 response */ - private _disabledUntil: Date = new Date(Date.now()); - - private readonly _buffer: PromiseBuffer; - - constructor(public options: CustomEndpointTransportOptions) { - this._buffer = makePromiseBuffer(options.maxConcurrentRequests ?? DEFAULT_MAX_CONCURRENT_REQUESTS); - } - - sendEvent(event: Event): PromiseLike { - if (new Date(Date.now()) < this._disabledUntil) { - const reason = `Dropping frontend event due to too many requests.`; - console.warn(reason); - return Promise.resolve({ - event, - reason, - status: 'skipped', - }); - } - - const sentryReq = { - // convert all timestamps to iso string, so it's parseable by backend - body: JSON.stringify({ - ...event, - level: event.level ?? (event.exception ? Severity.Error : Severity.Info), - exception: event.exception - ? { - values: event.exception.values?.map((value) => ({ - ...value, - // according to both typescript and go types, value is supposed to be string. - // but in some odd cases at runtime it turns out to be an empty object {} - // let's fix it here - value: fmtSentryErrorValue(value.value), - })), - } - : event.exception, - breadcrumbs: event.breadcrumbs?.map((breadcrumb) => ({ - ...breadcrumb, - timestamp: makeTimestamp(breadcrumb.timestamp), - })), - timestamp: makeTimestamp(event.timestamp), - }), - url: this.options.endpoint, - }; - - const options: RequestInit = { - body: sentryReq.body, - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default - // https://caniuse.com/#feat=referrer-policy - // It doesn't. And it throw exception instead of ignoring this parameter... - // REF: https://github.com/getsentry/raven-js/issues/1233 - referrerPolicy: supportsReferrerPolicy() ? 'origin' : '', - }; - - if (this.options.fetchParameters !== undefined) { - Object.assign(options, this.options.fetchParameters); - } - - return this._buffer - .add( - () => - new SyncPromise((resolve, reject) => { - window - .fetch(sentryReq.url, options) - .then((response) => { - if (response.status === 200) { - resolve({ status: 'success' }); - return; - } - - if (response.status === 429) { - const now = Date.now(); - const retryAfterHeader = response.headers.get('Retry-After'); - if (retryAfterHeader) { - this._disabledUntil = new Date(now + parseRetryAfterHeader(retryAfterHeader, now)); - } else { - this._disabledUntil = new Date(now + DEFAULT_RATE_LIMIT_TIMEOUT_MS); - } - logger.warn(`Too many requests, backing off till: ${this._disabledUntil}`); - } - - reject(response); - }) - .catch(reject); - }) - ) - .then(undefined, (reason) => { - if (reason.message === 'Not adding Promise due to buffer limit reached.') { - const msg = `Dropping frontend log event due to too many requests in flight.`; - console.warn(msg); - return { - event, - reason: msg, - status: 'skipped', - }; - } - throw reason; - }); - } -} - -function makeTimestamp(time: number | undefined): string { - if (time) { - return new Date(time * 1000).toISOString(); - } - return new Date().toISOString(); -} - -function fmtSentryErrorValue(value: unknown): string | undefined { - if (typeof value === 'string' || value === undefined) { - return value; - } else if (value && typeof value === 'object' && Object.keys(value).length === 0) { - return ''; - } - return String(value); -} diff --git a/public/app/core/services/echo/backends/sentry/transports/EchoSrvTransport.ts b/public/app/core/services/echo/backends/sentry/transports/EchoSrvTransport.ts deleted file mode 100644 index a49c3219940..00000000000 --- a/public/app/core/services/echo/backends/sentry/transports/EchoSrvTransport.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Event } from '@sentry/browser'; -import { BaseTransport } from '@sentry/browser/dist/transports'; -import { EventStatus, Request, Session, Response } from '@sentry/types'; - -import { getEchoSrv, EchoEventType } from '@grafana/runtime'; - -export class EchoSrvTransport extends BaseTransport { - sendEvent(event: Event): Promise<{ status: EventStatus; event: Event }> { - getEchoSrv().addEvent({ - type: EchoEventType.Sentry, - payload: event, - }); - return Promise.resolve({ - status: 'success', - event, - }); - } - // not recording sessions for now - sendSession(session: Session): PromiseLike { - return Promise.resolve({ status: 'skipped' }); - } - // required by BaseTransport definition but not used by this implementation - _sendRequest(sentryRequest: Request, originalPayload: Event | Session): PromiseLike { - throw new Error('should not happen'); - } -} diff --git a/public/app/core/services/echo/backends/sentry/types.ts b/public/app/core/services/echo/backends/sentry/types.ts deleted file mode 100644 index a19fe3c0856..00000000000 --- a/public/app/core/services/echo/backends/sentry/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Event as SentryEvent } from '@sentry/browser'; -import { Response } from '@sentry/types'; - -import { EchoEvent, EchoEventType } from '@grafana/runtime'; - -export interface BaseTransport { - sendEvent(event: SentryEvent): PromiseLike; -} - -export type SentryEchoEvent = EchoEvent; - -export interface User { - email: string; - id: number; - orgId: number; -} diff --git a/public/app/features/alerting/unified/Analytics.ts b/public/app/features/alerting/unified/Analytics.ts index da31b83ebf1..8ce2236f361 100644 --- a/public/app/features/alerting/unified/Analytics.ts +++ b/public/app/features/alerting/unified/Analytics.ts @@ -17,7 +17,7 @@ export const LogMessages = { unknownMessageFromError: 'unknown messageFromError', }; -// logInfo from '@grafana/runtime' should be used, but it doesn't handle Grafana JS Agent and Sentry correctly +// logInfo from '@grafana/runtime' should be used, but it doesn't handle Grafana JS Agent correctly export function logInfo(message: string, context: Record = {}) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushLog([message], { diff --git a/yarn.lock b/yarn.lock index 7ee26a3a648..de2e1c7be14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3028,7 +3028,6 @@ __metadata: "@grafana/ui": 10.1.0-pre "@rollup/plugin-commonjs": 23.0.2 "@rollup/plugin-node-resolve": 15.0.1 - "@sentry/browser": 6.19.7 "@testing-library/dom": 9.0.1 "@testing-library/react": 14.0.0 "@testing-library/user-event": 14.4.3 @@ -3141,7 +3140,6 @@ __metadata: "@react-aria/utils": 3.13.1 "@react-stately/menu": 3.4.1 "@rollup/plugin-node-resolve": 15.0.1 - "@sentry/browser": 6.19.7 "@storybook/addon-a11y": 6.5.16 "@storybook/addon-actions": 6.5.16 "@storybook/addon-docs": 6.5.16 @@ -6451,70 +6449,6 @@ __metadata: languageName: node linkType: hard -"@sentry/browser@npm:6.19.7": - version: 6.19.7 - resolution: "@sentry/browser@npm:6.19.7" - dependencies: - "@sentry/core": 6.19.7 - "@sentry/types": 6.19.7 - "@sentry/utils": 6.19.7 - tslib: ^1.9.3 - checksum: 071d00c76c2d0384580474c634c58c6196bbd1a3cf510da1309bd1565c57df7422fca8ceb717db189fa557f2c711a21664ee1ab935dfd9869faf416d388e6f78 - languageName: node - linkType: hard - -"@sentry/core@npm:6.19.7": - version: 6.19.7 - resolution: "@sentry/core@npm:6.19.7" - dependencies: - "@sentry/hub": 6.19.7 - "@sentry/minimal": 6.19.7 - "@sentry/types": 6.19.7 - "@sentry/utils": 6.19.7 - tslib: ^1.9.3 - checksum: d212e8ef07114549de4a93b81f8bfa217ca1550ca7a5eeaa611e5629faef78ff72663ce561ffa2cff48f3dc556745ef65177044f9965cdd3cbccf617cf3bf675 - languageName: node - linkType: hard - -"@sentry/hub@npm:6.19.7": - version: 6.19.7 - resolution: "@sentry/hub@npm:6.19.7" - dependencies: - "@sentry/types": 6.19.7 - "@sentry/utils": 6.19.7 - tslib: ^1.9.3 - checksum: 10bb1c5cba1b0f1e27a3dd0a186c22f94aeaf11c4662890ab07b2774f46f46af78d61e3ba71d76edc750a7b45af86edd032f35efecdb4efa2eaf551080ccdcb1 - languageName: node - linkType: hard - -"@sentry/minimal@npm:6.19.7": - version: 6.19.7 - resolution: "@sentry/minimal@npm:6.19.7" - dependencies: - "@sentry/hub": 6.19.7 - "@sentry/types": 6.19.7 - tslib: ^1.9.3 - checksum: 9153ac426ee056fc34c5be898f83d74ec08f559d69f544c5944ec05e584b62ed356b92d1a9b08993a7022ad42b5661c3d72881221adc19bee5fc1af3ad3864a8 - languageName: node - linkType: hard - -"@sentry/types@npm:6.19.7": - version: 6.19.7 - resolution: "@sentry/types@npm:6.19.7" - checksum: f46ef74a33376ad6ea9b128115515c58eb9369d89293c60aa67abca26b5d5d519aa4d0a736db56ae0d75ffd816643d62187018298523cbc2e6c2fb3a6b2a9035 - languageName: node - linkType: hard - -"@sentry/utils@npm:6.19.7": - version: 6.19.7 - resolution: "@sentry/utils@npm:6.19.7" - dependencies: - "@sentry/types": 6.19.7 - tslib: ^1.9.3 - checksum: a000223b9c646c64e3565e79cace1eeb75114342b768367c4dddd646476c215eb1bddfb70c63f05e2352d3bce2d7d415344e4757a001605d0e01ac74da5dd306 - languageName: node - linkType: hard - "@sideway/address@npm:^4.1.3": version: 4.1.4 resolution: "@sideway/address@npm:4.1.4" @@ -18770,9 +18704,6 @@ __metadata: "@reduxjs/toolkit": 1.9.3 "@remix-run/router": ^1.5.0 "@rtsao/plugin-proposal-class-properties": 7.0.1-patch.1 - "@sentry/browser": 6.19.7 - "@sentry/types": 6.19.7 - "@sentry/utils": 6.19.7 "@swc/core": 1.3.38 "@swc/helpers": 0.4.14 "@testing-library/dom": 9.0.1 @@ -32939,7 +32870,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.10.0, tslib@npm:^1.14.1, tslib@npm:^1.8.1, tslib@npm:^1.9.3": +"tslib@npm:^1.10.0, tslib@npm:^1.14.1, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd