diff --git a/packages/core/src/transport/eventBridge.ts b/packages/core/src/transport/eventBridge.ts index ae93f6e1b0..6c02065281 100644 --- a/packages/core/src/transport/eventBridge.ts +++ b/packages/core/src/transport/eventBridge.ts @@ -8,6 +8,7 @@ export interface BrowserWindowWithEventBridge extends Window { export interface DatadogEventBridge { getCapabilities?(): string getPrivacyLevel?(): DefaultPrivacyLevel + getIsTraceSampled?(): boolean | null getAllowedWebViewHosts(): string send(msg: string): void } @@ -30,6 +31,12 @@ export function getEventBridge() { getPrivacyLevel() { return eventBridgeGlobal.getPrivacyLevel?.() }, + getIsTraceSampled() { + const value = eventBridgeGlobal.getIsTraceSampled?.() + if (value === true || value === false) { + return value + } + }, getAllowedWebViewHosts() { return JSON.parse(eventBridgeGlobal.getAllowedWebViewHosts()) as string[] }, diff --git a/packages/core/test/emulate/mockEventBridge.ts b/packages/core/test/emulate/mockEventBridge.ts index b9f76a82dd..e33924b434 100644 --- a/packages/core/test/emulate/mockEventBridge.ts +++ b/packages/core/test/emulate/mockEventBridge.ts @@ -7,12 +7,19 @@ export function mockEventBridge({ allowedWebViewHosts = [window.location.hostname], privacyLevel = DefaultPrivacyLevel.MASK, capabilities = [BridgeCapability.RECORDS], -}: { allowedWebViewHosts?: string[]; privacyLevel?: DefaultPrivacyLevel; capabilities?: BridgeCapability[] } = {}) { + isTraceSampled, +}: { + allowedWebViewHosts?: string[] + privacyLevel?: DefaultPrivacyLevel + capabilities?: BridgeCapability[] + isTraceSampled?: boolean +} = {}) { const eventBridge: DatadogEventBridge = { send: (_msg: string) => undefined, getAllowedWebViewHosts: () => JSON.stringify(allowedWebViewHosts), getCapabilities: () => JSON.stringify(capabilities), getPrivacyLevel: () => privacyLevel, + getIsTraceSampled: isTraceSampled !== undefined ? () => isTraceSampled : undefined, } ;(window as BrowserWindowWithEventBridge).DatadogEventBridge = eventBridge diff --git a/packages/rum-core/src/boot/preStartRum.spec.ts b/packages/rum-core/src/boot/preStartRum.spec.ts index deb8ab34c8..b56494c085 100644 --- a/packages/rum-core/src/boot/preStartRum.spec.ts +++ b/packages/rum-core/src/boot/preStartRum.spec.ts @@ -141,6 +141,27 @@ describe('preStartRum', () => { ) }) + it('should set traceSampleRate to 100 when the bridge reports trace is sampled', () => { + mockEventBridge({ isTraceSampled: true }) + const hybridInitConfiguration: HybridInitConfiguration = {} + strategy.init(hybridInitConfiguration as RumInitConfiguration, PUBLIC_API) + expect((strategy.initConfiguration as RumInitConfiguration)?.traceSampleRate).toEqual(100) + }) + + it('should set traceSampleRate to 0 when the bridge reports trace is not sampled', () => { + mockEventBridge({ isTraceSampled: false }) + const hybridInitConfiguration: HybridInitConfiguration = {} + strategy.init(hybridInitConfiguration as RumInitConfiguration, PUBLIC_API) + expect((strategy.initConfiguration as RumInitConfiguration)?.traceSampleRate).toEqual(0) + }) + + it('should preserve the original traceSampleRate when the bridge does not provide isTraceSampled', () => { + mockEventBridge() + const hybridInitConfiguration: HybridInitConfiguration = { traceSampleRate: 50 } + strategy.init(hybridInitConfiguration as RumInitConfiguration, PUBLIC_API) + expect((strategy.initConfiguration as RumInitConfiguration)?.traceSampleRate).toEqual(50) + }) + it('should initialize even if session cannot be handled', () => { mockEventBridge() spyOnProperty(document, 'cookie', 'get').and.returnValue('') diff --git a/packages/rum-core/src/boot/preStartRum.ts b/packages/rum-core/src/boot/preStartRum.ts index 58cf5ad3e4..efcbd350cb 100644 --- a/packages/rum-core/src/boot/preStartRum.ts +++ b/packages/rum-core/src/boot/preStartRum.ts @@ -326,12 +326,15 @@ export function createPreStartStrategy( } function overrideInitConfigurationForBridge(initConfiguration: RumInitConfiguration): RumInitConfiguration { + const bridge = getEventBridge() + const isTraceSampled = bridge?.getIsTraceSampled() return { ...initConfiguration, applicationId: '00000000-aaaa-0000-aaaa-000000000000', clientToken: 'empty', sessionSampleRate: 100, - defaultPrivacyLevel: initConfiguration.defaultPrivacyLevel ?? getEventBridge()?.getPrivacyLevel(), + defaultPrivacyLevel: initConfiguration.defaultPrivacyLevel ?? bridge?.getPrivacyLevel(), + traceSampleRate: isTraceSampled !== undefined ? (isTraceSampled ? 100 : 0) : initConfiguration.traceSampleRate, } } diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index a1d5042867..bb443e9de2 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -14,7 +14,7 @@ import { IntakeRegistry } from './intakeRegistry' import { flushEvents } from './flushEvents' import type { Servers } from './httpServers' import { getTestServers, waitForServersIdle } from './httpServers' -import type { CallerLocation, SetupFactory, SetupOptions, UrlHook } from './pageSetups' +import type { CallerLocation, EventBridgeOptions, SetupFactory, SetupOptions, UrlHook } from './pageSetups' import { html, DEFAULT_SETUPS, npmSetup, appSetup, formatConfiguration } from './pageSetups' import { createIntakeServerApp } from './serverApps/intake' import { createMockServerApp } from './serverApps/mock' @@ -52,7 +52,7 @@ class TestBuilder { private head = '' private body = '' private baseUrlHooks: UrlHook[] = [] - private eventBridge = false + private eventBridge: EventBridgeOptions | undefined private setups: Array<{ factory: SetupFactory; name?: string }> = DEFAULT_SETUPS private testFixture: typeof test = test private extension: { @@ -101,8 +101,8 @@ class TestBuilder { return this } - withEventBridge() { - this.eventBridge = true + withEventBridge(options: EventBridgeOptions = {}) { + this.eventBridge = options return this } diff --git a/test/e2e/lib/framework/pageSetups.ts b/test/e2e/lib/framework/pageSetups.ts index dac6bec2a2..61fbee2650 100644 --- a/test/e2e/lib/framework/pageSetups.ts +++ b/test/e2e/lib/framework/pageSetups.ts @@ -12,7 +12,7 @@ export interface SetupOptions { logsInit: (initConfiguration: LogsInitConfiguration) => void rumInit: (initConfiguration: RumInitConfiguration) => void remoteConfiguration?: RemoteConfiguration - eventBridge: boolean + eventBridge?: EventBridgeOptions head?: string body?: string baseUrlHooks: UrlHook[] @@ -41,6 +41,10 @@ export interface WorkerOptions { logsConfiguration?: LogsInitConfiguration } +export interface EventBridgeOptions { + isTraceSampled?: boolean +} + export type SetupFactory = (options: SetupOptions, servers: Servers) => string export type UrlHook = (baseUrl: URL, servers: Servers, options: SetupOptions) => void @@ -60,7 +64,7 @@ export function asyncSetup(options: SetupOptions, servers: Servers) { let footer = '' if (options.eventBridge) { - header += setupEventBridge(servers) + header += setupEventBridge(servers, options.eventBridge) } if (options.extension) { @@ -108,7 +112,7 @@ export function bundleSetup(options: SetupOptions, servers: Servers) { let header = options.head || '' if (options.eventBridge) { - header += setupEventBridge(servers) + header += setupEventBridge(servers, options.eventBridge) } if (options.extension) { @@ -143,7 +147,7 @@ export function npmSetup(options: SetupOptions, servers: Servers) { let header = options.head || '' if (options.eventBridge) { - header += setupEventBridge(servers) + header += setupEventBridge(servers, options.eventBridge) } if (options.extension) { @@ -180,7 +184,7 @@ export function appSetup(options: SetupOptions, servers: Servers, appName: strin let header = options.head || '' if (options.eventBridge) { - header += setupEventBridge(servers) + header += setupEventBridge(servers, options.eventBridge) } if (options.extension) { @@ -238,7 +242,7 @@ export function microfrontendSetup(options: SetupOptions, servers: Servers) { let header = options.head || '' if (options.eventBridge) { - header += setupEventBridge(servers) + header += setupEventBridge(servers, options.eventBridge) } if (options.extension) { @@ -278,7 +282,7 @@ function js(parts: readonly string[], ...vars: string[]) { return parts.reduce((full, part, index) => full + vars[index - 1] + part) } -function setupEventBridge(servers: Servers) { +function setupEventBridge(servers: Servers, options: EventBridgeOptions = {}) { const baseHostname = new URL(servers.base.origin).hostname // Send EventBridge events to the intake so we can inspect them in our E2E test cases. The URL @@ -289,6 +293,13 @@ function setupEventBridge(servers: Servers) { bridge: 'true', }).toString()}` + const isTraceSampledMethod = + options.isTraceSampled !== undefined + ? `getIsTraceSampled() { + return ${options.isTraceSampled} + },` + : '' + return html`