diff --git a/packages/core/src/domain/telemetry/telemetry.spec.ts b/packages/core/src/domain/telemetry/telemetry.spec.ts index 27befba09b..35c5f26b66 100644 --- a/packages/core/src/domain/telemetry/telemetry.spec.ts +++ b/packages/core/src/domain/telemetry/telemetry.spec.ts @@ -1,4 +1,5 @@ import type { StackTrace } from '@datadog/browser-core' +import { addTelemetryError } from '@datadog/browser-core' import { NO_ERROR_STACK_PRESENT_MESSAGE } from '../error/error' import { callMonitored } from '../../tools/monitor' import type { ExperimentalFeature } from '../../tools/experimentalFeatures' @@ -20,6 +21,7 @@ function startAndSpyTelemetry(configuration?: Partial) { const telemetry = startTelemetry(TelemetryService.RUM, { maxTelemetryEventsPerPage: 7, telemetrySampleRate: 100, + telemetryUsageSampleRate: 100, ...configuration, } as Configuration) const notifySpy = jasmine.createSpy('notified') @@ -181,6 +183,34 @@ describe('telemetry', () => { }) }) + describe('deduplicating', () => { + it('should discard already seen telemetry', () => { + const { notifySpy } = startAndSpyTelemetry() + const fooError = new Error('foo') + const barError = new Error('bar') + + addTelemetryError(fooError) + addTelemetryError(fooError) + addTelemetryError(barError) + + expect(notifySpy).toHaveBeenCalledTimes(2) + expect(notifySpy.calls.argsFor(0)[0].telemetry.message).toEqual('foo') + expect(notifySpy.calls.argsFor(1)[0].telemetry.message).toEqual('bar') + }) + + it('should not consider a discarded event for the maxTelemetryEventsPerPage', () => { + const { notifySpy } = startAndSpyTelemetry({ maxTelemetryEventsPerPage: 2 }) + + addTelemetryUsage({ feature: 'stop-session' }) + addTelemetryUsage({ feature: 'stop-session' }) + addTelemetryUsage({ feature: 'start-session-replay-recording' }) + + expect(notifySpy).toHaveBeenCalledTimes(2) + expect(notifySpy.calls.argsFor(0)[0].telemetry.usage.feature).toEqual('stop-session') + expect(notifySpy.calls.argsFor(1)[0].telemetry.usage.feature).toEqual('start-session-replay-recording') + }) + }) + describe('excluded sites', () => { ;[ { site: INTAKE_SITE_US1_FED, enabled: false }, diff --git a/packages/core/src/domain/telemetry/telemetry.ts b/packages/core/src/domain/telemetry/telemetry.ts index fef7197b95..50fb55229b 100644 --- a/packages/core/src/domain/telemetry/telemetry.ts +++ b/packages/core/src/domain/telemetry/telemetry.ts @@ -64,6 +64,7 @@ let onRawTelemetryEventCollected: ((event: RawTelemetryEvent) => void) | undefin export function startTelemetry(telemetryService: TelemetryService, configuration: Configuration): Telemetry { let contextProvider: () => Context const observable = new Observable() + const alreadySeenEvents = new Set() const telemetryEnabled = !includes(TELEMETRY_EXCLUDED_SITES, configuration.site) && performDraw(configuration.telemetrySampleRate) @@ -76,10 +77,17 @@ export function startTelemetry(telemetryService: TelemetryService, configuration const runtimeEnvInfo = getRuntimeEnvInfo() onRawTelemetryEventCollected = (rawEvent: RawTelemetryEvent) => { - if (telemetryEnabledPerType[rawEvent.type!]) { + const stringifiedEvent = jsonStringify(rawEvent)! + if ( + telemetryEnabledPerType[rawEvent.type!] && + telemetryConfiguration.sentEventCount < telemetryConfiguration.maxEventsPerPage && + !alreadySeenEvents.has(stringifiedEvent) + ) { const event = toTelemetryEvent(telemetryService, rawEvent, runtimeEnvInfo) observable.notify(event) sendToExtension('telemetry', event) + alreadySeenEvents.add(stringifiedEvent) + telemetryConfiguration.sentEventCount += 1 } } startMonitorErrorCollection(addTelemetryError) @@ -197,8 +205,7 @@ export function addTelemetryUsage(usage: RawTelemetryUsage) { } function addTelemetry(event: RawTelemetryEvent) { - if (onRawTelemetryEventCollected && telemetryConfiguration.sentEventCount < telemetryConfiguration.maxEventsPerPage) { - telemetryConfiguration.sentEventCount += 1 + if (onRawTelemetryEventCollected) { onRawTelemetryEventCollected(event) } }