From ea25b759668da2868db9fdddaaa44a835a91404f Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Mon, 13 Oct 2025 13:46:29 +0100 Subject: [PATCH] Faro: Update configuration with best practices (#112108) * Clean up faro configuration to align with best practices * Update grafana docs * remove fixme comment * fix tests * don't spread config in --- conf/defaults.ini | 56 +++++----- conf/sample.ini | 42 +++---- .../setup-grafana/configure-grafana/_index.md | 52 ++++----- packages/grafana-data/src/types/config.ts | 13 ++- packages/grafana-runtime/src/config.ts | 9 +- .../setting_grafana_javascript_agent.go | 48 ++++---- .../GrafanaJavascriptAgentBackend.test.ts | 82 ++++++++------ .../GrafanaJavascriptAgentBackend.ts | 103 +++++++----------- public/app/core/services/echo/init.ts | 19 ++-- 9 files changed, 223 insertions(+), 201 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 1c47f30e6f3..b057f887fb4 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1194,11 +1194,39 @@ facility = tag = [log.frontend] -# Should Faro javascript agent be initialized +# Enables the Grafana Faro javascript agent integration for capturing frontend performance and error events. enabled = false -# Custom HTTP endpoint to send events to. Default will log the events to stdout. -custom_endpoint = +# Custom HTTP endpoint to send Grafana Faro events to. Default will send events to /log-grafana-javascript-agent and log the events to stdout. +custom_endpoint = /log-grafana-javascript-agent + +# API Key for Grafana Faro custom endpoint +api_key = + +# level of internal logging for debugging Grafana Javascript Agent. +# possible values are: 0 = OFF, 1 = ERROR, 2 = WARN, 3 = INFO, 4 = VERBOSE +# more details: https://github.com/grafana/faro-web-sdk/blob/v1.3.7/docs/sources/tutorials/quick-start-browser.md#how-to-activate-debugging +internal_logger_level = 0 + +# Enables the Console instrumentation for Grafana Faro +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/console-instrumentation/ +instrumentations_console_enabled = true + +# Enables the Performance instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/performance-instrumentation/ +instrumentations_performance_enabled = true + +# Enables the Content Security Policy Violations instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/csp-violation-tracking/ +instrumentations_csp_enabled = true + +# Enables the Tracing instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/tracing-instrumentation/ +instrumentations_tracing_enabled = true + +# Enables sending attribution data for web vitals with the Performance instrumentation. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/web-vitals/#web-vitals-attribution-data +web_vitals_attribution_enabled = true # Requests per second limit enforced per an extended period, for Grafana backend log ingestion endpoint (/log). log_endpoint_requests_per_second_limit = 3 @@ -1206,28 +1234,6 @@ log_endpoint_requests_per_second_limit = 3 # Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log) log_endpoint_burst_limit = 15 -# Enables all Faro default instrumentation by using `getWebInstrumentations`. Overrides other instrumentation flags. -instrumentations_all_enabled = false - -# Should error instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_errors_enabled = true - -# Should console instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_console_enabled = false - -# Should webvitals instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_webvitals_enabled = false - -# Should tracing instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_tracing_enabled = false - -# level of internal logging for debugging Grafana Javascript Agent. -# possible values are: 0 = OFF, 1 = ERROR, 2 = WARN, 3 = INFO, 4 = VERBOSE -# more details: https://github.com/grafana/faro-web-sdk/blob/v1.3.7/docs/sources/tutorials/quick-start-browser.md#how-to-activate-debugging -internal_logger_level = 0 - -# Api Key, only applies to Grafana Javascript Agent provider -api_key = #################################### Usage Quotas ######################## [quota] diff --git a/conf/sample.ini b/conf/sample.ini index 964b3df5196..eb59e6688f3 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1172,35 +1172,39 @@ ;tag = [log.frontend] -# Should Faro javascript agent be initialized +# Enables the Grafana Faro javascript agent integration for capturing frontend performance and error events. ;enabled = false -# Custom HTTP endpoint to send events to. Default will log the events to stdout. +# Custom HTTP endpoint to send Grafana Faro events to. Default will send events to /log-grafana-javascript-agent and 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 +# API Key for Grafana Faro custom endpoint +;api_key = -# Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log). -;log_endpoint_burst_limit = 15 +# level of internal logging for debugging Grafana Javascript Agent. +# possible values are: 0 = OFF, 1 = ERROR, 2 = WARN, 3 = INFO, 4 = VERBOSE +# more details: https://github.com/grafana/faro-web-sdk/blob/v1.3.7/docs/sources/tutorials/quick-start-browser.md#how-to-activate-debugging +;internal_logger_level = -# Enables all Faro default instrumentation by using `getWebInstrumentations`. Overrides other instrumentation flags. -;instrumentations_all_enabled = false +# Enables the Console instrumentation for Grafana Faro +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/console-instrumentation/ +;instrumentations_console_enabled = true -# Should error instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_errors_enabled = true +# Enables the Performance instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/performance-instrumentation/ +;instrumentations_performance_enabled = true -# Should console instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_console_enabled = false +# Enables the Content Security Policy Violations instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/csp-violation-tracking/ +; instrumentations_csp_enabled = true -# Should webvitals instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_webvitals_enabled = false +# Enables the Tracing instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/tracing-instrumentation/ +;instrumentations_tracing_enabled = true -# Should tracing instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_tracing_enabled = false - -# Api Key, only applies to Grafana Javascript Agent provider -;api_key = testApiKey +# Enables sending attribution data for web vitals with the Performance instrumentation. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/web-vitals/#web-vitals-attribution-data +;web_vitals_attribution_enabled = true #################################### Usage Quotas ######################## [quota] diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 9702851cd19..ff77ac47fc8 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1641,11 +1641,35 @@ Syslog tag. By default, the process's `argv[0]` is used. #### `enabled` -Faro JavaScript agent is initialized. Default is `false`. +Grafana Faro instrumentation is initialized. Default is `false`. Enables the default set of instrumentations from `getWebInstrumentations`. See the options below to selectively disable some of these. #### `custom_endpoint` -Custom HTTP endpoint to send events captured by the Faro agent to. Default, `/log-grafana-javascript-agent`, logs the events to standard output. +Custom HTTP endpoint to send events captured by the Grafana Faro agent to. Default, `/log-grafana-javascript-agent`, logs the events to standard output. + +#### `api_key` + +If `custom_endpoint` required authentication, you can set the API key here. Only relevant for Grafana JavaScript Agent provider. + +#### `instrumentations_console_enabled` + +Enables the [Console instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/console-instrumentation/) for Grafana Faro, defaults to `true`. + +#### `instrumentations_performance_enabled` + +Enables the [Performance instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/performance-instrumentation/) for Grafana Faro, defaults to `true`. + +#### `instrumentations_csp_enabled` + +Enables the [Content Security Policy Violations instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/csp-violation-tracking/) for Grafana Faro, defaults to `true`. + +#### `instrumentations_tracing_enabled` + +Enables the [Tracing instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/tracing-instrumentation/) for Grafana Faro, defaults to `true`. + +#### `web_vitals_attribution_enabled` + +Enables sending [attribution data for web vitals](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/web-vitals/#web-vitals-attribution-data) with the Performance instrumentation, defaults to `true`. #### `log_endpoint_requests_per_second_limit` @@ -1655,30 +1679,6 @@ Requests per second limit enforced per an extended period, for Grafana backend l Maximum requests accepted per short interval of time for Grafana backend log ingestion endpoint, `/log-grafana-javascript-agent`. Default is `15`. -#### `instrumentations_all_enabled` - -Enables all Faro default instrumentation by using `getWebInstrumentations`. Overrides other instrumentation flags. - -#### `instrumentations_errors_enabled` - -Turn on error instrumentation. Only affects Grafana JavaScript Agent. - -#### `instrumentations_console_enabled` - -Turn on console instrumentation. Only affects Grafana JavaScript Agent - -#### `instrumentations_webvitals_enabled` - -Turn on web vitals instrumentation. Only affects Grafana JavaScript Agent - -#### `instrumentations_tracing_enabled` - -Turns on tracing instrumentation. Only affects Grafana JavaScript Agent. - -#### `api_key` - -If `custom_endpoint` required authentication, you can set the API key here. Only relevant for Grafana JavaScript Agent provider. -
### `[quota]` diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index 11592e20ae3..92991080976 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -1,3 +1,5 @@ +import type { InternalLoggerLevel } from '@grafana/faro-core'; + import { SystemDateFormatSettings } from '../datetime/formats'; import { MapLayerOptions } from '../geo/layer'; @@ -93,11 +95,14 @@ export interface LicenseInfo { export interface GrafanaJavascriptAgentConfig { enabled: boolean; customEndpoint: string; - errorInstrumentalizationEnabled: boolean; - consoleInstrumentalizationEnabled: boolean; - webVitalsInstrumentalizationEnabled: boolean; - tracingInstrumentalizationEnabled: boolean; apiKey: string; + internalLoggerLevel: InternalLoggerLevel; + + consoleInstrumentalizationEnabled: boolean; + performanceInstrumentalizationEnabled: boolean; + cspInstrumentalizationEnabled: boolean; + tracingInstrumentalizationEnabled: boolean; + webVitalsAttribution: boolean; } export interface UnifiedAlertingStateHistoryConfig { diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 2b816dfe1ad..8fc1fb68218 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -153,13 +153,14 @@ export class GrafanaBootConfig { dateFormats?: SystemDateFormatSettings; grafanaJavascriptAgent = { enabled: false, - customEndpoint: '', apiKey: '', - allInstrumentationsEnabled: false, - errorInstrumentalizationEnabled: true, + customEndpoint: '', consoleInstrumentalizationEnabled: false, - webVitalsInstrumentalizationEnabled: false, + performanceInstrumentalizationEnabled: false, + cspInstrumentalizationEnabled: false, tracingInstrumentalizationEnabled: false, + webVitalsAttribution: false, + internalLoggerLevel: 0, }; pluginCatalogURL = 'https://grafana.com/grafana/plugins/'; pluginAdminEnabled = true; diff --git a/pkg/setting/setting_grafana_javascript_agent.go b/pkg/setting/setting_grafana_javascript_agent.go index 84d99162e47..9ce5633f492 100644 --- a/pkg/setting/setting_grafana_javascript_agent.go +++ b/pkg/setting/setting_grafana_javascript_agent.go @@ -1,32 +1,36 @@ package setting type GrafanaJavascriptAgent struct { - Enabled bool `json:"enabled"` - CustomEndpoint string `json:"customEndpoint"` - EndpointRPS int `json:"-"` - EndpointBurst int `json:"-"` - AllInstrumentationsEnabeld bool `json:"allInstrumentationEnabeld"` - ErrorInstrumentalizationEnabled bool `json:"errorInstrumentalizationEnabled"` - ConsoleInstrumentalizationEnabled bool `json:"consoleInstrumentalizationEnabled"` - WebVitalsInstrumentalizationEnabled bool `json:"webVitalsInstrumentalizationEnabled"` - TracingInstrumentalizationEnabled bool `json:"tracingInstrumentalizationEnabled"` - InternalLoggerLevel int `json:"internalLoggerLevel"` - ApiKey string `json:"apiKey"` + EndpointRPS int `json:"-"` + EndpointBurst int `json:"-"` + + // Faro config + Enabled bool `json:"enabled"` + CustomEndpoint string `json:"customEndpoint"` + ApiKey string `json:"apiKey"` + InternalLoggerLevel int `json:"internalLoggerLevel"` + ConsoleInstrumentalizationEnabled bool `json:"consoleInstrumentalizationEnabled"` + PerformanceInstrumentalizationEnabled bool `json:"performanceInstrumentalizationEnabled"` + CSPInstrumentalizationEnabled bool `json:"cspInstrumentalizationEnabled"` + TracingInstrumentalizationEnabled bool `json:"tracingInstrumentalizationEnabled"` + WebVitalsAttributionEnabled bool `json:"webVitalsAttributionEnabled"` } func (cfg *Cfg) readGrafanaJavascriptAgentConfig() { raw := cfg.Raw.Section("log.frontend") 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), - AllInstrumentationsEnabeld: raw.Key("instrumentations_all_enabled").MustBool(false), - ErrorInstrumentalizationEnabled: raw.Key("instrumentations_errors_enabled").MustBool(true), - ConsoleInstrumentalizationEnabled: raw.Key("instrumentations_console_enabled").MustBool(true), - WebVitalsInstrumentalizationEnabled: raw.Key("instrumentations_webvitals_enabled").MustBool(true), - TracingInstrumentalizationEnabled: raw.Key("instrumentations_tracing_enabled").MustBool(true), - InternalLoggerLevel: raw.Key("internal_logger_level").MustInt(0), - ApiKey: raw.Key("api_key").String(), + EndpointRPS: raw.Key("log_endpoint_requests_per_second_limit").MustInt(3), + EndpointBurst: raw.Key("log_endpoint_burst_limit").MustInt(15), + + // Faro config + Enabled: raw.Key("enabled").MustBool(false), + CustomEndpoint: raw.Key("custom_endpoint").MustString("/log-grafana-javascript-agent"), + ApiKey: raw.Key("api_key").String(), + InternalLoggerLevel: raw.Key("internal_logger_level").MustInt(0), + ConsoleInstrumentalizationEnabled: raw.Key("instrumentations_console_enabled").MustBool(true), + PerformanceInstrumentalizationEnabled: raw.Key("instrumentations_performance_enabled").MustBool(true), + CSPInstrumentalizationEnabled: raw.Key("instrumentations_csp_enabled").MustBool(true), + TracingInstrumentalizationEnabled: raw.Key("instrumentations_tracing_enabled").MustBool(true), + WebVitalsAttributionEnabled: raw.Key("web_vitals_attribution_enabled").MustBool(true), } } diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts index cf6bdb9da62..04fb4e88d33 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts @@ -2,7 +2,15 @@ import { BuildInfo } from '@grafana/data'; import { GrafanaEdition } from '@grafana/data/internal'; import { Faro, Instrumentation } from '@grafana/faro-core'; import * as faroWebSdkModule from '@grafana/faro-web-sdk'; -import { BrowserConfig, FetchTransport, SessionInstrumentation } from '@grafana/faro-web-sdk'; +import { + BrowserConfig, + FetchTransport, + SessionInstrumentation, + UserActionInstrumentation, + ErrorsInstrumentation, + WebVitalsInstrumentation, + ViewInstrumentation, +} from '@grafana/faro-web-sdk'; import { TracingInstrumentation } from '@grafana/faro-web-tracing'; import { config } from '@grafana/runtime'; @@ -71,21 +79,16 @@ describe('GrafanaJavascriptAgentEchoBackend', () => { }; const options: GrafanaJavascriptAgentBackendOptions = { - buildInfo, - app: { - version: '1.0', - }, - allInstrumentationsEnabled: true, - errorInstrumentalizationEnabled: true, - consoleInstrumentalizationEnabled: true, - webVitalsInstrumentalizationEnabled: true, - tracingInstrumentalizationEnabled: true, customEndpoint: '/log-grafana-javascript-agent', - user: { - email: 'darth.vader@sith.glx', - id: '504', - orgId: 1, - }, + + webVitalsAttribution: true, + consoleInstrumentalizationEnabled: true, + performanceInstrumentalizationEnabled: true, + cspInstrumentalizationEnabled: true, + tracingInstrumentalizationEnabled: true, + + buildInfo: buildInfo, + userIdentifier: 'abc123', ignoreUrls: [], }; @@ -116,13 +119,13 @@ describe('GrafanaJavascriptAgentEchoBackend', () => { //assert expect(initializeFaroMock).toHaveBeenCalledTimes(1); - expect(mockedSetUser).toHaveBeenCalledTimes(1); - expect(mockedSetUser).toHaveBeenCalledWith({ - id: '504', - attributes: { - orgId: '1', - }, - }); + expect(initializeFaroMock).toHaveBeenCalledWith( + expect.objectContaining({ + user: { + id: 'abc123', + }, + }) + ); }); test('will ensure the performance of TRACKING_URLS', async () => { @@ -139,22 +142,39 @@ describe('GrafanaJavascriptAgentEchoBackend', () => { it('correctly set instrumentation based on options', async () => { let opts = { ...options, - allInstrumentationsEnabled: false, - errorInstrumentalizationEnabled: false, consoleInstrumentalizationEnabled: false, - webVitalsInstrumentalizationEnabled: false, + performanceInstrumentalizationEnabled: false, + cspInstrumentalizationEnabled: false, tracingInstrumentalizationEnabled: false, }; new GrafanaJavascriptAgentBackend(opts); - expect(initializeFaroMock.mock.calls[0][0].instrumentations?.length).toEqual(1); - expect(initializeFaroMock.mock.calls[0][0].instrumentations?.[0]).toBeInstanceOf(SessionInstrumentation); + + let lastInstrumentations = initializeFaroMock.mock.calls.at(-1)?.[0].instrumentations; + expect(lastInstrumentations).toHaveLength(5); + expect(lastInstrumentations).toEqual( + expect.arrayContaining([ + expect.any(SessionInstrumentation), + expect.any(UserActionInstrumentation), + expect.any(ErrorsInstrumentation), + expect.any(WebVitalsInstrumentation), + expect.any(ViewInstrumentation), + ]) + ); opts.tracingInstrumentalizationEnabled = true; - new GrafanaJavascriptAgentBackend(opts); - expect(initializeFaroMock.mock.calls[1][0].instrumentations?.length).toEqual(2); - expect(initializeFaroMock.mock.calls[1][0].instrumentations?.[0]).toBeInstanceOf(TracingInstrumentation); - expect(initializeFaroMock.mock.calls[1][0].instrumentations?.[1]).toBeInstanceOf(SessionInstrumentation); + lastInstrumentations = initializeFaroMock.mock.calls.at(-1)?.[0].instrumentations; + expect(lastInstrumentations).toHaveLength(6); + expect(lastInstrumentations).toEqual( + expect.arrayContaining([ + expect.any(SessionInstrumentation), + expect.any(UserActionInstrumentation), + expect.any(ErrorsInstrumentation), + expect.any(WebVitalsInstrumentation), + expect.any(ViewInstrumentation), + expect.any(TracingInstrumentation), + ]) + ); }); it('should use a beforeSend handler', () => { diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts index e2ec294ff23..386caff9f3b 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts @@ -1,23 +1,18 @@ import { BuildInfo, escapeRegex } from '@grafana/data'; -import { BaseTransport, defaultInternalLoggerLevel } from '@grafana/faro-core'; +import { BaseTransport, defaultInternalLoggerLevel, InternalLoggerLevel } from '@grafana/faro-core'; import { initializeFaro, BrowserConfig, - ErrorsInstrumentation, - ConsoleInstrumentation, - WebVitalsInstrumentation, - SessionInstrumentation, FetchTransport, - type Instrumentation, getWebInstrumentations, - Config, + type Instrumentation, } from '@grafana/faro-web-sdk'; import { TracingInstrumentation } from '@grafana/faro-web-tracing'; import { EchoBackend, EchoEvent, EchoEventType } from '@grafana/runtime'; import { EchoSrvTransport } from './EchoSrvTransport'; import { beforeSendHandler } from './beforeSendHandler'; -import { GrafanaJavascriptAgentEchoEvent, User } from './types'; +import { GrafanaJavascriptAgentEchoEvent } from './types'; function isCrossOriginIframe() { try { @@ -27,15 +22,19 @@ function isCrossOriginIframe() { } } -export interface GrafanaJavascriptAgentBackendOptions extends BrowserConfig { - buildInfo: BuildInfo; - customEndpoint: string; - user: User; - allInstrumentationsEnabled: boolean; - errorInstrumentalizationEnabled: boolean; +export interface GrafanaJavascriptAgentBackendOptions { + apiKey?: string; + customEndpoint?: string; + internalLoggerLevel?: InternalLoggerLevel; + + webVitalsAttribution: boolean; consoleInstrumentalizationEnabled: boolean; - webVitalsInstrumentalizationEnabled: boolean; + performanceInstrumentalizationEnabled: boolean; + cspInstrumentalizationEnabled: boolean; tracingInstrumentalizationEnabled: boolean; + + buildInfo: BuildInfo; + userIdentifier: string; ignoreUrls: RegExp[]; } @@ -49,62 +48,52 @@ export class GrafanaJavascriptAgentBackend implements EchoBackend { supportedEvents = [EchoEventType.GrafanaJavascriptAgent]; - private faroInstance; constructor(public options: GrafanaJavascriptAgentBackendOptions) { // configure instrumentations. - const instrumentations: Instrumentation[] = []; - - const ignoreUrls = [ - new RegExp(`.*${escapeRegex(options.customEndpoint)}.*`), - ...TRACKING_URLS, - ...options.ignoreUrls, + const instrumentations: Instrumentation[] = [ + ...getWebInstrumentations({ + captureConsole: options.consoleInstrumentalizationEnabled, + enablePerformanceInstrumentation: options.performanceInstrumentalizationEnabled, + enableContentSecurityPolicyInstrumentation: options.cspInstrumentalizationEnabled, + }), ]; + if (options.tracingInstrumentalizationEnabled) { + instrumentations.push(new TracingInstrumentation()); + } + + const ignoreUrls = [...TRACKING_URLS, ...options.ignoreUrls]; + if (options.customEndpoint) { + ignoreUrls.unshift(new RegExp(`.*${escapeRegex(options.customEndpoint)}.*`)); + } + const transports: BaseTransport[] = [new EchoSrvTransport({ ignoreUrls })]; - const consoleInstrumentationOptions: Config['consoleInstrumentation'] = - options.allInstrumentationsEnabled || options.consoleInstrumentalizationEnabled - ? { - serializeErrors: true, - } - : {}; // If in cross origin iframe, default to writing to instance logging endpoint if (options.customEndpoint && !isCrossOriginIframe()) { transports.push(new FetchTransport({ url: options.customEndpoint, apiKey: options.apiKey })); } - if (options.errorInstrumentalizationEnabled) { - instrumentations.push(new ErrorsInstrumentation()); - } - if (options.consoleInstrumentalizationEnabled) { - instrumentations.push(new ConsoleInstrumentation()); - } - if (options.webVitalsInstrumentalizationEnabled) { - instrumentations.push(new WebVitalsInstrumentation()); - } - if (options.tracingInstrumentalizationEnabled) { - instrumentations.push(new TracingInstrumentation()); - } - - // session instrumentation must be added! - instrumentations.push(new SessionInstrumentation()); - // initialize GrafanaJavascriptAgent so it can set up its hooks and start collecting errors const grafanaJavaScriptAgentOptions: BrowserConfig = { - globalObjectKey: options.globalObjectKey || 'faro', - preventGlobalExposure: options.preventGlobalExposure || false, app: { name: 'grafana-frontend', version: options.buildInfo.version, environment: options.buildInfo.env, }, - instrumentations: options.allInstrumentationsEnabled - ? [...getWebInstrumentations(), new TracingInstrumentation()] - : instrumentations, - consoleInstrumentation: consoleInstrumentationOptions, - trackWebVitalsAttribution: options.webVitalsInstrumentalizationEnabled || options.allInstrumentationsEnabled, + + user: { + id: options.userIdentifier, + }, + + instrumentations: instrumentations, transports, + + consoleInstrumentation: { + serializeErrors: true, + }, + trackWebVitalsAttribution: options.webVitalsAttribution, ignoreErrors: [ 'ResizeObserver loop limit exceeded', 'ResizeObserver loop completed', @@ -118,19 +107,11 @@ export class GrafanaJavascriptAgentBackend batching: { sendTimeout: 1000, }, - internalLoggerLevel: options.internalLoggerLevel || defaultInternalLoggerLevel, beforeSend: beforeSendHandler, + internalLoggerLevel: options.internalLoggerLevel ?? defaultInternalLoggerLevel, }; - this.faroInstance = initializeFaro(grafanaJavaScriptAgentOptions); - if (options.user) { - this.faroInstance.api.setUser({ - id: options.user.id, - attributes: { - orgId: String(options.user.orgId) || '', - }, - }); - } + initializeFaro(grafanaJavaScriptAgentOptions); } // noop because the EchoSrvTransport registered in Faro will already broadcast all signals emitted by the Faro API diff --git a/public/app/core/services/echo/init.ts b/public/app/core/services/echo/init.ts index f25e13f2325..6b13b4c4200 100644 --- a/public/app/core/services/echo/init.ts +++ b/public/app/core/services/echo/init.ts @@ -97,17 +97,18 @@ async function initFaroBackend() { registerEchoBackend( new GrafanaJavascriptAgentBackend({ - ...config.grafanaJavascriptAgent, - app: { - version: config.buildInfo.version, - environment: config.buildInfo.env, - }, buildInfo: config.buildInfo, - user: { - id: String(contextSrv.user?.id), - email: contextSrv.user?.email, - }, + userIdentifier: contextSrv.user.analytics.identifier, ignoreUrls: rudderstackUrls, + + apiKey: config.grafanaJavascriptAgent.apiKey, + customEndpoint: config.grafanaJavascriptAgent.customEndpoint, + consoleInstrumentalizationEnabled: config.grafanaJavascriptAgent.consoleInstrumentalizationEnabled, + performanceInstrumentalizationEnabled: config.grafanaJavascriptAgent.performanceInstrumentalizationEnabled, + cspInstrumentalizationEnabled: config.grafanaJavascriptAgent.cspInstrumentalizationEnabled, + tracingInstrumentalizationEnabled: config.grafanaJavascriptAgent.tracingInstrumentalizationEnabled, + webVitalsAttribution: config.grafanaJavascriptAgent.webVitalsAttribution, + internalLoggerLevel: config.grafanaJavascriptAgent.internalLoggerLevel, }) ); }