From a504f324f675410b858a99625cdf76e9a5d135c3 Mon Sep 17 00:00:00 2001 From: Aymeric Date: Wed, 6 Apr 2022 14:45:28 +0200 Subject: [PATCH 01/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20[RUMF-1178]=20improv?= =?UTF-8?q?e=20logs=20assembly=20part=202=20(#1463)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create network collection * Create runtime collection * Create a console collection * Create a report collection * Move the assemble function in its own module --- packages/core/src/tools/error.ts | 5 - packages/logs/src/boot/startLogs.spec.ts | 336 +----------------- packages/logs/src/boot/startLogs.ts | 116 +----- packages/logs/src/domain/assemble.spec.ts | 146 +++++++- packages/logs/src/domain/assemble.ts | 28 +- .../console/consoleCollection.spec.ts | 91 +++++ .../console/consoleCollection.ts | 44 +++ .../networkErrorCollection.spec.ts} | 62 ++-- .../networkError/networkErrorCollection.ts} | 30 +- .../report/reportCollection.spec.ts | 83 +++++ .../logsCollection/report/reportCollection.ts | 49 +++ .../runtimeErrorCollection.spec.ts | 44 +++ .../runtimeError/runtimeErrorCollection.ts | 30 ++ packages/logs/src/domain/reportRawError.ts | 16 + 14 files changed, 580 insertions(+), 500 deletions(-) create mode 100644 packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts create mode 100644 packages/logs/src/domain/logsCollection/console/consoleCollection.ts rename packages/logs/src/domain/{trackNetworkError.spec.ts => logsCollection/networkError/networkErrorCollection.spec.ts} (82%) rename packages/logs/src/domain/{trackNetworkError.ts => logsCollection/networkError/networkErrorCollection.ts} (88%) create mode 100644 packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts create mode 100644 packages/logs/src/domain/logsCollection/report/reportCollection.ts create mode 100644 packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts create mode 100644 packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.ts create mode 100644 packages/logs/src/domain/reportRawError.ts diff --git a/packages/core/src/tools/error.ts b/packages/core/src/tools/error.ts index 6ad1bcc11c..8371fab46d 100644 --- a/packages/core/src/tools/error.ts +++ b/packages/core/src/tools/error.ts @@ -10,11 +10,6 @@ export interface RawError { type?: string stack?: string source: ErrorSource - resource?: { - url: string - statusCode: number - method: string - } originalError?: unknown handling?: ErrorHandling handlingStack?: string diff --git a/packages/logs/src/boot/startLogs.spec.ts b/packages/logs/src/boot/startLogs.spec.ts index 3b1ddac7b0..2e4a3a3bc8 100644 --- a/packages/logs/src/boot/startLogs.spec.ts +++ b/packages/logs/src/boot/startLogs.spec.ts @@ -1,30 +1,12 @@ -import type { ConsoleLog, Context, RawError, RelativeTime, RawReport, TimeStamp } from '@datadog/browser-core' -import { - ErrorSource, - noop, - Observable, - ONE_MINUTE, - resetExperimentalFeatures, - updateExperimentalFeatures, - getTimeStamp, - stopSessionManager, - display, - initConsoleObservable, -} from '@datadog/browser-core' +import type { Context, TimeStamp } from '@datadog/browser-core' +import { noop, stopSessionManager } from '@datadog/browser-core' import sinon from 'sinon' -import type { Clock } from '../../../core/test/specHelper' -import { - deleteEventBridgeStub, - initEventBridgeStub, - mockClock, - stubEndpointBuilder, -} from '../../../core/test/specHelper' -import { stubReportingObserver } from '../../../core/test/stubReportApis' +import { deleteEventBridgeStub, initEventBridgeStub, stubEndpointBuilder } from '../../../core/test/specHelper' import type { LogsConfiguration } from '../domain/configuration' import { validateAndBuildLogsConfiguration } from '../domain/configuration' import type { LogsMessage } from '../domain/logger' -import { StatusType, HandlerType } from '../domain/logger' +import { StatusType } from '../domain/logger' import type { LogsSessionManager } from '../domain/logsSessionManager' import type { Sender } from '../domain/sender' import { createSender } from '../domain/sender' @@ -61,28 +43,15 @@ describe('logs', () => { let baseConfiguration: LogsConfiguration let sessionIsTracked: boolean let server: sinon.SinonFakeServer - let rawErrorObservable: Observable - let reportObservable: Observable - let consoleObservable: Observable - let consoleLogSpy: jasmine.Spy + const sessionManager: LogsSessionManager = { findTrackedSession: () => (sessionIsTracked ? { id: SESSION_ID } : undefined), } - let stopLogs = noop const startLogs = ({ - sender = createSender(noop), configuration: configurationOverrides, }: { sender?: Sender; configuration?: Partial } = {}) => { const configuration = { ...baseConfiguration, ...configurationOverrides } - const startLogs = doStartLogs( - configuration, - rawErrorObservable, - consoleObservable, - reportObservable, - sessionManager, - sender - ) - stopLogs = startLogs.stop + const startLogs = doStartLogs(configuration, sessionManager, createSender(noop)) return startLogs.send } @@ -93,11 +62,7 @@ describe('logs', () => { maxBatchSize: 1, } sessionIsTracked = true - rawErrorObservable = new Observable() - consoleObservable = new Observable() - reportObservable = new Observable() server = sinon.fakeServer.create() - consoleLogSpy = spyOn(console, 'log').and.callFake(() => true) }) afterEach(() => { @@ -105,7 +70,6 @@ describe('logs', () => { delete window.DD_RUM deleteEventBridgeStub() stopSessionManager() - stopLogs() }) describe('request', () => { @@ -135,45 +99,6 @@ describe('logs', () => { }) }) - it('should include RUM context', () => { - window.DD_RUM = { - getInternalContext() { - return { view: { url: 'http://from-rum-context.com', id: 'view-id' } } - }, - } - const sendLog = startLogs() - sendLog(DEFAULT_MESSAGE, {}) - - expect(getLoggedMessage(server, 0).view).toEqual({ - id: 'view-id', - url: 'http://from-rum-context.com', - }) - }) - - it('should use the rum internal context related to the error time', () => { - window.DD_RUM = { - getInternalContext(startTime) { - return { - foo: startTime === 1234 ? 'b' : 'a', - } - }, - } - let sendLogStrategy: (message: LogsMessage, currentContext: Context) => void = noop - const sendLog = (message: LogsMessage) => { - sendLogStrategy(message, {}) - } - sendLogStrategy = startLogs({ sender: createSender(sendLog) }) - - rawErrorObservable.notify({ - message: 'error!', - source: ErrorSource.SOURCE, - startClocks: { relative: 1234 as RelativeTime, timeStamp: getTimeStamp(1234 as RelativeTime) }, - type: 'Error', - }) - - expect(getLoggedMessage(server, 0).foo).toBe('b') - }) - it('should all use the same batch', () => { const sendLog = startLogs({ configuration: { maxBatchSize: 3 } }) sendLog(DEFAULT_MESSAGE, {}) @@ -197,130 +122,6 @@ describe('logs', () => { event: jasmine.objectContaining({ message: 'message' }), }) }) - - it('should not print the log twice when console handler is enabled', () => { - const sender = createSender(noop) - const logErrorSpy = spyOn(sender, 'sendToHttp') - const displaySpy = spyOn(display, 'log') - - consoleObservable = initConsoleObservable(['log']) - startLogs({ sender }) - sender.setHandler([HandlerType.console]) - /* eslint-disable-next-line no-console */ - console.log('foo', 'bar') - - expect(logErrorSpy).toHaveBeenCalled() - expect(consoleLogSpy).toHaveBeenCalledTimes(1) - expect(displaySpy).not.toHaveBeenCalled() - - resetExperimentalFeatures() - }) - - it('should send console logs when ff forward-logs is enabled', () => { - const sender = createSender(noop) - const logErrorSpy = spyOn(sender, 'sendToHttp') - - updateExperimentalFeatures(['forward-logs']) - const { stop } = originalStartLogs( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, - sender - ) - - /* eslint-disable-next-line no-console */ - console.log('foo', 'bar') - - expect(logErrorSpy).toHaveBeenCalled() - expect(consoleLogSpy).toHaveBeenCalled() - - resetExperimentalFeatures() - stop() - }) - - it('should not send console logs when ff forward-logs is disabled', () => { - const sender = createSender(noop) - const logErrorSpy = spyOn(sender, 'sendToHttp') - - const { stop } = originalStartLogs( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, - sender - ) - - /* eslint-disable-next-line no-console */ - console.log('foo', 'bar') - - expect(logErrorSpy).not.toHaveBeenCalled() - expect(consoleLogSpy).toHaveBeenCalled() - stop() - }) - }) - - describe('reports', () => { - let sender: Sender - let logErrorSpy: jasmine.Spy - let reportingObserverStub: ReturnType - - beforeEach(() => { - sender = createSender(noop) - logErrorSpy = spyOn(sender, 'sendToHttp') - reportingObserverStub = stubReportingObserver() - }) - - afterEach(() => { - reportingObserverStub.reset() - }) - - it('should send reports when ff forward-reports is enabled', () => { - updateExperimentalFeatures(['forward-reports']) - const { stop } = originalStartLogs( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, - sender - ) - - reportingObserverStub.raiseReport('intervention') - - expect(logErrorSpy).toHaveBeenCalled() - - resetExperimentalFeatures() - stop() - }) - - it('should not send reports when ff forward-reports is disabled', () => { - const { stop } = originalStartLogs( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, - sender - ) - reportingObserverStub.raiseReport('intervention') - - expect(logErrorSpy).not.toHaveBeenCalled() - stop() - }) - - it('should not send reports when forwardReports init option not specified', () => { - const { stop } = originalStartLogs(validateAndBuildLogsConfiguration({ ...initConfiguration })!, sender) - reportingObserverStub.raiseReport('intervention') - - expect(logErrorSpy).not.toHaveBeenCalled() - stop() - }) - - it('should add the source file information to the message for non error reports', () => { - updateExperimentalFeatures(['forward-reports']) - const { stop } = originalStartLogs( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['deprecation'] })!, - sender - ) - - reportingObserverStub.raiseReport('deprecation') - - expect(logErrorSpy).toHaveBeenCalledOnceWith( - 'deprecation: foo bar Found in http://foo.bar/index.js:20:10', - undefined, - 'warn' - ) - - resetExperimentalFeatures() - stop() - }) }) describe('sampling', () => { @@ -328,18 +129,16 @@ describe('logs', () => { const sendSpy = spyOn(initEventBridgeStub(), 'send') let configuration = { ...baseConfiguration, sampleRate: 0 } - let { send, stop } = originalStartLogs(configuration, createSender(noop)) + let { send } = originalStartLogs(configuration, createSender(noop)) send(DEFAULT_MESSAGE, {}) expect(sendSpy).not.toHaveBeenCalled() - stop() configuration = { ...baseConfiguration, sampleRate: 100 } - ;({ send, stop } = originalStartLogs(configuration, createSender(noop))) + ;({ send } = originalStartLogs(configuration, createSender(noop))) send(DEFAULT_MESSAGE, {}) expect(sendSpy).toHaveBeenCalled() - stop() }) }) @@ -374,123 +173,4 @@ describe('logs', () => { expect(server.requests.length).toEqual(2) }) }) - - describe('error collection', () => { - it('should send log errors', () => { - const sendLogSpy = jasmine.createSpy() - startLogs({ sender: createSender(sendLogSpy) }) - - rawErrorObservable.notify({ - message: 'error!', - source: ErrorSource.SOURCE, - startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp }, - type: 'Error', - }) - - expect(sendLogSpy).toHaveBeenCalled() - expect(sendLogSpy.calls.first().args).toEqual([ - { - date: 123456789 as TimeStamp, - error: { origin: ErrorSource.SOURCE, kind: 'Error', stack: undefined }, - message: 'error!', - status: StatusType.error, - }, - ]) - }) - }) - - describe('logs limitation', () => { - let clock: Clock - const configuration = { eventRateLimiterThreshold: 1 } - beforeEach(() => { - clock = mockClock() - }) - - afterEach(() => { - clock.cleanup() - }) - ;[ - { status: StatusType.error, message: 'Reached max number of errors by minute: 1' }, - { status: StatusType.warn, message: 'Reached max number of warns by minute: 1' }, - { status: StatusType.info, message: 'Reached max number of infos by minute: 1' }, - { status: StatusType.debug, message: 'Reached max number of debugs by minute: 1' }, - { status: 'unknown' as StatusType, message: 'Reached max number of customs by minute: 1' }, - ].forEach(({ status, message }) => { - it(`stops sending ${status} logs when reaching the limit`, () => { - const sendLogSpy = jasmine.createSpy<(message: LogsMessage & { foo?: string }) => void>() - const sendLog = startLogs({ sender: createSender(sendLogSpy), configuration }) - sendLog({ message: 'foo', status }, {}) - sendLog({ message: 'bar', status }, {}) - - expect(server.requests.length).toEqual(1) - expect(getLoggedMessage(server, 0).message).toBe('foo') - expect(sendLogSpy).toHaveBeenCalledOnceWith({ - message, - status: StatusType.error, - error: { - origin: ErrorSource.AGENT, - kind: undefined, - stack: undefined, - }, - date: Date.now(), - }) - }) - - it(`does not take discarded ${status} logs into account`, () => { - const sendLogSpy = jasmine.createSpy<(message: LogsMessage & { foo?: string }) => void>() - const sendLog = startLogs({ - sender: createSender(sendLogSpy), - configuration: { - ...configuration, - beforeSend(event) { - if (event.message === 'discard me') { - return false - } - }, - }, - }) - sendLog({ message: 'discard me', status }, {}) - sendLog({ message: 'discard me', status }, {}) - sendLog({ message: 'discard me', status }, {}) - sendLog({ message: 'foo', status }, {}) - - expect(server.requests.length).toEqual(1) - expect(getLoggedMessage(server, 0).message).toBe('foo') - expect(sendLogSpy).not.toHaveBeenCalled() - }) - - it(`allows to send new ${status}s after a minute`, () => { - const sendLog = startLogs({ configuration }) - sendLog({ message: 'foo', status }, {}) - sendLog({ message: 'bar', status }, {}) - clock.tick(ONE_MINUTE) - sendLog({ message: 'baz', status }, {}) - - expect(server.requests.length).toEqual(2) - expect(getLoggedMessage(server, 0).message).toBe('foo') - expect(getLoggedMessage(server, 1).message).toBe('baz') - }) - - it('allows to send logs with a different status when reaching the limit', () => { - const otherLogStatus = status === StatusType.error ? 'other' : StatusType.error - const sendLog = startLogs({ configuration }) - sendLog({ message: 'foo', status }, {}) - sendLog({ message: 'bar', status }, {}) - sendLog({ message: 'baz', status: otherLogStatus as StatusType }, {}) - - expect(server.requests.length).toEqual(2) - expect(getLoggedMessage(server, 0).message).toBe('foo') - expect(getLoggedMessage(server, 1).message).toBe('baz') - }) - }) - - it('two different custom statuses are accounted by the same limit', () => { - const sendLog = startLogs({ configuration }) - sendLog({ message: 'foo', status: 'foo' as StatusType }, {}) - sendLog({ message: 'bar', status: 'bar' as StatusType }, {}) - - expect(server.requests.length).toEqual(1) - expect(getLoggedMessage(server, 0).message).toBe('foo') - }) - }) }) diff --git a/packages/logs/src/boot/startLogs.ts b/packages/logs/src/boot/startLogs.ts index a31bdce726..0eb97e11db 100644 --- a/packages/logs/src/boot/startLogs.ts +++ b/packages/logs/src/boot/startLogs.ts @@ -1,43 +1,22 @@ -import type { ConsoleLog, Context, RawError, MonitoringMessage, TelemetryEvent, RawReport } from '@datadog/browser-core' +import type { Context, MonitoringMessage, TelemetryEvent } from '@datadog/browser-core' import { areCookiesAuthorized, combine, - Observable, - trackRuntimeError, canUseEventBridge, getEventBridge, startInternalMonitoring, - RawReportType, - initReportObservable, - initConsoleObservable, - ConsoleApiName, - ErrorSource, - getFileFromStackTraceString, startBatchWithReplica, } from '@datadog/browser-core' -import { trackNetworkError } from '../domain/trackNetworkError' import type { LogsMessage } from '../domain/logger' -import { StatusType } from '../domain/logger' import type { LogsSessionManager } from '../domain/logsSessionManager' import { startLogsSessionManager, startLogsSessionManagerStub } from '../domain/logsSessionManager' import type { LogsConfiguration } from '../domain/configuration' -import type { LogsEvent } from '../logsEvent.types' import { buildAssemble, getRUMInternalContext } from '../domain/assemble' import type { Sender } from '../domain/sender' - -const LogStatusForApi = { - [ConsoleApiName.log]: StatusType.info, - [ConsoleApiName.debug]: StatusType.debug, - [ConsoleApiName.info]: StatusType.info, - [ConsoleApiName.warn]: StatusType.warn, - [ConsoleApiName.error]: StatusType.error, -} - -const LogStatusForReport = { - [RawReportType.cspViolation]: StatusType.error, - [RawReportType.intervention]: StatusType.error, - [RawReportType.deprecation]: StatusType.warn, -} +import { startConsoleCollection } from '../domain/logsCollection/console/consoleCollection' +import { startReportCollection } from '../domain/logsCollection/report/reportCollection' +import { startNetworkErrorCollection } from '../domain/logsCollection/networkError/networkErrorCollection' +import { startRuntimeErrorCollection } from '../domain/logsCollection/runtimeError/runtimeErrorCollection' export function startLogs(configuration: LogsConfiguration, sender: Sender) { const internalMonitoring = startLogsInternalMonitoring(configuration) @@ -61,21 +40,17 @@ export function startLogs(configuration: LogsConfiguration, sender: Sender) { }, })) - const rawErrorObservable = new Observable() - - if (configuration.forwardErrorsToLogs) { - trackRuntimeError(rawErrorObservable) - trackNetworkError(configuration, rawErrorObservable) - } - const consoleObservable = initConsoleObservable(configuration.forwardConsoleLogs) - const reportObservable = initReportObservable(configuration.forwardReports) + startNetworkErrorCollection(configuration, sender) + startRuntimeErrorCollection(configuration, sender) + startConsoleCollection(configuration, sender) + startReportCollection(configuration, sender) const session = areCookiesAuthorized(configuration.cookieOptions) && !canUseEventBridge() ? startLogsSessionManager(configuration) : startLogsSessionManagerStub(configuration) - return doStartLogs(configuration, rawErrorObservable, consoleObservable, reportObservable, session, sender) + return doStartLogs(configuration, session, sender) } function startLogsInternalMonitoring(configuration: LogsConfiguration) { @@ -103,15 +78,8 @@ function startLogsInternalMonitoring(configuration: LogsConfiguration) { return internalMonitoring } -export function doStartLogs( - configuration: LogsConfiguration, - rawErrorObservable: Observable, - consoleObservable: Observable, - reportObservable: Observable, - sessionManager: LogsSessionManager, - sender: Sender -) { - const assemble = buildAssemble(sessionManager, configuration, reportRawError) +export function doStartLogs(configuration: LogsConfiguration, sessionManager: LogsSessionManager, sender: Sender) { + const assemble = buildAssemble(sessionManager, configuration, sender) let onLogEventCollected: (message: Context) => void if (canUseEventBridge()) { @@ -126,67 +94,7 @@ export function doStartLogs( onLogEventCollected = (message) => batch.add(message) } - function reportRawError(error: RawError) { - const messageContext: Partial = { - date: error.startClocks.timeStamp, - error: { - kind: error.type, - origin: error.source, - stack: error.stack, - }, - } - if (error.resource) { - messageContext.http = { - method: error.resource.method as any, // Cast resource method because of case mismatch cf issue RUMF-1152 - status_code: error.resource.statusCode, - url: error.resource.url, - } - } - sender.sendToHttp(error.message, messageContext, StatusType.error) - } - - function reportConsoleLog(log: ConsoleLog) { - let messageContext: Partial | undefined - if (log.api === ConsoleApiName.error) { - messageContext = { - error: { - origin: ErrorSource.CONSOLE, - stack: log.stack, - }, - } - } - sender.sendToHttp(log.message, messageContext, LogStatusForApi[log.api]) - } - - function logReport(report: RawReport) { - let message = report.message - let messageContext: Partial | undefined - const logStatus = LogStatusForReport[report.type] - if (logStatus === StatusType.error) { - messageContext = { - error: { - kind: report.subtype, - origin: ErrorSource.REPORT, - stack: report.stack, - }, - } - } else if (report.stack) { - message += ` Found in ${getFileFromStackTraceString(report.stack)!}` - } - - sender.sendToHttp(message, messageContext, logStatus) - } - - const rawErrorSubscription = rawErrorObservable.subscribe(reportRawError) - const consoleSubscription = consoleObservable.subscribe(reportConsoleLog) - const reportSubscription = reportObservable.subscribe(logReport) - return { - stop: () => { - rawErrorSubscription.unsubscribe() - consoleSubscription.unsubscribe() - reportSubscription.unsubscribe() - }, send: (message: LogsMessage, currentContext: Context) => { const contextualizedMessage = assemble(message, currentContext) if (contextualizedMessage) { diff --git a/packages/logs/src/domain/assemble.spec.ts b/packages/logs/src/domain/assemble.spec.ts index 6c33d202bb..10050bbef9 100644 --- a/packages/logs/src/domain/assemble.spec.ts +++ b/packages/logs/src/domain/assemble.spec.ts @@ -1,13 +1,15 @@ -import type { Context } from '@datadog/browser-core' -import { noop } from '@datadog/browser-core' +import type { Context, RelativeTime } from '@datadog/browser-core' +import { ErrorSource, ONE_MINUTE, getTimeStamp, noop, clocksNow } from '@datadog/browser-core' import type { LogsEvent } from '../logsEvent.types' -import { stubEndpointBuilder } from '../../../core/test/specHelper' +import type { Clock } from '../../../core/test/specHelper' +import { mockClock } from '../../../core/test/specHelper' import { buildAssemble } from './assemble' import type { LogsConfiguration } from './configuration' import { validateAndBuildLogsConfiguration } from './configuration' import type { LogsMessage } from './logger' import { StatusType } from './logger' import type { LogsSessionManager } from './logsSessionManager' +import { createSender } from './sender' describe('assemble', () => { const initConfiguration = { clientToken: 'xxx', service: 'service' } @@ -20,22 +22,19 @@ describe('assemble', () => { let assemble: (message: LogsMessage, currentContext: Context) => Context | undefined let beforeSend: (event: LogsEvent) => void | boolean - let baseConfiguration: LogsConfiguration let sessionIsTracked: boolean + let sendLogSpy: jasmine.Spy beforeEach(() => { sessionIsTracked = true - baseConfiguration = { + sendLogSpy = jasmine.createSpy() + const configuration = { ...validateAndBuildLogsConfiguration(initConfiguration)!, - logsEndpointBuilder: stubEndpointBuilder('https://localhost/v1/input/log'), maxBatchSize: 1, + beforeSend: (x: LogsEvent) => beforeSend(x), } beforeSend = noop - assemble = buildAssemble( - sessionManager, - { ...baseConfiguration, beforeSend: (x: LogsEvent) => beforeSend(x) }, - noop - ) + assemble = buildAssemble(sessionManager, configuration, createSender(sendLogSpy)) window.DD_RUM = { getInternalContext: noop, } @@ -116,4 +115,129 @@ describe('assemble', () => { expect(assembledMessage!.foo).toBe('bar') }) + + it('should use the rum internal context related to the error time', () => { + window.DD_RUM = { + getInternalContext(startTime) { + return { + foo: startTime === 1234 ? 'b' : 'a', + } + }, + } + + const message = { ...DEFAULT_MESSAGE, date: getTimeStamp(1234 as RelativeTime) } + + const assembledMessage = assemble(message, {}) + + expect(assembledMessage!.foo).toBe('b') + }) + + it('should include RUM context', () => { + window.DD_RUM = { + getInternalContext() { + return { view: { url: 'http://from-rum-context.com', id: 'view-id' } } + }, + } + + const message = { ...DEFAULT_MESSAGE } + + const assembledMessage = assemble(message, {}) + + expect(assembledMessage!.view).toEqual({ + id: 'view-id', + url: 'http://from-rum-context.com', + }) + }) + + describe('logs limitation', () => { + let clock: Clock + beforeEach(() => { + clock = mockClock() + assemble = buildAssemble( + sessionManager, + { eventRateLimiterThreshold: 1, beforeSend: (x: LogsEvent) => beforeSend(x) } as LogsConfiguration, + createSender(sendLogSpy) + ) + }) + + afterEach(() => { + clock.cleanup() + }) + ;[ + { status: StatusType.error, message: 'Reached max number of errors by minute: 1' }, + { status: StatusType.warn, message: 'Reached max number of warns by minute: 1' }, + { status: StatusType.info, message: 'Reached max number of infos by minute: 1' }, + { status: StatusType.debug, message: 'Reached max number of debugs by minute: 1' }, + { status: 'unknown' as StatusType, message: 'Reached max number of customs by minute: 1' }, + ].forEach(({ status, message }) => { + it(`stops sending ${status} logs when reaching the limit`, () => { + const assembledMessage1 = assemble({ message: 'foo', status }, {}) + const assembledMessage2 = assemble({ message: 'bar', status }, {}) + + expect(assembledMessage1!.message).toBe('foo') + expect(assembledMessage2).toBeUndefined() + + expect(sendLogSpy).toHaveBeenCalledOnceWith({ + message, + status: StatusType.error, + date: clocksNow().timeStamp, + error: { + kind: undefined, + origin: ErrorSource.AGENT, + stack: undefined, + }, + }) + }) + + it(`does not take discarded ${status} logs into account`, () => { + const sendLogSpy = jasmine.createSpy<(message: LogsMessage & { foo?: string }) => void>() + beforeSend = (event) => { + if (event.message === 'discard me') { + return false + } + } + + const assembledMessage1 = assemble({ message: 'discard me', status }, {}) + const assembledMessage2 = assemble({ message: 'discard me', status }, {}) + const assembledMessage3 = assemble({ message: 'discard me', status }, {}) + const assembledMessage4 = assemble({ message: 'foo', status }, {}) + + expect(assembledMessage1).toBeUndefined() + expect(assembledMessage2).toBeUndefined() + expect(assembledMessage3).toBeUndefined() + expect(assembledMessage4!.message).toBe('foo') + expect(sendLogSpy).not.toHaveBeenCalled() + }) + + it(`allows to send new ${status}s after a minute`, () => { + const assembledMessage1 = assemble({ message: 'foo', status }, {}) + const assembledMessage2 = assemble({ message: 'bar', status }, {}) + clock.tick(ONE_MINUTE) + const assembledMessage3 = assemble({ message: 'baz', status }, {}) + + expect(assembledMessage2).toBeUndefined() + expect(assembledMessage1!.message).toBe('foo') + expect(assembledMessage3!.message).toBe('baz') + }) + + it('allows to send logs with a different status when reaching the limit', () => { + const otherLogStatus = status === StatusType.error ? 'other' : StatusType.error + const assembledMessage1 = assemble({ message: 'foo', status }, {}) + const assembledMessage2 = assemble({ message: 'bar', status }, {}) + const assembledMessage3 = assemble({ message: 'baz', status: otherLogStatus as StatusType }, {}) + + expect(assembledMessage2).toBeUndefined() + expect(assembledMessage1!.message).toBe('foo') + expect(assembledMessage3!.message).toBe('baz') + }) + }) + + it('two different custom statuses are accounted by the same limit', () => { + const assembledMessage1 = assemble({ message: 'foo', status: 'foo' as StatusType }, {}) + const assembledMessage2 = assemble({ message: 'bar', status: 'bar' as StatusType }, {}) + + expect(assembledMessage2).toBeUndefined() + expect(assembledMessage1!.message).toBe('foo') + }) + }) }) diff --git a/packages/logs/src/domain/assemble.ts b/packages/logs/src/domain/assemble.ts index 53700a20e5..6752aca548 100644 --- a/packages/logs/src/domain/assemble.ts +++ b/packages/logs/src/domain/assemble.ts @@ -4,26 +4,34 @@ import type { LogsConfiguration } from './configuration' import type { LogsMessage } from './logger' import { StatusType } from './logger' import type { LogsSessionManager } from './logsSessionManager' +import { reportRawError } from './reportRawError' +import type { Sender } from './sender' + +export function buildAssemble(sessionManager: LogsSessionManager, configuration: LogsConfiguration, sender: Sender) { + const reportAgentError = (error: RawError) => reportRawError(error, sender) -export function buildAssemble( - sessionManager: LogsSessionManager, - configuration: LogsConfiguration, - reportRawError: (error: RawError) => void -) { const logRateLimiters = { [StatusType.error]: createEventRateLimiter( StatusType.error, configuration.eventRateLimiterThreshold, - reportRawError + reportAgentError + ), + [StatusType.warn]: createEventRateLimiter( + StatusType.warn, + configuration.eventRateLimiterThreshold, + reportAgentError + ), + [StatusType.info]: createEventRateLimiter( + StatusType.info, + configuration.eventRateLimiterThreshold, + reportAgentError ), - [StatusType.warn]: createEventRateLimiter(StatusType.warn, configuration.eventRateLimiterThreshold, reportRawError), - [StatusType.info]: createEventRateLimiter(StatusType.info, configuration.eventRateLimiterThreshold, reportRawError), [StatusType.debug]: createEventRateLimiter( StatusType.debug, configuration.eventRateLimiterThreshold, - reportRawError + reportAgentError ), - ['custom']: createEventRateLimiter('custom', configuration.eventRateLimiterThreshold, reportRawError), + ['custom']: createEventRateLimiter('custom', configuration.eventRateLimiterThreshold, reportAgentError), } return (message: LogsMessage, currentContext: Context) => { diff --git a/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts b/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts new file mode 100644 index 0000000000..eb07187a33 --- /dev/null +++ b/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts @@ -0,0 +1,91 @@ +import { ErrorSource, resetExperimentalFeatures, updateExperimentalFeatures, display } from '@datadog/browser-core' +import { validateAndBuildLogsConfiguration } from '../../configuration' +import { HandlerType, StatusType } from '../../logger' +import { createSender } from '../../sender' +import { startConsoleCollection } from './consoleCollection' + +describe('error collection', () => { + const initConfiguration = { clientToken: 'xxx', service: 'service' } + let sendLogSpy: jasmine.Spy + let consoleLogSpy: jasmine.Spy + + beforeEach(() => { + sendLogSpy = jasmine.createSpy('sendLogSpy') + consoleLogSpy = spyOn(console, 'log').and.callFake(() => true) + spyOn(console, 'error').and.callFake(() => true) + }) + + it('should send console logs when ff forward-logs is enabled', () => { + updateExperimentalFeatures(['forward-logs']) + const { stop } = startConsoleCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, + createSender(sendLogSpy) + ) + + /* eslint-disable-next-line no-console */ + console.log('foo', 'bar') + + expect(sendLogSpy).toHaveBeenCalledWith({ + message: 'console log: foo bar', + status: StatusType.info, + }) + + expect(consoleLogSpy).toHaveBeenCalled() + + resetExperimentalFeatures() + stop() + }) + + it('should not send console logs when ff forward-logs is disabled', () => { + const { stop } = startConsoleCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, + createSender(sendLogSpy) + ) + + /* eslint-disable-next-line no-console */ + console.log('foo', 'bar') + + expect(sendLogSpy).not.toHaveBeenCalled() + expect(consoleLogSpy).toHaveBeenCalled() + + stop() + }) + + it('console error should have an error object defined', () => { + const { stop } = startConsoleCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, + createSender(sendLogSpy) + ) + + /* eslint-disable-next-line no-console */ + console.error('foo', 'bar') + + expect(sendLogSpy.calls.mostRecent().args[0].error).toEqual({ + origin: ErrorSource.CONSOLE, + stack: undefined, + }) + + stop() + }) + + it('should not print the log twice when console handler is enabled', () => { + updateExperimentalFeatures(['forward-logs']) + + const sender = createSender(sendLogSpy) + const displaySpy = spyOn(display, 'log') + const { stop } = startConsoleCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, + sender + ) + + sender.setHandler([HandlerType.console]) + /* eslint-disable-next-line no-console */ + console.log('foo', 'bar') + + expect(consoleLogSpy).toHaveBeenCalledTimes(1) + expect(displaySpy).not.toHaveBeenCalled() + + resetExperimentalFeatures() + stop() + }) +}) diff --git a/packages/logs/src/domain/logsCollection/console/consoleCollection.ts b/packages/logs/src/domain/logsCollection/console/consoleCollection.ts new file mode 100644 index 0000000000..f6c8bad399 --- /dev/null +++ b/packages/logs/src/domain/logsCollection/console/consoleCollection.ts @@ -0,0 +1,44 @@ +import type { Context, ClocksState, ConsoleLog } from '@datadog/browser-core' +import { ConsoleApiName, ErrorSource, initConsoleObservable } from '@datadog/browser-core' +import type { LogsEvent } from '../../../logsEvent.types' +import type { LogsConfiguration } from '../../configuration' +import { StatusType } from '../../logger' +import type { Sender } from '../../sender' + +export interface ProvidedError { + startClocks: ClocksState + error: unknown + context?: Context + handlingStack: string +} + +const LogStatusForApi = { + [ConsoleApiName.log]: StatusType.info, + [ConsoleApiName.debug]: StatusType.debug, + [ConsoleApiName.info]: StatusType.info, + [ConsoleApiName.warn]: StatusType.warn, + [ConsoleApiName.error]: StatusType.error, +} +export function startConsoleCollection(configuration: LogsConfiguration, sender: Sender) { + const consoleObservable = initConsoleObservable(configuration.forwardConsoleLogs) + const consoleSubscription = consoleObservable.subscribe(reportConsoleLog) + + function reportConsoleLog(log: ConsoleLog) { + let messageContext: Partial | undefined + if (log.api === ConsoleApiName.error) { + messageContext = { + error: { + origin: ErrorSource.CONSOLE, + stack: log.stack, + }, + } + } + sender.sendToHttp(log.message, messageContext, LogStatusForApi[log.api]) + } + + return { + stop: () => { + consoleSubscription.unsubscribe() + }, + } +} diff --git a/packages/logs/src/domain/trackNetworkError.spec.ts b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts similarity index 82% rename from packages/logs/src/domain/trackNetworkError.spec.ts rename to packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts index 9ca459cedf..00e239d1e1 100644 --- a/packages/logs/src/domain/trackNetworkError.spec.ts +++ b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts @@ -1,27 +1,28 @@ -import type { RawError } from '@datadog/browser-core' -import { isIE, Observable } from '@datadog/browser-core' -import type { FetchStub, FetchStubManager } from '../../../core/test/specHelper' -import { SPEC_ENDPOINTS, ResponseStub, stubFetch } from '../../../core/test/specHelper' -import type { LogsConfiguration } from './configuration' +import { isIE, ErrorSource } from '@datadog/browser-core' +import type { FetchStub, FetchStubManager } from '@datadog/browser-core/test/specHelper' +import { SPEC_ENDPOINTS, ResponseStub, stubFetch } from '@datadog/browser-core/test/specHelper' +import type { LogsConfiguration } from '../../configuration' +import { StatusType } from '../../logger' +import { createSender } from '../../sender' import { computeFetchErrorText, computeFetchResponseText, computeXhrResponseData, - trackNetworkError, -} from './trackNetworkError' + startNetworkErrorCollection, +} from './networkErrorCollection' const CONFIGURATION = { requestErrorResponseLengthLimit: 64, ...SPEC_ENDPOINTS, } as LogsConfiguration -describe('network error tracker', () => { - let errorObservableSpy: jasmine.Spy +describe('network error collection', () => { let fetchStub: FetchStub let fetchStubManager: FetchStubManager - let stopNetworkErrorTracking: () => void - let errorObservable: Observable + let stopNetworkErrorCollection: () => void + let sendLogSpy: jasmine.Spy + const FAKE_URL = 'http://fake.com/' const DEFAULT_REQUEST = { duration: 10, @@ -36,16 +37,14 @@ describe('network error tracker', () => { if (isIE()) { pending('no fetch support') } - errorObservable = new Observable() - errorObservableSpy = spyOn(errorObservable, 'notify') - + sendLogSpy = jasmine.createSpy('sendLogSpy') fetchStubManager = stubFetch() - ;({ stop: stopNetworkErrorTracking } = trackNetworkError(CONFIGURATION, errorObservable)) + ;({ stop: stopNetworkErrorCollection } = startNetworkErrorCollection(CONFIGURATION, createSender(sendLogSpy))) fetchStub = window.fetch as FetchStub }) afterEach(() => { - stopNetworkErrorTracking() + stopNetworkErrorCollection() fetchStubManager.reset() }) @@ -53,16 +52,19 @@ describe('network error tracker', () => { fetchStub(FAKE_URL).resolveWith(DEFAULT_REQUEST) fetchStubManager.whenAllComplete(() => { - expect(errorObservableSpy).toHaveBeenCalledWith({ + expect(sendLogSpy).toHaveBeenCalledWith({ message: 'Fetch error GET http://fake.com/', - resource: { + date: jasmine.any(Number), + status: StatusType.error, + error: { + origin: ErrorSource.NETWORK, + stack: 'Server error', + }, + http: { method: 'GET', - statusCode: 503, + status_code: 503, url: 'http://fake.com/', }, - source: 'network', - stack: 'Server error', - startClocks: jasmine.any(Object), }) done() }) @@ -72,16 +74,16 @@ describe('network error tracker', () => { fetchStub('https://logs-intake.com/v1/input/send?foo=bar').resolveWith(DEFAULT_REQUEST) fetchStubManager.whenAllComplete(() => { - expect(errorObservableSpy).not.toHaveBeenCalled() + expect(sendLogSpy).not.toHaveBeenCalled() done() }) }) - it('should track aborted requests ', (done) => { + it('should track aborted requests', (done) => { fetchStub(FAKE_URL).abort() fetchStubManager.whenAllComplete(() => { - expect(errorObservableSpy).toHaveBeenCalled() + expect(sendLogSpy).toHaveBeenCalled() done() }) }) @@ -90,7 +92,7 @@ describe('network error tracker', () => { fetchStub(FAKE_URL).resolveWith({ ...DEFAULT_REQUEST, status: 0 }) fetchStubManager.whenAllComplete(() => { - expect(errorObservableSpy).toHaveBeenCalled() + expect(sendLogSpy).toHaveBeenCalled() done() }) }) @@ -99,7 +101,7 @@ describe('network error tracker', () => { fetchStub(FAKE_URL).resolveWith({ ...DEFAULT_REQUEST, status: 400 }) fetchStubManager.whenAllComplete(() => { - expect(errorObservableSpy).not.toHaveBeenCalled() + expect(sendLogSpy).not.toHaveBeenCalled() done() }) }) @@ -108,7 +110,7 @@ describe('network error tracker', () => { fetchStub(FAKE_URL).resolveWith({ ...DEFAULT_REQUEST, status: 200 }) fetchStubManager.whenAllComplete(() => { - expect(errorObservableSpy).not.toHaveBeenCalled() + expect(sendLogSpy).not.toHaveBeenCalled() done() }) }) @@ -117,8 +119,8 @@ describe('network error tracker', () => { fetchStub(FAKE_URL).resolveWith({ ...DEFAULT_REQUEST, responseText: '' }) fetchStubManager.whenAllComplete(() => { - expect(errorObservableSpy).toHaveBeenCalled() - const stack = (errorObservableSpy.calls.mostRecent().args[0] as RawError).stack + expect(sendLogSpy).toHaveBeenCalled() + const stack = sendLogSpy.calls.mostRecent().args[0].error.stack expect(stack).toEqual('Failed to load') done() }) diff --git a/packages/logs/src/domain/trackNetworkError.ts b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts similarity index 88% rename from packages/logs/src/domain/trackNetworkError.ts rename to packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts index 178ce8274b..5e728b5b0f 100644 --- a/packages/logs/src/domain/trackNetworkError.ts +++ b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts @@ -1,4 +1,4 @@ -import type { FetchCompleteContext, Observable, RawError, XhrCompleteContext } from '@datadog/browser-core' +import type { FetchCompleteContext, XhrCompleteContext } from '@datadog/browser-core' import { ErrorSource, initXhrObservable, @@ -9,9 +9,12 @@ import { monitor, noop, } from '@datadog/browser-core' -import type { LogsConfiguration } from './configuration' +import type { LogsEvent } from '../../../logsEvent.types' +import type { LogsConfiguration } from '../../configuration' +import { StatusType } from '../../logger' +import type { Sender } from '../../sender' -export function trackNetworkError(configuration: LogsConfiguration, errorObservable: Observable) { +export function startNetworkErrorCollection(configuration: LogsConfiguration, sender: Sender) { const xhrSubscription = initXhrObservable().subscribe((context) => { if (context.state === 'complete') { handleCompleteRequest(RequestType.XHR, context) @@ -35,17 +38,20 @@ export function trackNetworkError(configuration: LogsConfiguration, errorObserva } function onResponseDataAvailable(responseData: unknown) { - errorObservable.notify({ - message: `${format(type)} error ${request.method} ${request.url}`, - resource: { - method: request.method, - statusCode: request.status, + const messageContext: Partial = { + date: request.startClocks.timeStamp, + error: { + origin: ErrorSource.NETWORK, + stack: (responseData as string) || 'Failed to load', + }, + http: { + method: request.method as any, // Cast resource method because of case mismatch cf issue RUMF-1152 + status_code: request.status, url: request.url, }, - source: ErrorSource.NETWORK, - stack: (responseData as string) || 'Failed to load', - startClocks: request.startClocks, - }) + } + + sender.sendToHttp(`${format(type)} error ${request.method} ${request.url}`, messageContext, StatusType.error) } } diff --git a/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts b/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts new file mode 100644 index 0000000000..a2cd23f728 --- /dev/null +++ b/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts @@ -0,0 +1,83 @@ +import { ErrorSource, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' +import { stubReportingObserver } from '@datadog/browser-core/test/stubReportApis' +import { validateAndBuildLogsConfiguration } from '../../configuration' +import { StatusType } from '../../logger' +import { createSender } from '../../sender' +import { startReportCollection } from './reportCollection' + +describe('reports', () => { + const initConfiguration = { clientToken: 'xxx', service: 'service' } + let sendLogSpy: jasmine.Spy + let reportingObserverStub: ReturnType + + beforeEach(() => { + sendLogSpy = jasmine.createSpy('sendLogSpy') + reportingObserverStub = stubReportingObserver() + }) + + afterEach(() => { + reportingObserverStub.reset() + }) + + it('should send reports when ff forward-reports is enabled', () => { + updateExperimentalFeatures(['forward-reports']) + const { stop } = startReportCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, + createSender(sendLogSpy) + ) + + reportingObserverStub.raiseReport('intervention') + expect(sendLogSpy).toHaveBeenCalledOnceWith({ + error: { + kind: 'NavigatorVibrate', + origin: ErrorSource.REPORT, + stack: jasmine.any(String), + }, + message: 'intervention: foo bar', + status: StatusType.error, + }) + + resetExperimentalFeatures() + stop() + }) + + it('should not send reports when ff forward-reports is disabled', () => { + const { stop } = startReportCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, + createSender(sendLogSpy) + ) + reportingObserverStub.raiseReport('intervention') + + expect(sendLogSpy).not.toHaveBeenCalled() + stop() + }) + + it('should not send reports when forwardReports init option not specified', () => { + const { stop } = startReportCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration })!, + createSender(sendLogSpy) + ) + reportingObserverStub.raiseReport('intervention') + + expect(sendLogSpy).not.toHaveBeenCalled() + stop() + }) + + it('should add the source file information to the message for non error reports', () => { + updateExperimentalFeatures(['forward-reports']) + const { stop } = startReportCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['deprecation'] })!, + createSender(sendLogSpy) + ) + + reportingObserverStub.raiseReport('deprecation') + + expect(sendLogSpy).toHaveBeenCalledOnceWith({ + message: 'deprecation: foo bar Found in http://foo.bar/index.js:20:10', + status: StatusType.warn, + }) + + resetExperimentalFeatures() + stop() + }) +}) diff --git a/packages/logs/src/domain/logsCollection/report/reportCollection.ts b/packages/logs/src/domain/logsCollection/report/reportCollection.ts new file mode 100644 index 0000000000..62caa24582 --- /dev/null +++ b/packages/logs/src/domain/logsCollection/report/reportCollection.ts @@ -0,0 +1,49 @@ +import type { Context, ClocksState, RawReport } from '@datadog/browser-core' +import { ErrorSource, RawReportType, getFileFromStackTraceString, initReportObservable } from '@datadog/browser-core' +import type { LogsEvent } from '../../../logsEvent.types' +import type { LogsConfiguration } from '../../configuration' +import { StatusType } from '../../logger' +import type { Sender } from '../../sender' + +export interface ProvidedError { + startClocks: ClocksState + error: unknown + context?: Context + handlingStack: string +} + +const LogStatusForReport = { + [RawReportType.cspViolation]: StatusType.error, + [RawReportType.intervention]: StatusType.error, + [RawReportType.deprecation]: StatusType.warn, +} + +export function startReportCollection(configuration: LogsConfiguration, sender: Sender) { + const reportObservable = initReportObservable(configuration.forwardReports) + const reportSubscription = reportObservable.subscribe(logReport) + + function logReport(report: RawReport) { + let message = report.message + let messageContext: Partial | undefined + const logStatus = LogStatusForReport[report.type] + if (logStatus === StatusType.error) { + messageContext = { + error: { + kind: report.subtype, + origin: ErrorSource.REPORT, + stack: report.stack, + }, + } + } else if (report.stack) { + message += ` Found in ${getFileFromStackTraceString(report.stack)!}` + } + + sender.sendToHttp(message, messageContext, logStatus) + } + + return { + stop: () => { + reportSubscription.unsubscribe() + }, + } +} diff --git a/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts new file mode 100644 index 0000000000..6b9a66f5a7 --- /dev/null +++ b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts @@ -0,0 +1,44 @@ +import { ErrorSource, Observable } from '@datadog/browser-core' +import type { RawError, RelativeTime, TimeStamp } from '@datadog/browser-core' +import type { LogsConfiguration } from '../../configuration' +import { createSender } from '../../sender' +import { StatusType } from '../../logger' +import { startRuntimeErrorCollection } from './runtimeErrorCollection' + +describe('runtime error collection', () => { + let rawErrorObservable: Observable + let sendLogSpy: jasmine.Spy + let stopRuntimeErrorCollection: () => void + beforeEach(() => { + rawErrorObservable = new Observable() + sendLogSpy = jasmine.createSpy('sendLogSpy') + ;({ stop: stopRuntimeErrorCollection } = startRuntimeErrorCollection( + {} as LogsConfiguration, + createSender(sendLogSpy), + rawErrorObservable + )) + }) + + afterEach(() => { + stopRuntimeErrorCollection() + }) + + it('should send runtime errors', () => { + rawErrorObservable.notify({ + message: 'error!', + source: ErrorSource.SOURCE, + startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp }, + type: 'Error', + }) + + expect(sendLogSpy).toHaveBeenCalled() + expect(sendLogSpy.calls.first().args).toEqual([ + { + date: 123456789 as TimeStamp, + error: { origin: ErrorSource.SOURCE, kind: 'Error', stack: undefined }, + message: 'error!', + status: StatusType.error, + }, + ]) + }) +}) diff --git a/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.ts b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.ts new file mode 100644 index 0000000000..2e1da2b745 --- /dev/null +++ b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.ts @@ -0,0 +1,30 @@ +import type { Context, RawError, ClocksState } from '@datadog/browser-core' +import { trackRuntimeError, Observable } from '@datadog/browser-core' +import type { LogsConfiguration } from '../../configuration' +import { reportRawError } from '../../reportRawError' +import type { Sender } from '../../sender' + +export interface ProvidedError { + startClocks: ClocksState + error: unknown + context?: Context + handlingStack: string +} + +export function startRuntimeErrorCollection( + configuration: LogsConfiguration, + sender: Sender, + rawErrorObservable = new Observable() +) { + if (configuration.forwardErrorsToLogs) { + trackRuntimeError(rawErrorObservable) + } + + const rawErrorSubscription = rawErrorObservable.subscribe((rawError) => reportRawError(rawError, sender)) + + return { + stop: () => { + rawErrorSubscription.unsubscribe() + }, + } +} diff --git a/packages/logs/src/domain/reportRawError.ts b/packages/logs/src/domain/reportRawError.ts new file mode 100644 index 0000000000..fcb4c71a6d --- /dev/null +++ b/packages/logs/src/domain/reportRawError.ts @@ -0,0 +1,16 @@ +import type { RawError } from '@datadog/browser-core' +import type { LogsEvent } from '../logsEvent.types' +import { StatusType } from './logger' +import type { Sender } from './sender' + +export function reportRawError(error: RawError, sender: Sender) { + const messageContext: Partial = { + date: error.startClocks.timeStamp, + error: { + kind: error.type, + origin: error.source, + stack: error.stack, + }, + } + sender.sendToHttp(error.message, messageContext, StatusType.error) +} From 7409e1bcd64b4877183ef55c13a71fe7dd4329d1 Mon Sep 17 00:00:00 2001 From: Aymeric Date: Thu, 7 Apr 2022 11:22:48 +0200 Subject: [PATCH 02/23] =?UTF-8?q?=E2=9C=A8=20=E2=9A=97=20=20[RUMF-1224]=20?= =?UTF-8?q?remove=20console=20APIs=20prefix=20(#1479)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove the console API prefix * Add origin to the root of the log --- .../domain/console/consoleObservable.spec.ts | 132 +++++++++--------- .../src/domain/console/consoleObservable.ts | 21 +-- packages/logs/src/boot/logsPublicApi.spec.ts | 20 ++- packages/logs/src/domain/logger.spec.ts | 25 +++- packages/logs/src/domain/logger.ts | 11 +- .../console/consoleCollection.spec.ts | 69 ++++++--- .../console/consoleCollection.ts | 9 +- .../networkErrorCollection.spec.ts | 14 +- .../networkError/networkErrorCollection.ts | 6 +- .../report/reportCollection.spec.ts | 32 ++--- .../logsCollection/report/reportCollection.ts | 14 +- .../runtimeErrorCollection.spec.ts | 17 ++- packages/logs/src/domain/reportRawError.ts | 6 +- packages/logs/src/logsEvent.types.ts | 4 + 14 files changed, 251 insertions(+), 129 deletions(-) diff --git a/packages/core/src/domain/console/consoleObservable.spec.ts b/packages/core/src/domain/console/consoleObservable.spec.ts index f65912a8ae..b42f6fba19 100644 --- a/packages/core/src/domain/console/consoleObservable.spec.ts +++ b/packages/core/src/domain/console/consoleObservable.spec.ts @@ -6,71 +6,75 @@ import { ConsoleApiName, initConsoleObservable } from './consoleObservable' // prettier: avoid formatting issue // cf https://github.com/prettier/prettier/issues/12211 -;[ConsoleApiName.log, ConsoleApiName.info, ConsoleApiName.warn, ConsoleApiName.debug, ConsoleApiName.error].forEach( - (api) => { - describe(`console ${api} observable`, () => { - let consoleStub: jasmine.Spy - let consoleSubscription: Subscription - let notifyLog: jasmine.Spy - - beforeEach(() => { - consoleStub = spyOn(console, api) - notifyLog = jasmine.createSpy('notifyLog') - - consoleSubscription = initConsoleObservable([api]).subscribe(notifyLog) - }) - - afterEach(() => { - consoleSubscription.unsubscribe() - }) - - it(`should notify ${api}`, () => { - console[api]('foo', 'bar') - - const consoleLog = notifyLog.calls.mostRecent().args[0] - - expect(consoleLog).toEqual( - jasmine.objectContaining({ - message: `console ${api}: foo bar`, - api, - }) - ) - }) - - it('should keep original behavior', () => { - console[api]('foo', 'bar') - - expect(consoleStub).toHaveBeenCalledWith('foo', 'bar') - }) - - it('should format error instance', () => { - console[api](new TypeError('hello')) - const consoleLog = notifyLog.calls.mostRecent().args[0] - expect(consoleLog.message).toBe(`console ${api}: TypeError: hello`) - }) - - it('should stringify object parameters', () => { - console[api]('Hello', { foo: 'bar' }) - const consoleLog = notifyLog.calls.mostRecent().args[0] - expect(consoleLog.message).toBe(`console ${api}: Hello {\n "foo": "bar"\n}`) - }) - - it('should allow multiple callers', () => { - const notifyOtherCaller = jasmine.createSpy('notifyOtherCaller') - const instrumentedConsoleApi = console[api] - const otherConsoleSubscription = initConsoleObservable([api]).subscribe(notifyOtherCaller) - - console[api]('foo', 'bar') - - expect(instrumentedConsoleApi).toEqual(console[api]) - expect(notifyLog).toHaveBeenCalledTimes(1) - expect(notifyOtherCaller).toHaveBeenCalledTimes(1) - - otherConsoleSubscription.unsubscribe() - }) +;[ + { api: ConsoleApiName.log, prefix: '' }, + { api: ConsoleApiName.info, prefix: '' }, + { api: ConsoleApiName.warn, prefix: '' }, + { api: ConsoleApiName.debug, prefix: '' }, + { api: ConsoleApiName.error, prefix: 'console error: ' }, +].forEach(({ api, prefix }) => { + describe(`console ${api} observable`, () => { + let consoleStub: jasmine.Spy + let consoleSubscription: Subscription + let notifyLog: jasmine.Spy + + beforeEach(() => { + consoleStub = spyOn(console, api) + notifyLog = jasmine.createSpy('notifyLog') + + consoleSubscription = initConsoleObservable([api]).subscribe(notifyLog) }) - } -) + + afterEach(() => { + consoleSubscription.unsubscribe() + }) + + it(`should notify ${api}`, () => { + console[api]('foo', 'bar') + + const consoleLog = notifyLog.calls.mostRecent().args[0] + + expect(consoleLog).toEqual( + jasmine.objectContaining({ + message: `${prefix}foo bar`, + api, + }) + ) + }) + + it('should keep original behavior', () => { + console[api]('foo', 'bar') + + expect(consoleStub).toHaveBeenCalledWith('foo', 'bar') + }) + + it('should format error instance', () => { + console[api](new TypeError('hello')) + const consoleLog = notifyLog.calls.mostRecent().args[0] + expect(consoleLog.message).toBe(`${prefix}TypeError: hello`) + }) + + it('should stringify object parameters', () => { + console[api]('Hello', { foo: 'bar' }) + const consoleLog = notifyLog.calls.mostRecent().args[0] + expect(consoleLog.message).toBe(`${prefix}Hello {\n "foo": "bar"\n}`) + }) + + it('should allow multiple callers', () => { + const notifyOtherCaller = jasmine.createSpy('notifyOtherCaller') + const instrumentedConsoleApi = console[api] + const otherConsoleSubscription = initConsoleObservable([api]).subscribe(notifyOtherCaller) + + console[api]('foo', 'bar') + + expect(instrumentedConsoleApi).toEqual(console[api]) + expect(notifyLog).toHaveBeenCalledTimes(1) + expect(notifyOtherCaller).toHaveBeenCalledTimes(1) + + otherConsoleSubscription.unsubscribe() + }) + }) +}) describe('console error observable', () => { let consoleSubscription: Subscription diff --git a/packages/core/src/domain/console/consoleObservable.ts b/packages/core/src/domain/console/consoleObservable.ts index 51239fb898..6d69ef1275 100644 --- a/packages/core/src/domain/console/consoleObservable.ts +++ b/packages/core/src/domain/console/consoleObservable.ts @@ -57,21 +57,22 @@ function createConsoleObservable(api: ConsoleApiName) { } function buildConsoleLog(params: unknown[], api: ConsoleApiName, handlingStack: string): ConsoleLog { - const log: ConsoleLog = { - message: [`console ${api}:` as unknown] - .concat(params) - .map((param) => formatConsoleParameters(param)) - .join(' '), - api, - } + // Todo: remove console error prefix in the next major version + let message = params.map((param) => formatConsoleParameters(param)).join(' ') + let stack if (api === ConsoleApiName.error) { const firstErrorParam = find(params, (param: unknown): param is Error => param instanceof Error) - log.stack = firstErrorParam ? toStackTraceString(computeStackTrace(firstErrorParam)) : undefined - log.handlingStack = handlingStack + stack = firstErrorParam ? toStackTraceString(computeStackTrace(firstErrorParam)) : undefined + message = `console error: ${message}` } - return log + return { + api, + message, + stack, + handlingStack, + } } function formatConsoleParameters(param: unknown) { diff --git a/packages/logs/src/boot/logsPublicApi.spec.ts b/packages/logs/src/boot/logsPublicApi.spec.ts index 6829900bd1..da38f9c901 100644 --- a/packages/logs/src/boot/logsPublicApi.spec.ts +++ b/packages/logs/src/boot/logsPublicApi.spec.ts @@ -1,5 +1,12 @@ import type { Context } from '@datadog/browser-core' -import { monitor, ONE_SECOND, display } from '@datadog/browser-core' +import { + monitor, + ONE_SECOND, + display, + ErrorSource, + updateExperimentalFeatures, + resetExperimentalFeatures, +} from '@datadog/browser-core' import type { Clock } from '../../../core/test/specHelper' import { deleteEventBridgeStub, initEventBridgeStub, mockClock } from '../../../core/test/specHelper' import type { HybridInitConfiguration, LogsInitConfiguration } from '../domain/configuration' @@ -209,6 +216,10 @@ describe('logs entry', () => { LOGS.init(DEFAULT_INIT_CONFIGURATION) }) + afterEach(() => { + resetExperimentalFeatures() + }) + it('logs a message', () => { LOGS.logger.log('message') @@ -227,6 +238,13 @@ describe('logs entry', () => { }) }) + it('logs a message with "logger" origin when ff forward-logs is enabled', () => { + updateExperimentalFeatures(['forward-logs']) + LOGS.logger.log('message') + + expect(getLoggedMessage(0).message.origin).toEqual(ErrorSource.LOGGER) + }) + it('returns cloned initial configuration', () => { expect(LOGS.getInitConfiguration()).toEqual(DEFAULT_INIT_CONFIGURATION) expect(LOGS.getInitConfiguration()).not.toBe(DEFAULT_INIT_CONFIGURATION) diff --git a/packages/logs/src/domain/logger.spec.ts b/packages/logs/src/domain/logger.spec.ts index 690b135dfd..cc1a5b7b70 100644 --- a/packages/logs/src/domain/logger.spec.ts +++ b/packages/logs/src/domain/logger.spec.ts @@ -1,4 +1,4 @@ -import { display } from '@datadog/browser-core' +import { display, ErrorSource, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' import type { LogsMessage } from './logger' import { HandlerType, Logger, STATUSES, StatusType } from './logger' import type { Sender } from './sender' @@ -19,6 +19,10 @@ describe('Logger', () => { logger = new Logger(sender) }) + afterEach(() => { + resetExperimentalFeatures() + }) + describe('log methods', () => { it("'logger.log' should have info status by default", () => { logger.log('message') @@ -26,6 +30,25 @@ describe('Logger', () => { expect(getLoggedMessage(0).status).toEqual(StatusType.info) }) + it("'logger.log' should set 'logger' origin when ff forward-logs enabled", () => { + updateExperimentalFeatures(['forward-logs']) + logger.log('message') + + expect(getLoggedMessage(0).origin).toEqual(ErrorSource.LOGGER) + }) + + it("'logger.log' should not set 'logger' origin when ff forward-logs disabled", () => { + logger.log('message') + expect(getLoggedMessage(0).origin).not.toBeDefined() + }) + + it("'logger.log' message context can override the 'logger' origin", () => { + updateExperimentalFeatures(['forward-logs']) + logger.log('message', { origin: 'foo' }) + + expect(getLoggedMessage(0).origin).toEqual('foo') + }) + STATUSES.forEach((status) => { it(`'logger.${status}' should have ${status} status`, () => { logger[status]('message') diff --git a/packages/logs/src/domain/logger.ts b/packages/logs/src/domain/logger.ts index d05ca0122c..fc2f1ae9c5 100644 --- a/packages/logs/src/domain/logger.ts +++ b/packages/logs/src/domain/logger.ts @@ -1,5 +1,5 @@ import type { ContextValue, TimeStamp } from '@datadog/browser-core' -import { combine, ErrorSource, monitored } from '@datadog/browser-core' +import { combine, ErrorSource, monitored, isExperimentalFeatureEnabled } from '@datadog/browser-core' import type { Sender } from './sender' export const StatusType = { @@ -32,7 +32,13 @@ export class Logger { @monitored log(message: string, messageContext?: object, status: StatusType = StatusType.info) { - this.sender.sendLog(message, messageContext, status) + let logOrigin + if (isExperimentalFeatureEnabled('forward-logs')) { + logOrigin = { + origin: ErrorSource.LOGGER, + } + } + this.sender.sendLog(message, combine(logOrigin, messageContext), status) } debug(message: string, messageContext?: object) { @@ -50,6 +56,7 @@ export class Logger { error(message: string, messageContext?: object) { const errorOrigin = { error: { + // Todo: remove error origin in the next major version origin: ErrorSource.LOGGER, }, } diff --git a/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts b/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts index eb07187a33..30b9264b11 100644 --- a/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts @@ -1,61 +1,95 @@ -import { ErrorSource, resetExperimentalFeatures, updateExperimentalFeatures, display } from '@datadog/browser-core' +import { + ErrorSource, + resetExperimentalFeatures, + updateExperimentalFeatures, + display, + noop, +} from '@datadog/browser-core' import { validateAndBuildLogsConfiguration } from '../../configuration' import { HandlerType, StatusType } from '../../logger' import { createSender } from '../../sender' import { startConsoleCollection } from './consoleCollection' -describe('error collection', () => { +describe('console collection', () => { const initConfiguration = { clientToken: 'xxx', service: 'service' } let sendLogSpy: jasmine.Spy let consoleLogSpy: jasmine.Spy + let stopConsolCollection: () => void beforeEach(() => { + stopConsolCollection = noop sendLogSpy = jasmine.createSpy('sendLogSpy') consoleLogSpy = spyOn(console, 'log').and.callFake(() => true) spyOn(console, 'error').and.callFake(() => true) }) + afterEach(() => { + resetExperimentalFeatures() + stopConsolCollection() + }) + it('should send console logs when ff forward-logs is enabled', () => { updateExperimentalFeatures(['forward-logs']) - const { stop } = startConsoleCollection( + ;({ stop: stopConsolCollection } = startConsoleCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, createSender(sendLogSpy) - ) + )) /* eslint-disable-next-line no-console */ console.log('foo', 'bar') expect(sendLogSpy).toHaveBeenCalledWith({ - message: 'console log: foo bar', + message: 'foo bar', status: StatusType.info, + origin: ErrorSource.CONSOLE, }) expect(consoleLogSpy).toHaveBeenCalled() - - resetExperimentalFeatures() - stop() }) it('should not send console logs when ff forward-logs is disabled', () => { - const { stop } = startConsoleCollection( + ;({ stop: stopConsolCollection } = startConsoleCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, createSender(sendLogSpy) - ) + )) /* eslint-disable-next-line no-console */ console.log('foo', 'bar') expect(sendLogSpy).not.toHaveBeenCalled() expect(consoleLogSpy).toHaveBeenCalled() + }) - stop() + it('should send console errors with "console" origin when ff forward-logs is enabled', () => { + updateExperimentalFeatures(['forward-logs']) + ;({ stop: stopConsolCollection } = startConsoleCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, + createSender(sendLogSpy) + )) + + /* eslint-disable-next-line no-console */ + console.error('foo', 'bar') + + expect(sendLogSpy.calls.mostRecent().args[0].origin).toEqual(ErrorSource.CONSOLE) + }) + + it('should not send console errors with "console" origin when ff forward-logs is disabled', () => { + ;({ stop: stopConsolCollection } = startConsoleCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, + createSender(sendLogSpy) + )) + + /* eslint-disable-next-line no-console */ + console.error('foo', 'bar') + + expect(sendLogSpy.calls.mostRecent().args[0].origin).not.toBeDefined() }) it('console error should have an error object defined', () => { - const { stop } = startConsoleCollection( + ;({ stop: stopConsolCollection } = startConsoleCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, createSender(sendLogSpy) - ) + )) /* eslint-disable-next-line no-console */ console.error('foo', 'bar') @@ -64,8 +98,6 @@ describe('error collection', () => { origin: ErrorSource.CONSOLE, stack: undefined, }) - - stop() }) it('should not print the log twice when console handler is enabled', () => { @@ -73,10 +105,10 @@ describe('error collection', () => { const sender = createSender(sendLogSpy) const displaySpy = spyOn(display, 'log') - const { stop } = startConsoleCollection( + ;({ stop: stopConsolCollection } = startConsoleCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, sender - ) + )) sender.setHandler([HandlerType.console]) /* eslint-disable-next-line no-console */ @@ -84,8 +116,5 @@ describe('error collection', () => { expect(consoleLogSpy).toHaveBeenCalledTimes(1) expect(displaySpy).not.toHaveBeenCalled() - - resetExperimentalFeatures() - stop() }) }) diff --git a/packages/logs/src/domain/logsCollection/console/consoleCollection.ts b/packages/logs/src/domain/logsCollection/console/consoleCollection.ts index f6c8bad399..168b4034c3 100644 --- a/packages/logs/src/domain/logsCollection/console/consoleCollection.ts +++ b/packages/logs/src/domain/logsCollection/console/consoleCollection.ts @@ -1,5 +1,5 @@ import type { Context, ClocksState, ConsoleLog } from '@datadog/browser-core' -import { ConsoleApiName, ErrorSource, initConsoleObservable } from '@datadog/browser-core' +import { ConsoleApiName, ErrorSource, initConsoleObservable, isExperimentalFeatureEnabled } from '@datadog/browser-core' import type { LogsEvent } from '../../../logsEvent.types' import type { LogsConfiguration } from '../../configuration' import { StatusType } from '../../logger' @@ -24,15 +24,18 @@ export function startConsoleCollection(configuration: LogsConfiguration, sender: const consoleSubscription = consoleObservable.subscribe(reportConsoleLog) function reportConsoleLog(log: ConsoleLog) { - let messageContext: Partial | undefined + let messageContext: Partial = {} if (log.api === ConsoleApiName.error) { messageContext = { error: { - origin: ErrorSource.CONSOLE, + origin: ErrorSource.CONSOLE, // Todo: Remove in the next major release stack: log.stack, }, } } + if (isExperimentalFeatureEnabled('forward-logs')) { + messageContext.origin = ErrorSource.CONSOLE + } sender.sendToHttp(log.message, messageContext, LogStatusForApi[log.api]) } diff --git a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts index 00e239d1e1..db979c5004 100644 --- a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts @@ -1,4 +1,4 @@ -import { isIE, ErrorSource } from '@datadog/browser-core' +import { isIE, ErrorSource, updateExperimentalFeatures, resetExperimentalFeatures } from '@datadog/browser-core' import type { FetchStub, FetchStubManager } from '@datadog/browser-core/test/specHelper' import { SPEC_ENDPOINTS, ResponseStub, stubFetch } from '@datadog/browser-core/test/specHelper' import type { LogsConfiguration } from '../../configuration' @@ -46,6 +46,18 @@ describe('network error collection', () => { afterEach(() => { stopNetworkErrorCollection() fetchStubManager.reset() + resetExperimentalFeatures() + }) + + it('should send server errors with "network" origin when ff forward-logs is enabled', (done) => { + updateExperimentalFeatures(['forward-logs']) + + fetchStub(FAKE_URL).resolveWith(DEFAULT_REQUEST) + + fetchStubManager.whenAllComplete(() => { + expect(sendLogSpy.calls.mostRecent().args[0].origin).toEqual(ErrorSource.NETWORK) + done() + }) }) it('should track server error', (done) => { diff --git a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts index 5e728b5b0f..ad2f148708 100644 --- a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts +++ b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts @@ -8,6 +8,7 @@ import { toStackTraceString, monitor, noop, + isExperimentalFeatureEnabled, } from '@datadog/browser-core' import type { LogsEvent } from '../../../logsEvent.types' import type { LogsConfiguration } from '../../configuration' @@ -41,7 +42,7 @@ export function startNetworkErrorCollection(configuration: LogsConfiguration, se const messageContext: Partial = { date: request.startClocks.timeStamp, error: { - origin: ErrorSource.NETWORK, + origin: ErrorSource.NETWORK, // Todo: Remove in the next major release stack: (responseData as string) || 'Failed to load', }, http: { @@ -50,6 +51,9 @@ export function startNetworkErrorCollection(configuration: LogsConfiguration, se url: request.url, }, } + if (isExperimentalFeatureEnabled('forward-logs')) { + messageContext.origin = ErrorSource.NETWORK + } sender.sendToHttp(`${format(type)} error ${request.method} ${request.url}`, messageContext, StatusType.error) } diff --git a/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts b/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts index a2cd23f728..387132522c 100644 --- a/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts @@ -1,4 +1,4 @@ -import { ErrorSource, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' +import { ErrorSource, noop, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' import { stubReportingObserver } from '@datadog/browser-core/test/stubReportApis' import { validateAndBuildLogsConfiguration } from '../../configuration' import { StatusType } from '../../logger' @@ -9,22 +9,26 @@ describe('reports', () => { const initConfiguration = { clientToken: 'xxx', service: 'service' } let sendLogSpy: jasmine.Spy let reportingObserverStub: ReturnType + let stopReportCollection: () => void beforeEach(() => { + stopReportCollection = noop sendLogSpy = jasmine.createSpy('sendLogSpy') reportingObserverStub = stubReportingObserver() }) afterEach(() => { reportingObserverStub.reset() + resetExperimentalFeatures() + stopReportCollection() }) it('should send reports when ff forward-reports is enabled', () => { updateExperimentalFeatures(['forward-reports']) - const { stop } = startReportCollection( + ;({ stop: stopReportCollection } = startReportCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, createSender(sendLogSpy) - ) + )) reportingObserverStub.raiseReport('intervention') expect(sendLogSpy).toHaveBeenCalledOnceWith({ @@ -35,49 +39,43 @@ describe('reports', () => { }, message: 'intervention: foo bar', status: StatusType.error, + origin: ErrorSource.REPORT, }) - - resetExperimentalFeatures() - stop() }) it('should not send reports when ff forward-reports is disabled', () => { - const { stop } = startReportCollection( + ;({ stop: stopReportCollection } = startReportCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, createSender(sendLogSpy) - ) + )) reportingObserverStub.raiseReport('intervention') expect(sendLogSpy).not.toHaveBeenCalled() - stop() }) it('should not send reports when forwardReports init option not specified', () => { - const { stop } = startReportCollection( + ;({ stop: stopReportCollection } = startReportCollection( validateAndBuildLogsConfiguration({ ...initConfiguration })!, createSender(sendLogSpy) - ) + )) reportingObserverStub.raiseReport('intervention') expect(sendLogSpy).not.toHaveBeenCalled() - stop() }) it('should add the source file information to the message for non error reports', () => { updateExperimentalFeatures(['forward-reports']) - const { stop } = startReportCollection( + ;({ stop: stopReportCollection } = startReportCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['deprecation'] })!, createSender(sendLogSpy) - ) + )) reportingObserverStub.raiseReport('deprecation') expect(sendLogSpy).toHaveBeenCalledOnceWith({ message: 'deprecation: foo bar Found in http://foo.bar/index.js:20:10', status: StatusType.warn, + origin: ErrorSource.REPORT, }) - - resetExperimentalFeatures() - stop() }) }) diff --git a/packages/logs/src/domain/logsCollection/report/reportCollection.ts b/packages/logs/src/domain/logsCollection/report/reportCollection.ts index 62caa24582..c257f46a13 100644 --- a/packages/logs/src/domain/logsCollection/report/reportCollection.ts +++ b/packages/logs/src/domain/logsCollection/report/reportCollection.ts @@ -24,15 +24,15 @@ export function startReportCollection(configuration: LogsConfiguration, sender: function logReport(report: RawReport) { let message = report.message - let messageContext: Partial | undefined + const messageContext: Partial = { + origin: ErrorSource.REPORT, + } const logStatus = LogStatusForReport[report.type] if (logStatus === StatusType.error) { - messageContext = { - error: { - kind: report.subtype, - origin: ErrorSource.REPORT, - stack: report.stack, - }, + messageContext.error = { + kind: report.subtype, + origin: ErrorSource.REPORT, // Todo: Remove in the next major release + stack: report.stack, } } else if (report.stack) { message += ` Found in ${getFileFromStackTraceString(report.stack)!}` diff --git a/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts index 6b9a66f5a7..3c7cb597ca 100644 --- a/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts @@ -1,4 +1,4 @@ -import { ErrorSource, Observable } from '@datadog/browser-core' +import { ErrorSource, Observable, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' import type { RawError, RelativeTime, TimeStamp } from '@datadog/browser-core' import type { LogsConfiguration } from '../../configuration' import { createSender } from '../../sender' @@ -21,6 +21,7 @@ describe('runtime error collection', () => { afterEach(() => { stopRuntimeErrorCollection() + resetExperimentalFeatures() }) it('should send runtime errors', () => { @@ -41,4 +42,18 @@ describe('runtime error collection', () => { }, ]) }) + + it('should send runtime errors with "source" origin when ff forward-logs is enabled', (done) => { + updateExperimentalFeatures(['forward-logs']) + + rawErrorObservable.notify({ + message: 'error!', + source: ErrorSource.SOURCE, + startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp }, + type: 'Error', + }) + + expect(sendLogSpy.calls.mostRecent().args[0].origin).toEqual(ErrorSource.SOURCE) + done() + }) }) diff --git a/packages/logs/src/domain/reportRawError.ts b/packages/logs/src/domain/reportRawError.ts index fcb4c71a6d..f844c46da0 100644 --- a/packages/logs/src/domain/reportRawError.ts +++ b/packages/logs/src/domain/reportRawError.ts @@ -1,4 +1,5 @@ import type { RawError } from '@datadog/browser-core' +import { isExperimentalFeatureEnabled } from '@datadog/browser-core' import type { LogsEvent } from '../logsEvent.types' import { StatusType } from './logger' import type { Sender } from './sender' @@ -8,9 +9,12 @@ export function reportRawError(error: RawError, sender: Sender) { date: error.startClocks.timeStamp, error: { kind: error.type, - origin: error.source, + origin: error.source, // Todo: Remove in the next major release stack: error.stack, }, } + if (isExperimentalFeatureEnabled('forward-logs')) { + messageContext.origin = error.source + } sender.sendToHttp(error.message, messageContext, StatusType.error) } diff --git a/packages/logs/src/logsEvent.types.ts b/packages/logs/src/logsEvent.types.ts index 8adb2ff1d4..31ff7ec289 100644 --- a/packages/logs/src/logsEvent.types.ts +++ b/packages/logs/src/logsEvent.types.ts @@ -11,6 +11,10 @@ export interface LogsEvent { * The log status */ status: 'debug' | 'info' | 'warn' | 'error' + /** + * Origin of the log + */ + origin?: 'network' | 'source' | 'console' | 'logger' | 'agent' | 'report' | 'custom' /** * UUID of the application */ From 85e732de18bef9a37af5a9405135e35811ccd79e Mon Sep 17 00:00:00 2001 From: Aymeric Date: Thu, 7 Apr 2022 11:27:57 +0200 Subject: [PATCH 03/23] =?UTF-8?q?=F0=9F=90=9B=20=20=20Adjust=20records=20g?= =?UTF-8?q?enerated=20during=20view=20change=20so=20their=20date=20matches?= =?UTF-8?q?=20the=20view=20date=20(#1486)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adjust records generated during view change so their date matches the view date --- packages/rum-core/src/index.ts | 1 + packages/rum/src/boot/startRecording.spec.ts | 40 +++++++++++++++++-- packages/rum/src/boot/startRecording.ts | 27 ++++++++----- packages/rum/src/domain/record/record.spec.ts | 5 ++- packages/rum/src/domain/record/record.ts | 13 ++++-- packages/rum/src/domain/record/types.ts | 8 ++-- .../domain/segmentCollection/segment.spec.ts | 11 ++--- .../segmentCollection.spec.ts | 5 ++- packages/rum/src/types.ts | 14 ++++--- 9 files changed, 88 insertions(+), 36 deletions(-) diff --git a/packages/rum-core/src/index.ts b/packages/rum-core/src/index.ts index b97b4aed59..64eed8067d 100644 --- a/packages/rum-core/src/index.ts +++ b/packages/rum-core/src/index.ts @@ -21,6 +21,7 @@ export { export { ViewContext, CommonContext, ReplayStats } from './rawRumEvent.types' export { startRum } from './boot/startRum' export { LifeCycle, LifeCycleEventType } from './domain/lifeCycle' +export { ViewCreatedEvent } from './domain/rumEventsCollection/view/trackViews' export { ViewContexts } from './domain/viewContexts' export { RumSessionManager, RumSessionPlan } from './domain/rumSessionManager' export { getMutationObserverConstructor } from './browser/domMutationObservable' diff --git a/packages/rum/src/boot/startRecording.spec.ts b/packages/rum/src/boot/startRecording.spec.ts index d23f4f4e35..22f4c90b58 100644 --- a/packages/rum/src/boot/startRecording.spec.ts +++ b/packages/rum/src/boot/startRecording.spec.ts @@ -1,10 +1,11 @@ -import { HttpRequest, DefaultPrivacyLevel, noop, isIE } from '@datadog/browser-core' -import type { LifeCycle } from '@datadog/browser-rum-core' +import type { TimeStamp } from '@datadog/browser-core' +import { HttpRequest, DefaultPrivacyLevel, noop, isIE, timeStampNow } from '@datadog/browser-core' +import type { LifeCycle, ViewCreatedEvent } from '@datadog/browser-rum-core' import { LifeCycleEventType } from '@datadog/browser-rum-core' import { inflate } from 'pako' import type { RumSessionManagerMock } from '../../../rum-core/test/mockRumSessionManager' import { createRumSessionManagerMock } from '../../../rum-core/test/mockRumSessionManager' -import { createNewEvent } from '../../../core/test/specHelper' +import { createNewEvent, mockClock } from '../../../core/test/specHelper' import type { TestSetupBuilder } from '../../../rum-core/test/specHelper' import { setup } from '../../../rum-core/test/specHelper' @@ -16,6 +17,8 @@ import { RecordType } from '../types' import { resetReplayStats } from '../domain/replayStats' import { startRecording } from './startRecording' +const VIEW_TIMESTAMP = 1 as TimeStamp + describe('startRecording', () => { let setupBuilder: TestSetupBuilder let sessionManager: RumSessionManagerMock @@ -165,6 +168,33 @@ describe('startRecording', () => { }) }) + it('full snapshot related records should have the view change date', (done) => { + const clock = mockClock() + const { lifeCycle } = setupBuilder.build() + + changeView(lifeCycle) + flushSegment(lifeCycle) + + waitRequestSendCalls(2, (calls) => { + readRequestSegment(calls.first(), (segment) => { + expect(segment.records[0].timestamp).toEqual(timeStampNow()) + expect(segment.records[1].timestamp).toEqual(timeStampNow()) + expect(segment.records[2].timestamp).toEqual(timeStampNow()) + expect(segment.records[3].timestamp).toEqual(timeStampNow()) + + clock.cleanup() + + readRequestSegment(calls.mostRecent(), (segment) => { + expect(segment.records[0].timestamp).toEqual(VIEW_TIMESTAMP) + expect(segment.records[1].timestamp).toEqual(VIEW_TIMESTAMP) + expect(segment.records[2].timestamp).toEqual(VIEW_TIMESTAMP) + + expectNoExtraRequestSendCalls(done) + }) + }) + }) + }) + it('adds a ViewEnd record when the view ends', (done) => { const { lifeCycle } = setupBuilder.build() @@ -247,7 +277,9 @@ describe('startRecording', () => { function changeView(lifeCycle: LifeCycle) { lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, {} as any) viewId = 'view-id-2' - lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, {} as any) + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { + startClocks: { relative: 1, timeStamp: VIEW_TIMESTAMP }, + } as Partial as any) } }) diff --git a/packages/rum/src/boot/startRecording.ts b/packages/rum/src/boot/startRecording.ts index 1620270f87..158a983b9d 100644 --- a/packages/rum/src/boot/startRecording.ts +++ b/packages/rum/src/boot/startRecording.ts @@ -1,12 +1,17 @@ -import { assign } from '@datadog/browser-core' -import type { LifeCycle, ViewContexts, RumConfiguration, RumSessionManager } from '@datadog/browser-rum-core' +import { timeStampNow } from '@datadog/browser-core' +import type { + LifeCycle, + ViewContexts, + RumConfiguration, + RumSessionManager, + ViewCreatedEvent, +} from '@datadog/browser-rum-core' import { LifeCycleEventType } from '@datadog/browser-rum-core' import { record } from '../domain/record' import type { DeflateWorker } from '../domain/segmentCollection' import { startSegmentCollection } from '../domain/segmentCollection' import { send } from '../transport/send' -import type { RawRecord } from '../types' import { RecordType } from '../types' export function startRecording( @@ -26,26 +31,28 @@ export function startRecording( worker ) - function addRawRecord(rawRecord: RawRecord) { - addRecord(assign({ timestamp: Date.now() }, rawRecord)) - } - const { stop: stopRecording, takeFullSnapshot, flushMutations, } = record({ - emit: addRawRecord, + emit: addRecord, defaultPrivacyLevel: configuration.defaultPrivacyLevel, }) const { unsubscribe: unsubscribeViewEnded } = lifeCycle.subscribe(LifeCycleEventType.VIEW_ENDED, () => { flushMutations() - addRawRecord({ + addRecord({ + timestamp: timeStampNow(), type: RecordType.ViewEnd, }) }) - const { unsubscribe: unsubscribeViewCreated } = lifeCycle.subscribe(LifeCycleEventType.VIEW_CREATED, takeFullSnapshot) + const { unsubscribe: unsubscribeViewCreated } = lifeCycle.subscribe( + LifeCycleEventType.VIEW_CREATED, + (view: ViewCreatedEvent) => { + takeFullSnapshot(view.startClocks.timeStamp) + } + ) return { stop: () => { diff --git a/packages/rum/src/domain/record/record.spec.ts b/packages/rum/src/domain/record/record.spec.ts index 557505abf1..ec713080a7 100644 --- a/packages/rum/src/domain/record/record.spec.ts +++ b/packages/rum/src/domain/record/record.spec.ts @@ -2,7 +2,7 @@ import { DefaultPrivacyLevel, isIE } from '@datadog/browser-core' import type { Clock } from '../../../../core/test/specHelper' import { createNewEvent } from '../../../../core/test/specHelper' import { collectAsyncCalls, recordsPerFullSnapshot } from '../../../test/utils' -import type { RawRecord, IncrementalSnapshotRecord, FocusRecord } from '../../types' +import type { IncrementalSnapshotRecord, FocusRecord, Record } from '../../types' import { RecordType, IncrementalSource } from '../../types' import { record } from './record' import type { RecordAPI } from './types' @@ -10,7 +10,7 @@ import type { RecordAPI } from './types' describe('record', () => { let sandbox: HTMLElement let recordApi: RecordAPI - let emitSpy: jasmine.Spy<(record: RawRecord) => void> + let emitSpy: jasmine.Spy<(record: Record) => void> let waitEmitCalls: (expectedCallsCount: number, callback: () => void) => void let expectNoExtraEmitCalls: (done: () => void) => void let clock: Clock | undefined @@ -150,6 +150,7 @@ describe('record', () => { startRecording() expect(getEmittedRecords()[1]).toEqual({ type: RecordType.Focus, + timestamp: jasmine.any(Number), data: { has_focus: true, }, diff --git a/packages/rum/src/domain/record/record.ts b/packages/rum/src/domain/record/record.ts index 8459e6a38b..b35b37e5ad 100644 --- a/packages/rum/src/domain/record/record.ts +++ b/packages/rum/src/domain/record/record.ts @@ -1,4 +1,4 @@ -import { assign } from '@datadog/browser-core' +import { assign, timeStampNow } from '@datadog/browser-core' import type { IncrementalSnapshotRecord } from '../../types' import { RecordType } from '../../types' import { serializeDocument } from './serialize' @@ -30,7 +30,7 @@ export function record(options: RecordOptions): RecordAPI { const mutationController = new MutationController() - const takeFullSnapshot = () => { + const takeFullSnapshot = (timestamp = timeStampNow()) => { mutationController.flush() // process any pending mutation before taking a full snapshot emit({ @@ -40,6 +40,7 @@ export function record(options: RecordOptions): RecordAPI { width: getWindowWidth(), }, type: RecordType.Meta, + timestamp, }) emit({ @@ -47,6 +48,7 @@ export function record(options: RecordOptions): RecordAPI { has_focus: document.hasFocus(), }, type: RecordType.Focus, + timestamp, }) emit({ @@ -58,12 +60,14 @@ export function record(options: RecordOptions): RecordAPI { }, }, type: RecordType.FullSnapshot, + timestamp, }) if (window.visualViewport) { emit({ data: getVisualViewport(), type: RecordType.VisualViewport, + timestamp, }) } } @@ -86,13 +90,15 @@ export function record(options: RecordOptions): RecordAPI { focusCb: (data) => emit({ - type: RecordType.Focus, data, + type: RecordType.Focus, + timestamp: timeStampNow(), }), visualViewportResizeCb: (data) => { emit({ data, type: RecordType.VisualViewport, + timestamp: timeStampNow(), }) }, }) @@ -116,5 +122,6 @@ function assembleIncrementalSnapshot( data ) as Data, type: RecordType.IncrementalSnapshot, + timestamp: timeStampNow(), } } diff --git a/packages/rum/src/domain/record/types.ts b/packages/rum/src/domain/record/types.ts index 0fa7a0589e..57cf215da6 100644 --- a/packages/rum/src/domain/record/types.ts +++ b/packages/rum/src/domain/record/types.ts @@ -1,5 +1,5 @@ -import type { DefaultPrivacyLevel } from '@datadog/browser-core' -import type { FocusRecord, RawRecord, VisualViewportRecord } from '../../types' +import type { DefaultPrivacyLevel, TimeStamp } from '@datadog/browser-core' +import type { FocusRecord, VisualViewportRecord, Record } from '../../types' import type { MutationController } from './mutationObserver' export const IncrementalSource = { @@ -63,13 +63,13 @@ export type IncrementalData = | StyleSheetRuleData export interface RecordOptions { - emit?: (record: RawRecord) => void + emit?: (record: Record) => void defaultPrivacyLevel: DefaultPrivacyLevel } export interface RecordAPI { stop: ListenerHandler - takeFullSnapshot: () => void + takeFullSnapshot: (timestamp?: TimeStamp) => void flushMutations: () => void } diff --git a/packages/rum/src/domain/segmentCollection/segment.spec.ts b/packages/rum/src/domain/segmentCollection/segment.spec.ts index e93e698fc8..a30cebdecf 100644 --- a/packages/rum/src/domain/segmentCollection/segment.spec.ts +++ b/packages/rum/src/domain/segmentCollection/segment.spec.ts @@ -1,3 +1,4 @@ +import type { TimeStamp } from '@datadog/browser-core' import { noop, setDebugMode, display, isIE } from '@datadog/browser-core' import { MockWorker, parseSegment } from '../../../test/utils' import type { CreationReason, Record, SegmentContext } from '../../types' @@ -6,9 +7,9 @@ import { getReplayStats, resetReplayStats } from '../replayStats' import { Segment } from './segment' const CONTEXT: SegmentContext = { application: { id: 'a' }, view: { id: 'b' }, session: { id: 'c' } } - -const RECORD: Record = { type: RecordType.ViewEnd, timestamp: 10 } -const FULL_SNAPSHOT_RECORD: Record = { type: RecordType.FullSnapshot, timestamp: 10, data: {} as any } +const RECORD_TIMESTAMP = 10 as TimeStamp +const RECORD: Record = { type: RecordType.ViewEnd, timestamp: RECORD_TIMESTAMP } +const FULL_SNAPSHOT_RECORD: Record = { type: RecordType.FullSnapshot, timestamp: RECORD_TIMESTAMP, data: {} as any } const ENCODED_SEGMENT_HEADER_SIZE = 12 // {"records":[ const ENCODED_RECORD_SIZE = 25 const ENCODED_FULL_SNAPSHOT_RECORD_SIZE = 35 @@ -53,7 +54,7 @@ describe('Segment', () => { has_full_snapshot: false, records: [ { - timestamp: 10, + timestamp: RECORD_TIMESTAMP, type: RecordType.ViewEnd, }, ], @@ -123,7 +124,7 @@ describe('Segment', () => { let segment: Segment beforeEach(() => { segment = createSegment() - segment.addRecord({ type: RecordType.ViewEnd, timestamp: 15 }) + segment.addRecord({ type: RecordType.ViewEnd, timestamp: 15 as TimeStamp }) }) it('increments records_count', () => { expect(segment.metadata.records_count).toBe(2) diff --git a/packages/rum/src/domain/segmentCollection/segmentCollection.spec.ts b/packages/rum/src/domain/segmentCollection/segmentCollection.spec.ts index 6f3171bcf5..ce31f067d1 100644 --- a/packages/rum/src/domain/segmentCollection/segmentCollection.spec.ts +++ b/packages/rum/src/domain/segmentCollection/segmentCollection.spec.ts @@ -1,3 +1,4 @@ +import type { TimeStamp } from '@datadog/browser-core' import { DOM_EVENT, isIE } from '@datadog/browser-core' import type { ViewContexts, ViewContext } from '@datadog/browser-rum-core' import { LifeCycle, LifeCycleEventType } from '@datadog/browser-rum-core' @@ -16,12 +17,12 @@ import { SEND_BEACON_BYTE_LENGTH_LIMIT } from '../../transport/send' import { computeSegmentContext, doStartSegmentCollection, MAX_SEGMENT_DURATION } from './segmentCollection' const CONTEXT: SegmentContext = { application: { id: 'a' }, view: { id: 'b' }, session: { id: 'c' } } -const RECORD: Record = { type: RecordType.ViewEnd, timestamp: 10 } +const RECORD: Record = { type: RecordType.ViewEnd, timestamp: 10 as TimeStamp } // A record that will make the segment size reach the SEND_BEACON_BYTE_LENGTH_LIMIT limit const VERY_BIG_RECORD: Record = { type: RecordType.FullSnapshot, - timestamp: 10, + timestamp: 10 as TimeStamp, data: Array(SEND_BEACON_BYTE_LENGTH_LIMIT).join('a') as any, } diff --git a/packages/rum/src/types.ts b/packages/rum/src/types.ts index 008015b41d..2a4f0a35da 100644 --- a/packages/rum/src/types.ts +++ b/packages/rum/src/types.ts @@ -1,3 +1,4 @@ +import type { TimeStamp } from '@datadog/browser-core' import type { IncrementalData, SerializedNodeWithId } from './domain/record' export { IncrementalSource, MutationData, ViewportResizeData, ScrollData } from './domain/record' @@ -29,7 +30,7 @@ export type CreationReason = | 'before_unload' | 'visibility_hidden' -export type RawRecord = +export type Record = | FullSnapshotRecord | IncrementalSnapshotRecord | MetaRecord @@ -37,11 +38,6 @@ export type RawRecord = | ViewEndRecord | VisualViewportRecord -export type Record = RawRecord & { - timestamp: number - delay?: number -} - export const RecordType = { FullSnapshot: 2, IncrementalSnapshot: 3, @@ -55,6 +51,7 @@ export type RecordType = typeof RecordType[keyof typeof RecordType] export interface FullSnapshotRecord { type: typeof RecordType.FullSnapshot + timestamp: TimeStamp data: { node: SerializedNodeWithId initialOffset: { @@ -66,11 +63,13 @@ export interface FullSnapshotRecord { export interface IncrementalSnapshotRecord { type: typeof RecordType.IncrementalSnapshot + timestamp: TimeStamp data: IncrementalData } export interface MetaRecord { type: typeof RecordType.Meta + timestamp: TimeStamp data: { href: string width: number @@ -80,6 +79,7 @@ export interface MetaRecord { export interface FocusRecord { type: typeof RecordType.Focus + timestamp: TimeStamp data: { has_focus: boolean } @@ -87,10 +87,12 @@ export interface FocusRecord { export interface ViewEndRecord { type: typeof RecordType.ViewEnd + timestamp: TimeStamp } export interface VisualViewportRecord { type: typeof RecordType.VisualViewport + timestamp: TimeStamp data: { scale: number offsetLeft: number From 12096dbb60377a0bbcc4573de705d21a029f7e2b Mon Sep 17 00:00:00 2001 From: Aymeric Date: Thu, 7 Apr 2022 15:37:12 +0200 Subject: [PATCH 04/23] v4.7.1 (#1490) --- CHANGELOG.md | 10 +++++ developer-extension/package.json | 2 +- lerna.json | 2 +- packages/core/package.json | 2 +- packages/logs/package.json | 4 +- packages/rum-core/package.json | 4 +- packages/rum-slim/package.json | 6 +-- packages/rum/package.json | 6 +-- performances/package.json | 2 +- test/app/yarn.lock | 64 ++++++++++++++++---------------- 10 files changed, 56 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbab38eb77..a1a6b7b914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,16 @@ --- +## v4.7.1 + +- 🐛 Adjust records generated during view change so their date matches the view date ([#1486](https://github.com/DataDog/browser-sdk/pull/1486)) +- ⚗✨ [RUMF-1224] remove console APIs prefix ([#1479](https://github.com/DataDog/browser-sdk/pull/1479)) +- ♻️ [RUMF-1178] improve logs assembly part 2 ([#1463](https://github.com/DataDog/browser-sdk/pull/1463)) +- ⚗✨ Allow update service version with start view ([#1448](https://github.com/DataDog/browser-sdk/pull/1448)) +- ⚗✨ [RUMF-1208] don't discard automatic action on view creation ([#1451](https://github.com/DataDog/browser-sdk/pull/1451)) +- ⚗✨ [RUMF-1207] collect concurrent actions ([#1434](https://github.com/DataDog/browser-sdk/pull/1434)) +- ♻️ [RUMF-1207] collect concurrent actions groundwork - move action history closer to action collection ([#1432](https://github.com/DataDog/browser-sdk/pull/1432)) + ## v4.7.0 Note: The Logs Browser SDK 3.10.1 (released on December 21th, 2021) unexpectedly changed the initialization parameter `forwardErrorsToLogs` default value from `true` to `false`. This release restores the default value to `true`, so Logs Browser SDK users who don't specify this parameter will have errors forwarded as logs. diff --git a/developer-extension/package.json b/developer-extension/package.json index 0f07c643c7..978b8c5db3 100644 --- a/developer-extension/package.json +++ b/developer-extension/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-sdk-developer-extension", - "version": "4.7.0", + "version": "4.7.1", "private": true, "scripts": { "build": "rm -rf dist && webpack --mode production", diff --git a/lerna.json b/lerna.json index 537848a388..b957e5daac 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "npmClient": "yarn", "useWorkspaces": true, - "version": "4.7.0", + "version": "4.7.1", "publishConfig": { "access": "public" } diff --git a/packages/core/package.json b/packages/core/package.json index ef7662d084..3b54db92e2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-core", - "version": "4.7.0", + "version": "4.7.1", "license": "Apache-2.0", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/packages/logs/package.json b/packages/logs/package.json index e8bdf5400a..2ccccfda7a 100644 --- a/packages/logs/package.json +++ b/packages/logs/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-logs", - "version": "4.7.0", + "version": "4.7.1", "license": "Apache-2.0", "main": "cjs/entries/main.js", "module": "esm/entries/main.js", @@ -13,7 +13,7 @@ "replace-build-env": "node ../../scripts/replace-build-env.js" }, "dependencies": { - "@datadog/browser-core": "4.7.0" + "@datadog/browser-core": "4.7.1" }, "devDependencies": { "@types/sinon": "9.0.10", diff --git a/packages/rum-core/package.json b/packages/rum-core/package.json index 5ba2b54418..026a7e0059 100644 --- a/packages/rum-core/package.json +++ b/packages/rum-core/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-rum-core", - "version": "4.7.0", + "version": "4.7.1", "license": "Apache-2.0", "main": "cjs/index.js", "module": "esm/index.js", @@ -12,7 +12,7 @@ "replace-build-env": "node ../../scripts/replace-build-env.js" }, "dependencies": { - "@datadog/browser-core": "4.7.0" + "@datadog/browser-core": "4.7.1" }, "devDependencies": { "ajv": "6.12.6" diff --git a/packages/rum-slim/package.json b/packages/rum-slim/package.json index ebeb346039..aba885089f 100644 --- a/packages/rum-slim/package.json +++ b/packages/rum-slim/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-rum-slim", - "version": "4.7.0", + "version": "4.7.1", "license": "Apache-2.0", "main": "cjs/entries/main.js", "module": "esm/entries/main.js", @@ -12,8 +12,8 @@ "build:esm": "rm -rf esm && tsc -p tsconfig.esm.json" }, "dependencies": { - "@datadog/browser-core": "4.7.0", - "@datadog/browser-rum-core": "4.7.0" + "@datadog/browser-core": "4.7.1", + "@datadog/browser-rum-core": "4.7.1" }, "repository": { "type": "git", diff --git a/packages/rum/package.json b/packages/rum/package.json index 2f5270c808..b3c93b3fdc 100644 --- a/packages/rum/package.json +++ b/packages/rum/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-rum", - "version": "4.7.0", + "version": "4.7.1", "license": "Apache-2.0", "main": "cjs/entries/main.js", "module": "esm/entries/main.js", @@ -12,8 +12,8 @@ "build:esm": "rm -rf esm && tsc -p tsconfig.esm.json" }, "dependencies": { - "@datadog/browser-core": "4.7.0", - "@datadog/browser-rum-core": "4.7.0" + "@datadog/browser-core": "4.7.1", + "@datadog/browser-rum-core": "4.7.1" }, "repository": { "type": "git", diff --git a/performances/package.json b/performances/package.json index 22d7951db8..3bb52bfc8a 100644 --- a/performances/package.json +++ b/performances/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "performances", - "version": "4.7.0", + "version": "4.7.1", "scripts": { "start": "ts-node ./src/main.ts" }, diff --git a/test/app/yarn.lock b/test/app/yarn.lock index f2ea58fc02..683c98647f 100644 --- a/test/app/yarn.lock +++ b/test/app/yarn.lock @@ -2,24 +2,24 @@ # yarn lockfile v1 -"@datadog/browser-core@4.7.0", "@datadog/browser-core@file:../../packages/core": - version "4.7.0" +"@datadog/browser-core@4.7.1", "@datadog/browser-core@file:../../packages/core": + version "4.7.1" "@datadog/browser-logs@file:../../packages/logs": - version "4.7.0" + version "4.7.1" dependencies: - "@datadog/browser-core" "4.7.0" + "@datadog/browser-core" "4.7.1" -"@datadog/browser-rum-core@4.7.0", "@datadog/browser-rum-core@file:../../packages/rum-core": - version "4.7.0" +"@datadog/browser-rum-core@4.7.1", "@datadog/browser-rum-core@file:../../packages/rum-core": + version "4.7.1" dependencies: - "@datadog/browser-core" "4.7.0" + "@datadog/browser-core" "4.7.1" "@datadog/browser-rum@file:../../packages/rum": - version "4.7.0" + version "4.7.1" dependencies: - "@datadog/browser-core" "4.7.0" - "@datadog/browser-rum-core" "4.7.0" + "@datadog/browser-core" "4.7.1" + "@datadog/browser-rum-core" "4.7.1" "@types/eslint-scope@^3.7.0": version "3.7.3" @@ -48,14 +48,14 @@ integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== "@types/json-schema@*", "@types/json-schema@^7.0.8": - version "7.0.10" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" - integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/node@*": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.22.tgz#38b6c4b9b2f3ed9f2e376cce42a298fb2375251e" - integrity sha512-8FwbVoG4fy+ykY86XCAclKZDORttqE5/s7dyWZKLXTdv3vRy5HozBEinG5IqhvPXXzIZEcTVbuHlQEI6iuwcmw== + version "17.0.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" + integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== "@webassemblyjs/ast@1.11.0": version "1.11.0" @@ -220,7 +220,7 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -braces@^3.0.1: +braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -244,9 +244,9 @@ buffer-from@^1.0.0: integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== caniuse-lite@^1.0.30001317: - version "1.0.30001319" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz#eb4da4eb3ecdd409f7ba1907820061d56096e88f" - integrity sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw== + version "1.0.30001325" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz#2b4ad19b77aa36f61f2eaf72e636d7481d55e606" + integrity sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ== chalk@^2.3.0: version "2.4.2" @@ -285,9 +285,9 @@ core-util-is@~1.0.0: integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== electron-to-chromium@^1.4.84: - version "1.4.90" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.90.tgz#4a518590f118828d54fff045728f547fef08143f" - integrity sha512-ZwKgSA0mQMyEhz+NR0F8dRzkrCLeHLzLkjx/CWf16+zV85hQ6meXPQbKanvhnpkYb7b2uJNj+enQJ/N877ND4Q== + version "1.4.106" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.106.tgz#e7a3bfa9d745dd9b9e597616cb17283cc349781a" + integrity sha512-ZYfpVLULm67K7CaaGP7DmjyeMY4naxsbTy+syVVxT6QHI1Ww8XbJjmr9fDckrhq44WzCrcC5kH3zGpdusxwwqg== emojis-list@^3.0.0: version "3.0.0" @@ -386,9 +386,9 @@ glob-to-regexp@^0.4.1: integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== graceful-fs@^4.1.2, graceful-fs@^4.2.4: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== has-flag@^3.0.0: version "3.0.0" @@ -469,12 +469,12 @@ merge-stream@^2.0.0: integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== micromatch@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.2" + picomatch "^2.3.1" mime-db@1.52.0: version "1.52.0" @@ -508,7 +508,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.2.3: +picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== From af5ca494852bebf4896fd8138c491c3f8f078fec Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Thu, 7 Apr 2022 18:32:39 +0200 Subject: [PATCH 05/23] =?UTF-8?q?=F0=9F=91=B7=20developer=20extension=20mi?= =?UTF-8?q?nor=20improvements=20(#1491)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * clarify search syntax * add dedicated telemetry color * swith event theme between ligh/dark mode * 👌 use mantine hooks for dark mode detection --- developer-extension/src/panel/components/eventsTab.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/developer-extension/src/panel/components/eventsTab.tsx b/developer-extension/src/panel/components/eventsTab.tsx index e963d7d117..053b4f81a6 100644 --- a/developer-extension/src/panel/components/eventsTab.tsx +++ b/developer-extension/src/panel/components/eventsTab.tsx @@ -1,4 +1,5 @@ import { Badge, Group, SegmentedControl, Space, Table, TextInput } from '@mantine/core' +import { useColorScheme } from '@mantine/hooks' import React from 'react' import ReactJson from 'react-json-view' import type { RumEvent } from '../../../../packages/rum-core/src/rumEvent.types' @@ -11,6 +12,7 @@ const RUM_EVENT_TYPE_COLOR = { long_task: 'yellow', view: 'blue', resource: 'cyan', + telemetry: 'teal', } const LOG_STATUS_COLOR = { @@ -27,6 +29,7 @@ interface EventTabProps { } export function EventTab({ events, filters, onFiltered }: EventTabProps) { + const colorScheme = useColorScheme() return ( events && ( <> @@ -40,7 +43,7 @@ export function EventTab({ events, filters, onFiltered }: EventTabProps) { ]} /> onFiltered({ ...filters, query: event.currentTarget.value })} @@ -67,7 +70,7 @@ export function EventTab({ events, filters, onFiltered }: EventTabProps) { From 8590cfe1ba01d1548e8d4709294016ec5401d966 Mon Sep 17 00:00:00 2001 From: "ci.browser-sdk" Date: Mon, 11 Apr 2022 02:04:29 +0000 Subject: [PATCH 06/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20staging=20to=20stag?= =?UTF-8?q?ing-16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b0d7a874be..577ca71512 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - CURRENT_STAGING: staging-15 + CURRENT_STAGING: staging-16 APP: 'browser-sdk' CURRENT_CI_IMAGE: 31 BUILD_STABLE_REGISTRY: '486234852809.dkr.ecr.us-east-1.amazonaws.com' From d5bea0b9574b3859313fc121d4c63b77914d12f3 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Mon, 11 Apr 2022 17:06:22 +0200 Subject: [PATCH 07/23] =?UTF-8?q?=F0=9F=91=B7=20[deps]=20only=20notify=20o?= =?UTF-8?q?n=20major=20version=20update=20(#1492)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit too much generated PRs --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f4a6ad0c57..f501bf2b44 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -32,3 +32,5 @@ updates: # update node-fetch: RUMF-1167 - dependency-name: 'node-fetch' update-types: ['version-update:semver-major'] + - dependency-name: '*' + update-types: ['version-update:semver-minor', 'version-update:semver-patch'] From 19ab83b25e7bda0e42d8573dc0716fb4de4c5646 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 17:59:13 +0200 Subject: [PATCH 08/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20@types/react-dom=20?= =?UTF-8?q?from=2017.0.14=20to=2018.0.0=20(#1493)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 17.0.14 to 18.0.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- developer-extension/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/developer-extension/package.json b/developer-extension/package.json index 978b8c5db3..a7acc815c8 100644 --- a/developer-extension/package.json +++ b/developer-extension/package.json @@ -9,7 +9,7 @@ "devDependencies": { "@types/chrome": "0.0.180", "@types/react": "17.0.43", - "@types/react-dom": "17.0.14", + "@types/react-dom": "18.0.0", "copy-webpack-plugin": "8.1.0", "html-webpack-plugin": "5.3.1", "webpack": "5.28.0", diff --git a/yarn.lock b/yarn.lock index 399b821451..26218add95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1881,10 +1881,10 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/react-dom@17.0.14": - version "17.0.14" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f" - integrity sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ== +"@types/react-dom@18.0.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.0.tgz#b13f8d098e4b0c45df4f1ed123833143b0c71141" + integrity sha512-49897Y0UiCGmxZqpC8Blrf6meL8QUla6eb+BBhn69dTXlmuOlzkfr7HHY/O8J25e1lTUMs+YYxSlVDAaGHCOLg== dependencies: "@types/react" "*" From 2ee2635773b518ba1804761abefcb40b82dce92f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Apr 2022 09:28:34 +0200 Subject: [PATCH 09/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20@types/react=20from?= =?UTF-8?q?=2017.0.43=20to=2018.0.1=20(#1494)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.43 to 18.0.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- developer-extension/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/developer-extension/package.json b/developer-extension/package.json index a7acc815c8..4ec56727ba 100644 --- a/developer-extension/package.json +++ b/developer-extension/package.json @@ -8,7 +8,7 @@ }, "devDependencies": { "@types/chrome": "0.0.180", - "@types/react": "17.0.43", + "@types/react": "18.0.1", "@types/react-dom": "18.0.0", "copy-webpack-plugin": "8.1.0", "html-webpack-plugin": "5.3.1", diff --git a/yarn.lock b/yarn.lock index 26218add95..a527fcc63f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1888,10 +1888,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@17.0.43": - version "17.0.43" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.43.tgz#4adc142887dd4a2601ce730bc56c3436fdb07a55" - integrity sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A== +"@types/react@*", "@types/react@18.0.1": + version "18.0.1" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.1.tgz#1b2e02fb7613212518733946e49fb963dfc66e19" + integrity sha512-VnWlrVgG0dYt+NqlfMI0yUYb8Rdl4XUROyH+c6gq/iFCiZ805Vi//26UW38DHnxQkbDhnrIWTBiy6oKZqL11cw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" From d78dfe50c99519039ccc0b5bd0ca4b68b01003b1 Mon Sep 17 00:00:00 2001 From: Miranda Kapin Date: Tue, 12 Apr 2022 04:02:02 -0400 Subject: [PATCH 10/23] Update README.md (#1370) * Update README.md * Update README.md * format + consistent snippet config Co-authored-by: Bastien Caudan --- packages/rum/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/rum/README.md b/packages/rum/README.md index 33083a0f04..1118c9d52b 100644 --- a/packages/rum/README.md +++ b/packages/rum/README.md @@ -44,6 +44,7 @@ datadogRum.init({ // env: 'production', // version: '1.0.0', sampleRate: 100, + replaySampleRate: 100, // if not included - default 100 trackInteractions: true, }) ``` @@ -71,6 +72,7 @@ Add the generated code snippet to the head tag of every HTML page you want to mo // env: 'production', // version: '1.0.0', sampleRate: 100, + replaySampleRate: 100, // if not included - default 100 trackInteractions: true, }) }) @@ -98,6 +100,7 @@ Add the generated code snippet to the head tag (in front of any other script tag // env: 'production', // version: '1.0.0', sampleRate: 100, + replaySampleRate: 100, // if not included - default 100 trackInteractions: true, }) From e560c722bf8f9710fdd134e3f85ce9a8ad2cea88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Apr 2022 13:28:52 +0200 Subject: [PATCH 11/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20eslint-plugin-jsdoc?= =?UTF-8?q?=20from=2038.1.6=20to=2039.1.1=20(#1496)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 38.1.6 to 39.1.1. - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v38.1.6...v39.1.1) --- updated-dependencies: - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bastien Caudan --- package.json | 2 +- yarn.lock | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 4fb3df8038..8cc31d2ecf 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "eslint-module-utils": "2.7.3", "eslint-plugin-import": "2.25.4", "eslint-plugin-jasmine": "4.1.3", - "eslint-plugin-jsdoc": "38.1.6", + "eslint-plugin-jsdoc": "39.1.1", "eslint-plugin-local-rules": "1.1.0", "eslint-plugin-prefer-arrow": "1.2.3", "eslint-plugin-unicorn": "42.0.0", diff --git a/yarn.lock b/yarn.lock index a527fcc63f..d255bddcfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4432,18 +4432,17 @@ eslint-plugin-jasmine@4.1.3: resolved "https://registry.yarnpkg.com/eslint-plugin-jasmine/-/eslint-plugin-jasmine-4.1.3.tgz#c4ced986a61dd5b180982bafe6da1cbac0941c52" integrity sha512-q8j8KnLH/4uwmPELFZvEyfEcuCuGxXScJaRdqHjOjz064GcfX6aoFbzy5VohZ5QYk2+WvoqMoqDSb9nRLf89GQ== -eslint-plugin-jsdoc@38.1.6: - version "38.1.6" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.1.6.tgz#7dfa2a6d38f550935c6a3668a1fc5a05b19e4069" - integrity sha512-n4s95oYlg0L43Bs8C0dkzIldxYf8pLCutC/tCbjIdF7VDiobuzPI+HZn9Q0BvgOvgPNgh5n7CSStql25HUG4Tw== +eslint-plugin-jsdoc@39.1.1: + version "39.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.1.1.tgz#c5538e854c987abd30d72f2e0902772c2faaa313" + integrity sha512-sT2yhYh9PFIkvi8Sgz2gVBXF0hbD9Z7iZZBlZdCMk0SIhEVWREa9RXokF4S8tJ9BBsER1GNNToDkRok4uVmmig== dependencies: "@es-joy/jsdoccomment" "~0.22.1" comment-parser "1.3.1" debug "^4.3.4" escape-string-regexp "^4.0.0" esquery "^1.4.0" - regextras "^0.8.0" - semver "^7.3.5" + semver "^7.3.6" spdx-expression-parse "^3.0.1" eslint-plugin-local-rules@1.1.0: @@ -6691,6 +6690,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.4.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.1.tgz#68ee3f4807a57d2ba185b7fd90827d5c21ce82bb" + integrity sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg== + lru-queue@0.1: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" @@ -8352,11 +8356,6 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regextras@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.8.0.tgz#ec0f99853d4912839321172f608b544814b02217" - integrity sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ== - relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -8604,12 +8603,12 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== +semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6: + version "7.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b" + integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w== dependencies: - lru-cache "^6.0.0" + lru-cache "^7.4.0" semver@~5.3.0: version "5.3.0" From d2c96ea0dcfbae53e09d57d56309973012670178 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Tue, 12 Apr 2022 16:15:28 +0200 Subject: [PATCH 12/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20react=20and=20react?= =?UTF-8?q?-dom=20(#1498)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- developer-extension/package.json | 4 +-- yarn.lock | 43 +++++++++++++++++++------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/developer-extension/package.json b/developer-extension/package.json index 4ec56727ba..1c85cdda17 100644 --- a/developer-extension/package.json +++ b/developer-extension/package.json @@ -18,8 +18,8 @@ "dependencies": { "@mantine/core": "4.1.2", "@mantine/hooks": "4.1.2", - "react": "17.0.2", - "react-dom": "17.0.2", + "react": "18.0.0", + "react-dom": "18.0.0", "react-json-view": "1.21.3" } } diff --git a/yarn.lock b/yarn.lock index d255bddcfa..e248e75453 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2593,7 +2593,17 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" -ansi-regex@5.0.1, ansi-regex@^2.0.0, ansi-regex@^2.1.1, ansi-regex@^3.0.0, ansi-regex@^5.0.1: +ansi-regex@^2.0.0, ansi-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -7407,7 +7417,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -8105,14 +8115,13 @@ react-base16-styling@^0.6.0: lodash.flow "^3.3.0" pure-color "^1.2.0" -react-dom@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023" + integrity sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.21.0" react-fast-compare@^3.0.1: version "3.2.0" @@ -8161,13 +8170,12 @@ react-textarea-autosize@^8.3.2: use-composed-ref "^1.0.0" use-latest "^1.0.0" -react@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96" + integrity sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" read-cmd-shim@^2.0.0: version "2.0.0" @@ -8547,13 +8555,12 @@ safe-regex@^2.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" + integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@^2.7.0: version "2.7.1" From 59693a23e88c45538e668f3e023c61bdfb341559 Mon Sep 17 00:00:00 2001 From: Aymeric Date: Fri, 15 Apr 2022 11:27:49 +0200 Subject: [PATCH 13/23] =?UTF-8?q?=E2=9C=A8=20[RUMF-1237]=20The=20event=20b?= =?UTF-8?q?ridge=20allowed=20hosts=20should=20also=20match=20subdomains=20?= =?UTF-8?q?(#1499)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/src/transport/eventBridge.spec.ts | 22 ++++++++++++++----- packages/core/src/transport/eventBridge.ts | 14 ++++++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/core/src/transport/eventBridge.spec.ts b/packages/core/src/transport/eventBridge.spec.ts index a119b2dbd5..13e6260a3f 100644 --- a/packages/core/src/transport/eventBridge.spec.ts +++ b/packages/core/src/transport/eventBridge.spec.ts @@ -9,16 +9,26 @@ describe('canUseEventBridge', () => { }) it('should detect when the bridge is present and the webView host is allowed', () => { - initEventBridgeStub() - expect(canUseEventBridge()).toBeTrue() - }) - - it('should not detect when the bridge is absent', () => { - expect(canUseEventBridge()).toBeFalse() + initEventBridgeStub(allowedWebViewHosts) + expect(canUseEventBridge('foo.bar')).toBeTrue() + expect(canUseEventBridge('baz.foo.bar')).toBeTrue() + expect(canUseEventBridge('www.foo.bar')).toBeTrue() + expect(canUseEventBridge('www.qux.foo.bar')).toBeTrue() }) it('should not detect when the bridge is present and the webView host is not allowed', () => { initEventBridgeStub(allowedWebViewHosts) + expect(canUseEventBridge('foo.com')).toBeFalse() + expect(canUseEventBridge('foo.bar.baz')).toBeFalse() + expect(canUseEventBridge('bazfoo.bar')).toBeFalse() + }) + + it('should not detect when the bridge on the parent domain if only the subdomain is allowed', () => { + initEventBridgeStub(['baz.foo.bar']) + expect(canUseEventBridge('foo.bar')).toBeFalse() + }) + + it('should not detect when the bridge is absent', () => { expect(canUseEventBridge()).toBeFalse() }) }) diff --git a/packages/core/src/transport/eventBridge.ts b/packages/core/src/transport/eventBridge.ts index 0a41e80d67..25da490bb4 100644 --- a/packages/core/src/transport/eventBridge.ts +++ b/packages/core/src/transport/eventBridge.ts @@ -1,4 +1,4 @@ -import { getGlobalObject, includes } from '..' +import { getGlobalObject } from '..' export interface BrowserWindowWithEventBridge extends Window { DatadogEventBridge?: DatadogEventBridge @@ -26,10 +26,16 @@ export function getEventBridge() { } } -export function canUseEventBridge(): boolean { +export function canUseEventBridge(hostname = getGlobalObject().location?.hostname): boolean { const bridge = getEventBridge() - - return !!bridge && includes(bridge.getAllowedWebViewHosts(), window.location.hostname) + return ( + !!bridge && + bridge.getAllowedWebViewHosts().some((host) => { + const escapedHost = host.replace(/\./g, '\\.') + const isDomainOrSubDomain = new RegExp(`^(.+\\.)*${escapedHost}$`) + return isDomainOrSubDomain.test(hostname) + }) + ) } function getEventBridgeGlobal() { From f538f87c1977d32e57c74783becbfd9b5db767a3 Mon Sep 17 00:00:00 2001 From: "ci.browser-sdk" Date: Mon, 18 Apr 2022 02:04:30 +0000 Subject: [PATCH 14/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20staging=20to=20stag?= =?UTF-8?q?ing-17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 577ca71512..c2724ca0e1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - CURRENT_STAGING: staging-16 + CURRENT_STAGING: staging-17 APP: 'browser-sdk' CURRENT_CI_IMAGE: 31 BUILD_STABLE_REGISTRY: '486234852809.dkr.ecr.us-east-1.amazonaws.com' From c62335e00315ef5485a7851af70591c49933aaa2 Mon Sep 17 00:00:00 2001 From: Kate Yoak Date: Tue, 19 Apr 2022 08:08:06 -0700 Subject: [PATCH 15/23] Initialization parameters minor fix (#1503) --- packages/logs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/logs/README.md b/packages/logs/README.md index 70f91c73ab..f06aeacf41 100644 --- a/packages/logs/README.md +++ b/packages/logs/README.md @@ -127,7 +127,7 @@ The following parameters are available to configure the Datadog browser logs SDK | `forwardErrorsToLogs` | Boolean | No | `true` | Set to `false` to stop forwarding console.error logs, uncaught exceptions and network errors to Datadog. | | `sampleRate` | Number | No | `100` | The percentage of sessions to track: `100` for all, `0` for none. Only tracked sessions send logs. | | `silentMultipleInit` | Boolean | No | | Prevent logging errors while having multiple init. | -| `proxyUrl` | Boolean | No | | Optional proxy URL (ex: https://www.proxy.com/path), see the full [proxy setup guide][6] for more information. | +| `proxyUrl` | String | No | | Optional proxy URL (ex: https://www.proxy.com/path), see the full [proxy setup guide][6] for more information. | Options that must have a matching configuration when using the `RUM` SDK: From 6a7dddbce19a56402e2f0a2194c9bf1a82b230c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt?= Date: Tue, 19 Apr 2022 18:45:38 +0200 Subject: [PATCH 16/23] =?UTF-8?q?=E2=9C=A8=20[RUMF-1192]=20forward=20`cons?= =?UTF-8?q?ole.*`=20logs=20to=20Datadog=20(#1505)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚩 [RUMF-1192] enable forward-logs * 📝 [RUMF-1192] add doc for forwardConsoleLogs --- packages/logs/README.md | 24 +++++---- packages/logs/src/boot/logsPublicApi.spec.ts | 24 ++------- packages/logs/src/domain/assemble.spec.ts | 1 + packages/logs/src/domain/configuration.ts | 7 ++- packages/logs/src/domain/logger.spec.ts | 16 ++---- packages/logs/src/domain/logger.ts | 19 ++++--- .../console/consoleCollection.spec.ts | 52 +------------------ .../console/consoleCollection.ts | 17 +++--- .../networkErrorCollection.spec.ts | 15 +----- .../networkError/networkErrorCollection.ts | 5 +- .../runtimeErrorCollection.spec.ts | 18 +------ packages/logs/src/domain/reportRawError.ts | 5 +- 12 files changed, 50 insertions(+), 153 deletions(-) diff --git a/packages/logs/README.md b/packages/logs/README.md index f06aeacf41..ab7a05f3bd 100644 --- a/packages/logs/README.md +++ b/packages/logs/README.md @@ -117,17 +117,18 @@ window.DD_LOGS.init({ The following parameters are available to configure the Datadog browser logs SDK to send logs to Datadog: -| Parameter | Type | Required | Default | Description | -| --------------------- | ------- | -------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `clientToken` | String | Yes | | A [Datadog client token][2]. | -| `site` | String | Yes | `datadoghq.com` | The Datadog site of your organization. US: `datadoghq.com`, EU: `datadoghq.eu` | -| `service` | String | No | | The service name for your application. It should follow the [tag syntax requirements][7]. | -| `env` | String | No | | The application’s environment, for example: prod, pre-prod, staging, etc. It should follow the [tag syntax requirements][7]. | -| `version` | String | No | | The application’s version, for example: 1.2.3, 6c44da20, 2020.02.13, etc. It should follow the [tag syntax requirements][7]. | -| `forwardErrorsToLogs` | Boolean | No | `true` | Set to `false` to stop forwarding console.error logs, uncaught exceptions and network errors to Datadog. | -| `sampleRate` | Number | No | `100` | The percentage of sessions to track: `100` for all, `0` for none. Only tracked sessions send logs. | -| `silentMultipleInit` | Boolean | No | | Prevent logging errors while having multiple init. | -| `proxyUrl` | String | No | | Optional proxy URL (ex: https://www.proxy.com/path), see the full [proxy setup guide][6] for more information. | +| Parameter | Type | Required | Default | Description | +| --------------------- | -------------------------------------------------------------------- | -------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `clientToken` | String | Yes | | A [Datadog client token][2]. | +| `site` | String | Yes | `datadoghq.com` | The Datadog site of your organization. US: `datadoghq.com`, EU: `datadoghq.eu` | +| `service` | String | No | | The service name for your application. It should follow the [tag syntax requirements][7]. | +| `env` | String | No | | The application’s environment, for example: prod, pre-prod, staging, etc. It should follow the [tag syntax requirements][7]. | +| `version` | String | No | | The application’s version, for example: 1.2.3, 6c44da20, 2020.02.13, etc. It should follow the [tag syntax requirements][7]. | +| `forwardErrorsToLogs` | Boolean | No | `true` | Set to `false` to stop forwarding console.error logs, uncaught exceptions and network errors to Datadog. | +| `forwardConsoleLogs` | `"all"` or an Array of `"log"` `"debug"` `"info"` `"warn"` `"error"` | No | `[]` | Forward logs from `console.*` to Datadog. Use `"all"` to forward everything or an array of console API names to forward only a subset. | +| `sampleRate` | Number | No | `100` | The percentage of sessions to track: `100` for all, `0` for none. Only tracked sessions send logs. | +| `silentMultipleInit` | Boolean | No | | Prevent logging errors while having multiple init. | +| `proxyUrl` | String | No | | Optional proxy URL (ex: https://www.proxy.com/path), see the full [proxy setup guide][6] for more information. | Options that must have a matching configuration when using the `RUM` SDK: @@ -185,6 +186,7 @@ The results are the same when using NPM, CDN async or CDN sync: "id": 123, "message": "Button clicked", "date": 1234567890000, + "origin": "logger", "http": { "useragent": "Mozilla/5.0 ...", }, diff --git a/packages/logs/src/boot/logsPublicApi.spec.ts b/packages/logs/src/boot/logsPublicApi.spec.ts index da38f9c901..6ec80a4ce5 100644 --- a/packages/logs/src/boot/logsPublicApi.spec.ts +++ b/packages/logs/src/boot/logsPublicApi.spec.ts @@ -1,12 +1,5 @@ import type { Context } from '@datadog/browser-core' -import { - monitor, - ONE_SECOND, - display, - ErrorSource, - updateExperimentalFeatures, - resetExperimentalFeatures, -} from '@datadog/browser-core' +import { monitor, ONE_SECOND, display, ErrorSource } from '@datadog/browser-core' import type { Clock } from '../../../core/test/specHelper' import { deleteEventBridgeStub, initEventBridgeStub, mockClock } from '../../../core/test/specHelper' import type { HybridInitConfiguration, LogsInitConfiguration } from '../domain/configuration' @@ -216,10 +209,6 @@ describe('logs entry', () => { LOGS.init(DEFAULT_INIT_CONFIGURATION) }) - afterEach(() => { - resetExperimentalFeatures() - }) - it('logs a message', () => { LOGS.logger.log('message') @@ -234,17 +223,11 @@ describe('logs entry', () => { message: { message: 'message', status: StatusType.info, + origin: ErrorSource.LOGGER, }, }) }) - it('logs a message with "logger" origin when ff forward-logs is enabled', () => { - updateExperimentalFeatures(['forward-logs']) - LOGS.logger.log('message') - - expect(getLoggedMessage(0).message.origin).toEqual(ErrorSource.LOGGER) - }) - it('returns cloned initial configuration', () => { expect(LOGS.getInitConfiguration()).toEqual(DEFAULT_INIT_CONFIGURATION) expect(LOGS.getInitConfiguration()).not.toBe(DEFAULT_INIT_CONFIGURATION) @@ -296,6 +279,7 @@ describe('logs entry', () => { expect(sendLogsSpy).not.toHaveBeenCalled() expect(display.log).toHaveBeenCalledWith('error: message', { + origin: 'logger', error: { origin: 'logger' }, logger: { name: 'foo' }, }) @@ -310,7 +294,7 @@ describe('logs entry', () => { logger.debug('message') expect(sendLogsSpy).toHaveBeenCalled() - expect(display.log).toHaveBeenCalledWith('debug: message', { logger: { name: 'foo' } }) + expect(display.log).toHaveBeenCalledWith('debug: message', { origin: 'logger', logger: { name: 'foo' } }) }) it('should have their name in their context', () => { diff --git a/packages/logs/src/domain/assemble.spec.ts b/packages/logs/src/domain/assemble.spec.ts index 10050bbef9..73ca02fafa 100644 --- a/packages/logs/src/domain/assemble.spec.ts +++ b/packages/logs/src/domain/assemble.spec.ts @@ -181,6 +181,7 @@ describe('assemble', () => { message, status: StatusType.error, date: clocksNow().timeStamp, + origin: ErrorSource.AGENT, error: { kind: undefined, origin: ErrorSource.AGENT, diff --git a/packages/logs/src/domain/configuration.ts b/packages/logs/src/domain/configuration.ts index 1795cb6850..63e93bff30 100644 --- a/packages/logs/src/domain/configuration.ts +++ b/packages/logs/src/domain/configuration.ts @@ -42,8 +42,7 @@ export function validateAndBuildLogsConfiguration( const forwardConsoleLogs = validateAndBuildForwardOption( initConfiguration.forwardConsoleLogs, objectValues(ConsoleApiName), - 'Forward Console Logs', - 'forward-logs' + 'Forward Console Logs' ) const forwardReports = validateAndBuildForwardOption( @@ -76,9 +75,9 @@ export function validateAndBuildForwardOption( option: readonly T[] | 'all' | undefined, allowedValues: T[], label: string, - featureFlag: string + featureFlag?: string ): T[] | undefined { - if (!isExperimentalFeatureEnabled(featureFlag) || option === undefined) { + if ((featureFlag !== undefined && !isExperimentalFeatureEnabled(featureFlag)) || option === undefined) { return [] } diff --git a/packages/logs/src/domain/logger.spec.ts b/packages/logs/src/domain/logger.spec.ts index cc1a5b7b70..6b284ee092 100644 --- a/packages/logs/src/domain/logger.spec.ts +++ b/packages/logs/src/domain/logger.spec.ts @@ -1,4 +1,4 @@ -import { display, ErrorSource, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' +import { display, ErrorSource } from '@datadog/browser-core' import type { LogsMessage } from './logger' import { HandlerType, Logger, STATUSES, StatusType } from './logger' import type { Sender } from './sender' @@ -19,10 +19,6 @@ describe('Logger', () => { logger = new Logger(sender) }) - afterEach(() => { - resetExperimentalFeatures() - }) - describe('log methods', () => { it("'logger.log' should have info status by default", () => { logger.log('message') @@ -30,20 +26,13 @@ describe('Logger', () => { expect(getLoggedMessage(0).status).toEqual(StatusType.info) }) - it("'logger.log' should set 'logger' origin when ff forward-logs enabled", () => { - updateExperimentalFeatures(['forward-logs']) + it("'logger.log' should set 'logger' origin", () => { logger.log('message') expect(getLoggedMessage(0).origin).toEqual(ErrorSource.LOGGER) }) - it("'logger.log' should not set 'logger' origin when ff forward-logs disabled", () => { - logger.log('message') - expect(getLoggedMessage(0).origin).not.toBeDefined() - }) - it("'logger.log' message context can override the 'logger' origin", () => { - updateExperimentalFeatures(['forward-logs']) logger.log('message', { origin: 'foo' }) expect(getLoggedMessage(0).origin).toEqual('foo') @@ -118,6 +107,7 @@ describe('Logger', () => { expect(sendLogSpy).not.toHaveBeenCalled() expect(display.log).toHaveBeenCalledWith('error: message', { error: { origin: 'logger' }, + origin: 'logger', foo: 'bar', lorem: 'ipsum', }) diff --git a/packages/logs/src/domain/logger.ts b/packages/logs/src/domain/logger.ts index fc2f1ae9c5..3cb706b703 100644 --- a/packages/logs/src/domain/logger.ts +++ b/packages/logs/src/domain/logger.ts @@ -1,5 +1,5 @@ import type { ContextValue, TimeStamp } from '@datadog/browser-core' -import { combine, ErrorSource, monitored, isExperimentalFeatureEnabled } from '@datadog/browser-core' +import { combine, ErrorSource, monitored } from '@datadog/browser-core' import type { Sender } from './sender' export const StatusType = { @@ -32,13 +32,16 @@ export class Logger { @monitored log(message: string, messageContext?: object, status: StatusType = StatusType.info) { - let logOrigin - if (isExperimentalFeatureEnabled('forward-logs')) { - logOrigin = { - origin: ErrorSource.LOGGER, - } - } - this.sender.sendLog(message, combine(logOrigin, messageContext), status) + this.sender.sendLog( + message, + combine( + { + origin: ErrorSource.LOGGER, + }, + messageContext + ), + status + ) } debug(message: string, messageContext?: object) { diff --git a/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts b/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts index 30b9264b11..649b150025 100644 --- a/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/console/consoleCollection.spec.ts @@ -1,10 +1,4 @@ -import { - ErrorSource, - resetExperimentalFeatures, - updateExperimentalFeatures, - display, - noop, -} from '@datadog/browser-core' +import { ErrorSource, display, noop } from '@datadog/browser-core' import { validateAndBuildLogsConfiguration } from '../../configuration' import { HandlerType, StatusType } from '../../logger' import { createSender } from '../../sender' @@ -24,12 +18,10 @@ describe('console collection', () => { }) afterEach(() => { - resetExperimentalFeatures() stopConsolCollection() }) - it('should send console logs when ff forward-logs is enabled', () => { - updateExperimentalFeatures(['forward-logs']) + it('should send console logs', () => { ;({ stop: stopConsolCollection } = startConsoleCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, createSender(sendLogSpy) @@ -47,44 +39,6 @@ describe('console collection', () => { expect(consoleLogSpy).toHaveBeenCalled() }) - it('should not send console logs when ff forward-logs is disabled', () => { - ;({ stop: stopConsolCollection } = startConsoleCollection( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!, - createSender(sendLogSpy) - )) - - /* eslint-disable-next-line no-console */ - console.log('foo', 'bar') - - expect(sendLogSpy).not.toHaveBeenCalled() - expect(consoleLogSpy).toHaveBeenCalled() - }) - - it('should send console errors with "console" origin when ff forward-logs is enabled', () => { - updateExperimentalFeatures(['forward-logs']) - ;({ stop: stopConsolCollection } = startConsoleCollection( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, - createSender(sendLogSpy) - )) - - /* eslint-disable-next-line no-console */ - console.error('foo', 'bar') - - expect(sendLogSpy.calls.mostRecent().args[0].origin).toEqual(ErrorSource.CONSOLE) - }) - - it('should not send console errors with "console" origin when ff forward-logs is disabled', () => { - ;({ stop: stopConsolCollection } = startConsoleCollection( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, - createSender(sendLogSpy) - )) - - /* eslint-disable-next-line no-console */ - console.error('foo', 'bar') - - expect(sendLogSpy.calls.mostRecent().args[0].origin).not.toBeDefined() - }) - it('console error should have an error object defined', () => { ;({ stop: stopConsolCollection } = startConsoleCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, @@ -101,8 +55,6 @@ describe('console collection', () => { }) it('should not print the log twice when console handler is enabled', () => { - updateExperimentalFeatures(['forward-logs']) - const sender = createSender(sendLogSpy) const displaySpy = spyOn(display, 'log') ;({ stop: stopConsolCollection } = startConsoleCollection( diff --git a/packages/logs/src/domain/logsCollection/console/consoleCollection.ts b/packages/logs/src/domain/logsCollection/console/consoleCollection.ts index 168b4034c3..4fa13bf0c6 100644 --- a/packages/logs/src/domain/logsCollection/console/consoleCollection.ts +++ b/packages/logs/src/domain/logsCollection/console/consoleCollection.ts @@ -1,5 +1,5 @@ import type { Context, ClocksState, ConsoleLog } from '@datadog/browser-core' -import { ConsoleApiName, ErrorSource, initConsoleObservable, isExperimentalFeatureEnabled } from '@datadog/browser-core' +import { ConsoleApiName, ErrorSource, initConsoleObservable } from '@datadog/browser-core' import type { LogsEvent } from '../../../logsEvent.types' import type { LogsConfiguration } from '../../configuration' import { StatusType } from '../../logger' @@ -24,18 +24,15 @@ export function startConsoleCollection(configuration: LogsConfiguration, sender: const consoleSubscription = consoleObservable.subscribe(reportConsoleLog) function reportConsoleLog(log: ConsoleLog) { - let messageContext: Partial = {} + const messageContext: Partial = { + origin: ErrorSource.CONSOLE, + } if (log.api === ConsoleApiName.error) { - messageContext = { - error: { - origin: ErrorSource.CONSOLE, // Todo: Remove in the next major release - stack: log.stack, - }, + messageContext.error = { + origin: ErrorSource.CONSOLE, // Todo: Remove in the next major release + stack: log.stack, } } - if (isExperimentalFeatureEnabled('forward-logs')) { - messageContext.origin = ErrorSource.CONSOLE - } sender.sendToHttp(log.message, messageContext, LogStatusForApi[log.api]) } diff --git a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts index db979c5004..a0b109a123 100644 --- a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.spec.ts @@ -1,4 +1,4 @@ -import { isIE, ErrorSource, updateExperimentalFeatures, resetExperimentalFeatures } from '@datadog/browser-core' +import { isIE, ErrorSource } from '@datadog/browser-core' import type { FetchStub, FetchStubManager } from '@datadog/browser-core/test/specHelper' import { SPEC_ENDPOINTS, ResponseStub, stubFetch } from '@datadog/browser-core/test/specHelper' import type { LogsConfiguration } from '../../configuration' @@ -46,18 +46,6 @@ describe('network error collection', () => { afterEach(() => { stopNetworkErrorCollection() fetchStubManager.reset() - resetExperimentalFeatures() - }) - - it('should send server errors with "network" origin when ff forward-logs is enabled', (done) => { - updateExperimentalFeatures(['forward-logs']) - - fetchStub(FAKE_URL).resolveWith(DEFAULT_REQUEST) - - fetchStubManager.whenAllComplete(() => { - expect(sendLogSpy.calls.mostRecent().args[0].origin).toEqual(ErrorSource.NETWORK) - done() - }) }) it('should track server error', (done) => { @@ -68,6 +56,7 @@ describe('network error collection', () => { message: 'Fetch error GET http://fake.com/', date: jasmine.any(Number), status: StatusType.error, + origin: ErrorSource.NETWORK, error: { origin: ErrorSource.NETWORK, stack: 'Server error', diff --git a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts index ad2f148708..0a5d5e30e0 100644 --- a/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts +++ b/packages/logs/src/domain/logsCollection/networkError/networkErrorCollection.ts @@ -8,7 +8,6 @@ import { toStackTraceString, monitor, noop, - isExperimentalFeatureEnabled, } from '@datadog/browser-core' import type { LogsEvent } from '../../../logsEvent.types' import type { LogsConfiguration } from '../../configuration' @@ -50,9 +49,7 @@ export function startNetworkErrorCollection(configuration: LogsConfiguration, se status_code: request.status, url: request.url, }, - } - if (isExperimentalFeatureEnabled('forward-logs')) { - messageContext.origin = ErrorSource.NETWORK + origin: ErrorSource.NETWORK, } sender.sendToHttp(`${format(type)} error ${request.method} ${request.url}`, messageContext, StatusType.error) diff --git a/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts index 3c7cb597ca..0569cc6df1 100644 --- a/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/runtimeError/runtimeErrorCollection.spec.ts @@ -1,4 +1,4 @@ -import { ErrorSource, Observable, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' +import { ErrorSource, Observable } from '@datadog/browser-core' import type { RawError, RelativeTime, TimeStamp } from '@datadog/browser-core' import type { LogsConfiguration } from '../../configuration' import { createSender } from '../../sender' @@ -21,7 +21,6 @@ describe('runtime error collection', () => { afterEach(() => { stopRuntimeErrorCollection() - resetExperimentalFeatures() }) it('should send runtime errors', () => { @@ -39,21 +38,8 @@ describe('runtime error collection', () => { error: { origin: ErrorSource.SOURCE, kind: 'Error', stack: undefined }, message: 'error!', status: StatusType.error, + origin: ErrorSource.SOURCE, }, ]) }) - - it('should send runtime errors with "source" origin when ff forward-logs is enabled', (done) => { - updateExperimentalFeatures(['forward-logs']) - - rawErrorObservable.notify({ - message: 'error!', - source: ErrorSource.SOURCE, - startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp }, - type: 'Error', - }) - - expect(sendLogSpy.calls.mostRecent().args[0].origin).toEqual(ErrorSource.SOURCE) - done() - }) }) diff --git a/packages/logs/src/domain/reportRawError.ts b/packages/logs/src/domain/reportRawError.ts index f844c46da0..d4345df0e5 100644 --- a/packages/logs/src/domain/reportRawError.ts +++ b/packages/logs/src/domain/reportRawError.ts @@ -1,5 +1,4 @@ import type { RawError } from '@datadog/browser-core' -import { isExperimentalFeatureEnabled } from '@datadog/browser-core' import type { LogsEvent } from '../logsEvent.types' import { StatusType } from './logger' import type { Sender } from './sender' @@ -12,9 +11,7 @@ export function reportRawError(error: RawError, sender: Sender) { origin: error.source, // Todo: Remove in the next major release stack: error.stack, }, - } - if (isExperimentalFeatureEnabled('forward-logs')) { - messageContext.origin = error.source + origin: error.source, } sender.sendToHttp(error.message, messageContext, StatusType.error) } From fc2076bc2dde4217312ea1891803794a0d84c8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt?= Date: Tue, 19 Apr 2022 20:19:57 +0200 Subject: [PATCH 17/23] =?UTF-8?q?=E2=9C=A8=20[RUMF-1192]=20forward=20Repor?= =?UTF-8?q?ts=20to=20Datadog=20(#1506)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚩 [RUMF-1192] enable forward-logs * 📝 [RUMF-1192] add doc for forwardConsoleLogs * 🚩 [RUMF-1192] enable forward-reports * [RUMF-1192] remove FF support from `validateAndBuildForwardOption` * 📝 [RUMF-1192] add doc for forwardReports --- packages/logs/README.md | 26 +++++----- .../logs/src/domain/configuration.spec.ts | 52 +++++++------------ packages/logs/src/domain/configuration.ts | 9 ++-- .../report/reportCollection.spec.ts | 17 +----- .../error/trackReportError.spec.ts | 22 +------- .../error/trackReportError.ts | 16 +----- 6 files changed, 41 insertions(+), 101 deletions(-) diff --git a/packages/logs/README.md b/packages/logs/README.md index ab7a05f3bd..1d194ceb5e 100644 --- a/packages/logs/README.md +++ b/packages/logs/README.md @@ -117,18 +117,19 @@ window.DD_LOGS.init({ The following parameters are available to configure the Datadog browser logs SDK to send logs to Datadog: -| Parameter | Type | Required | Default | Description | -| --------------------- | -------------------------------------------------------------------- | -------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -| `clientToken` | String | Yes | | A [Datadog client token][2]. | -| `site` | String | Yes | `datadoghq.com` | The Datadog site of your organization. US: `datadoghq.com`, EU: `datadoghq.eu` | -| `service` | String | No | | The service name for your application. It should follow the [tag syntax requirements][7]. | -| `env` | String | No | | The application’s environment, for example: prod, pre-prod, staging, etc. It should follow the [tag syntax requirements][7]. | -| `version` | String | No | | The application’s version, for example: 1.2.3, 6c44da20, 2020.02.13, etc. It should follow the [tag syntax requirements][7]. | -| `forwardErrorsToLogs` | Boolean | No | `true` | Set to `false` to stop forwarding console.error logs, uncaught exceptions and network errors to Datadog. | -| `forwardConsoleLogs` | `"all"` or an Array of `"log"` `"debug"` `"info"` `"warn"` `"error"` | No | `[]` | Forward logs from `console.*` to Datadog. Use `"all"` to forward everything or an array of console API names to forward only a subset. | -| `sampleRate` | Number | No | `100` | The percentage of sessions to track: `100` for all, `0` for none. Only tracked sessions send logs. | -| `silentMultipleInit` | Boolean | No | | Prevent logging errors while having multiple init. | -| `proxyUrl` | String | No | | Optional proxy URL (ex: https://www.proxy.com/path), see the full [proxy setup guide][6] for more information. | +| Parameter | Type | Required | Default | Description | +| --------------------- | ------------------------------------------------------------------------- | -------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `clientToken` | String | Yes | | A [Datadog client token][2]. | +| `site` | String | Yes | `datadoghq.com` | The Datadog site of your organization. US: `datadoghq.com`, EU: `datadoghq.eu` | +| `service` | String | No | | The service name for your application. It should follow the [tag syntax requirements][7]. | +| `env` | String | No | | The application’s environment, for example: prod, pre-prod, staging, etc. It should follow the [tag syntax requirements][7]. | +| `version` | String | No | | The application’s version, for example: 1.2.3, 6c44da20, 2020.02.13, etc. It should follow the [tag syntax requirements][7]. | +| `forwardErrorsToLogs` | Boolean | No | `true` | Set to `false` to stop forwarding console.error logs, uncaught exceptions and network errors to Datadog. | +| `forwardConsoleLogs` | `"all"` or an Array of `"log"` `"debug"` `"info"` `"warn"` `"error"` | No | `[]` | Forward logs from `console.*` to Datadog. Use `"all"` to forward everything or an array of console API names to forward only a subset. | +| `forwardReports` | `"all"` or an Array of `"intervention"` `"deprecation"` `"csp_violation"` | No | `[]` | Forward reports from the [Reporting API][8] to Datadog. Use `"all"` to forward everything or an array of report types to forward only a subset. | +| `sampleRate` | Number | No | `100` | The percentage of sessions to track: `100` for all, `0` for none. Only tracked sessions send logs. | +| `silentMultipleInit` | Boolean | No | | Prevent logging errors while having multiple init. | +| `proxyUrl` | String | No | | Optional proxy URL (ex: https://www.proxy.com/path), see the full [proxy setup guide][6] for more information. | Options that must have a matching configuration when using the `RUM` SDK: @@ -672,3 +673,4 @@ window.DD_LOGS && DD_LOGS.logger.setHandler(['', '']) [5]: /real_user_monitoring/guide/enrich-and-control-rum-data/ [6]: https://docs.datadoghq.com/real_user_monitoring/faq/proxy_rum_data/ [7]: https://docs.datadoghq.com/getting_started/tagging/#defining-tags +[8]: https://developer.mozilla.org/en-US/docs/Web/API/Reporting_API diff --git a/packages/logs/src/domain/configuration.spec.ts b/packages/logs/src/domain/configuration.spec.ts index 326e7da550..f10b709073 100644 --- a/packages/logs/src/domain/configuration.spec.ts +++ b/packages/logs/src/domain/configuration.spec.ts @@ -1,4 +1,4 @@ -import { display, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' +import { display } from '@datadog/browser-core' import { validateAndBuildForwardOption, validateAndBuildLogsConfiguration } from './configuration' const DEFAULT_INIT_CONFIGURATION = { clientToken: 'xxx' } @@ -67,47 +67,33 @@ describe('validateAndBuildForwardOption', () => { let displaySpy: jasmine.Spy const allowedValues = ['foo', 'bar'] const label = 'Label' - const ff = 'flag' const errorMessage = 'Label should be "all" or an array with allowed values "foo", "bar"' - describe('if ff enabled', () => { - beforeEach(() => { - displaySpy = spyOn(display, 'error') - updateExperimentalFeatures([ff]) - }) - - afterEach(() => { - resetExperimentalFeatures() - }) - - it('does not validate the configuration if an incorrect string is provided', () => { - validateAndBuildForwardOption('foo' as any, allowedValues, label, ff) + beforeEach(() => { + displaySpy = spyOn(display, 'error') + }) - expect(displaySpy).toHaveBeenCalledOnceWith(errorMessage) - }) + it('does not validate the configuration if an incorrect string is provided', () => { + validateAndBuildForwardOption('foo' as any, allowedValues, label) - it('does not validate the configuration if an incorrect api is provided', () => { - validateAndBuildForwardOption(['dir'], allowedValues, label, ff) + expect(displaySpy).toHaveBeenCalledOnceWith(errorMessage) + }) - expect(displaySpy).toHaveBeenCalledOnceWith(errorMessage) - }) + it('does not validate the configuration if an incorrect api is provided', () => { + validateAndBuildForwardOption(['dir'], allowedValues, label) - it('defaults to an empty array', () => { - expect(validateAndBuildForwardOption(undefined, allowedValues, label, ff)).toEqual([]) - }) + expect(displaySpy).toHaveBeenCalledOnceWith(errorMessage) + }) - it('is set to provided value', () => { - expect(validateAndBuildForwardOption(['foo'], allowedValues, label, ff)).toEqual(['foo']) - }) + it('defaults to an empty array', () => { + expect(validateAndBuildForwardOption(undefined, allowedValues, label)).toEqual([]) + }) - it('contains all options when "all" is provided', () => { - expect(validateAndBuildForwardOption('all', allowedValues, label, ff)).toEqual(allowedValues) - }) + it('is set to provided value', () => { + expect(validateAndBuildForwardOption(['foo'], allowedValues, label)).toEqual(['foo']) }) - describe('if ff disabled', () => { - it('should be set to empty array', () => { - expect(validateAndBuildForwardOption(['log'], allowedValues, label, ff)).toEqual([]) - }) + it('contains all options when "all" is provided', () => { + expect(validateAndBuildForwardOption('all', allowedValues, label)).toEqual(allowedValues) }) }) diff --git a/packages/logs/src/domain/configuration.ts b/packages/logs/src/domain/configuration.ts index 63e93bff30..84c2b52d8c 100644 --- a/packages/logs/src/domain/configuration.ts +++ b/packages/logs/src/domain/configuration.ts @@ -4,7 +4,6 @@ import { ONE_KILO_BYTE, validateAndBuildConfiguration, display, - isExperimentalFeatureEnabled, removeDuplicates, ConsoleApiName, RawReportType, @@ -48,8 +47,7 @@ export function validateAndBuildLogsConfiguration( const forwardReports = validateAndBuildForwardOption( initConfiguration.forwardReports, objectValues(RawReportType), - 'Forward Reports', - 'forward-reports' + 'Forward Reports' ) if (!baseConfiguration || !forwardConsoleLogs || !forwardReports) { @@ -74,10 +72,9 @@ export function validateAndBuildLogsConfiguration( export function validateAndBuildForwardOption( option: readonly T[] | 'all' | undefined, allowedValues: T[], - label: string, - featureFlag?: string + label: string ): T[] | undefined { - if ((featureFlag !== undefined && !isExperimentalFeatureEnabled(featureFlag)) || option === undefined) { + if (option === undefined) { return [] } diff --git a/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts b/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts index 387132522c..6056a7ccda 100644 --- a/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts +++ b/packages/logs/src/domain/logsCollection/report/reportCollection.spec.ts @@ -1,4 +1,4 @@ -import { ErrorSource, noop, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' +import { ErrorSource, noop } from '@datadog/browser-core' import { stubReportingObserver } from '@datadog/browser-core/test/stubReportApis' import { validateAndBuildLogsConfiguration } from '../../configuration' import { StatusType } from '../../logger' @@ -19,12 +19,10 @@ describe('reports', () => { afterEach(() => { reportingObserverStub.reset() - resetExperimentalFeatures() stopReportCollection() }) - it('should send reports when ff forward-reports is enabled', () => { - updateExperimentalFeatures(['forward-reports']) + it('should send reports', () => { ;({ stop: stopReportCollection } = startReportCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, createSender(sendLogSpy) @@ -43,16 +41,6 @@ describe('reports', () => { }) }) - it('should not send reports when ff forward-reports is disabled', () => { - ;({ stop: stopReportCollection } = startReportCollection( - validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['intervention'] })!, - createSender(sendLogSpy) - )) - reportingObserverStub.raiseReport('intervention') - - expect(sendLogSpy).not.toHaveBeenCalled() - }) - it('should not send reports when forwardReports init option not specified', () => { ;({ stop: stopReportCollection } = startReportCollection( validateAndBuildLogsConfiguration({ ...initConfiguration })!, @@ -64,7 +52,6 @@ describe('reports', () => { }) it('should add the source file information to the message for non error reports', () => { - updateExperimentalFeatures(['forward-reports']) ;({ stop: stopReportCollection } = startReportCollection( validateAndBuildLogsConfiguration({ ...initConfiguration, forwardReports: ['deprecation'] })!, createSender(sendLogSpy) diff --git a/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.spec.ts index 6b33a4fe44..2afe69681d 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.spec.ts @@ -1,12 +1,5 @@ import type { RawError, Subscription } from '@datadog/browser-core' -import { - ErrorHandling, - ErrorSource, - Observable, - clocksNow, - updateExperimentalFeatures, - resetExperimentalFeatures, -} from '@datadog/browser-core' +import { ErrorHandling, ErrorSource, Observable, clocksNow } from '@datadog/browser-core' import type { Clock } from '../../../../../core/test/specHelper' import { mockClock } from '../../../../../core/test/specHelper' import { stubReportingObserver } from '../../../../../core/test/stubReportApis' @@ -31,12 +24,9 @@ describe('trackReportError', () => { subscription.unsubscribe() clock.cleanup() reportingObserverStub.reset() - resetExperimentalFeatures() }) - it('should report when ff forward-reports enabled', () => { - updateExperimentalFeatures(['forward-reports']) - + it('should track reports', () => { trackReportError(errorObservable) reportingObserverStub.raiseReport('intervention') @@ -49,12 +39,4 @@ describe('trackReportError', () => { type: 'NavigatorVibrate', }) }) - - it('should not report when ff forward-reports disabled', () => { - trackReportError(errorObservable) - - reportingObserverStub.raiseReport('intervention') - - expect(notifyLog).not.toHaveBeenCalled() - }) }) diff --git a/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.ts b/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.ts index b87200a1dd..ba7bda0e67 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/error/trackReportError.ts @@ -1,21 +1,7 @@ import type { Observable, RawError } from '@datadog/browser-core' -import { - clocksNow, - ErrorHandling, - ErrorSource, - initReportObservable, - RawReportType, - isExperimentalFeatureEnabled, - noop, -} from '@datadog/browser-core' +import { clocksNow, ErrorHandling, ErrorSource, initReportObservable, RawReportType } from '@datadog/browser-core' export function trackReportError(errorObservable: Observable) { - if (!isExperimentalFeatureEnabled('forward-reports')) { - return { - stop: noop, - } - } - const subscription = initReportObservable([RawReportType.cspViolation, RawReportType.intervention]).subscribe( (reportError) => errorObservable.notify({ From acdb79ddbd3866cfeab8019ca4f6ae6b777e37b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Apr 2022 11:27:57 +0200 Subject: [PATCH 18/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20glob=20from=207.2.0?= =?UTF-8?q?=20to=208.0.1=20(#1502)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [glob](https://github.com/isaacs/node-glob) from 7.2.0 to 8.0.1. - [Release notes](https://github.com/isaacs/node-glob/releases) - [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md) - [Commits](https://github.com/isaacs/node-glob/compare/v7.2.0...v8.0.1) --- updated-dependencies: - dependency-name: glob dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 28 +++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 8cc31d2ecf..6d72c5ccc4 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "eslint-plugin-prefer-arrow": "1.2.3", "eslint-plugin-unicorn": "42.0.0", "express": "4.17.3", - "glob": "7.2.0", + "glob": "8.0.1", "jasmine-core": "3.6.0", "js-polyfills": "0.1.43", "json-schema-to-typescript": "bcaudan/json-schema-to-typescript#bcaudan/add-readonly-support", diff --git a/yarn.lock b/yarn.lock index e248e75453..5c8113d960 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2593,17 +2593,7 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" -ansi-regex@^2.0.0, ansi-regex@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - -ansi-regex@^5.0.1: +ansi-regex@5.0.1, ansi-regex@^2.0.0, ansi-regex@^2.1.1, ansi-regex@^3.0.0, ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -5203,7 +5193,19 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.2.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: +glob@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.1.tgz#00308f5c035aa0b2a447cd37ead267ddff1577d3" + integrity sha512-cF7FYZZ47YzmCu7dDy50xSRRfO3ErRfrXuLZcNIuyiJEco0XSrGtuilG19L5xp3NcwTx7Gn+X6Tv3fmsUPTbow== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -6968,7 +6970,7 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@5.0.1, minimatch@^5.0.0: +minimatch@5.0.1, minimatch@^5.0.0, minimatch@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== From 16368f4a92eb378751222d5f2dcd4579fe684374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt?= Date: Thu, 21 Apr 2022 10:40:46 +0200 Subject: [PATCH 19/23] v4.8.0 (#1509) --- CHANGELOG.md | 8 +++++ developer-extension/package.json | 2 +- lerna.json | 2 +- packages/core/package.json | 2 +- packages/logs/package.json | 4 +-- packages/rum-core/package.json | 4 +-- packages/rum-slim/package.json | 6 ++-- packages/rum/package.json | 6 ++-- performances/package.json | 2 +- test/app/yarn.lock | 56 ++++++++++++++++---------------- 10 files changed, 50 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1a6b7b914..1a11c759b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,14 @@ --- +## v4.8.0 + +- ✨ [RUMF-1192] forward Reports to Datadog ([#1506](https://github.com/DataDog/browser-sdk/pull/1506)) +- ✨ [RUMF-1192] forward `console.*` logs to Datadog ([#1505](https://github.com/DataDog/browser-sdk/pull/1505)) +- 📝 fix documentation for `proxyUrl` documentation ([#1503](https://github.com/DataDog/browser-sdk/pull/1503)) +- ✨ [RUMF-1237] The event bridge allowed hosts should also match subdomains ([#1499](https://github.com/DataDog/browser-sdk/pull/1499)) +- 📝 add `replaySampleRate` to README examples ([#1370](https://github.com/DataDog/browser-sdk/pull/1370)) + ## v4.7.1 - 🐛 Adjust records generated during view change so their date matches the view date ([#1486](https://github.com/DataDog/browser-sdk/pull/1486)) diff --git a/developer-extension/package.json b/developer-extension/package.json index 1c85cdda17..3030fde8b5 100644 --- a/developer-extension/package.json +++ b/developer-extension/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-sdk-developer-extension", - "version": "4.7.1", + "version": "4.8.0", "private": true, "scripts": { "build": "rm -rf dist && webpack --mode production", diff --git a/lerna.json b/lerna.json index b957e5daac..60ae890316 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "npmClient": "yarn", "useWorkspaces": true, - "version": "4.7.1", + "version": "4.8.0", "publishConfig": { "access": "public" } diff --git a/packages/core/package.json b/packages/core/package.json index 3b54db92e2..139a7d44b0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-core", - "version": "4.7.1", + "version": "4.8.0", "license": "Apache-2.0", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/packages/logs/package.json b/packages/logs/package.json index 2ccccfda7a..0c9eae0a76 100644 --- a/packages/logs/package.json +++ b/packages/logs/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-logs", - "version": "4.7.1", + "version": "4.8.0", "license": "Apache-2.0", "main": "cjs/entries/main.js", "module": "esm/entries/main.js", @@ -13,7 +13,7 @@ "replace-build-env": "node ../../scripts/replace-build-env.js" }, "dependencies": { - "@datadog/browser-core": "4.7.1" + "@datadog/browser-core": "4.8.0" }, "devDependencies": { "@types/sinon": "9.0.10", diff --git a/packages/rum-core/package.json b/packages/rum-core/package.json index 026a7e0059..127565ae3a 100644 --- a/packages/rum-core/package.json +++ b/packages/rum-core/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-rum-core", - "version": "4.7.1", + "version": "4.8.0", "license": "Apache-2.0", "main": "cjs/index.js", "module": "esm/index.js", @@ -12,7 +12,7 @@ "replace-build-env": "node ../../scripts/replace-build-env.js" }, "dependencies": { - "@datadog/browser-core": "4.7.1" + "@datadog/browser-core": "4.8.0" }, "devDependencies": { "ajv": "6.12.6" diff --git a/packages/rum-slim/package.json b/packages/rum-slim/package.json index aba885089f..8efd9abe6a 100644 --- a/packages/rum-slim/package.json +++ b/packages/rum-slim/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-rum-slim", - "version": "4.7.1", + "version": "4.8.0", "license": "Apache-2.0", "main": "cjs/entries/main.js", "module": "esm/entries/main.js", @@ -12,8 +12,8 @@ "build:esm": "rm -rf esm && tsc -p tsconfig.esm.json" }, "dependencies": { - "@datadog/browser-core": "4.7.1", - "@datadog/browser-rum-core": "4.7.1" + "@datadog/browser-core": "4.8.0", + "@datadog/browser-rum-core": "4.8.0" }, "repository": { "type": "git", diff --git a/packages/rum/package.json b/packages/rum/package.json index b3c93b3fdc..630a3c7f7c 100644 --- a/packages/rum/package.json +++ b/packages/rum/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/browser-rum", - "version": "4.7.1", + "version": "4.8.0", "license": "Apache-2.0", "main": "cjs/entries/main.js", "module": "esm/entries/main.js", @@ -12,8 +12,8 @@ "build:esm": "rm -rf esm && tsc -p tsconfig.esm.json" }, "dependencies": { - "@datadog/browser-core": "4.7.1", - "@datadog/browser-rum-core": "4.7.1" + "@datadog/browser-core": "4.8.0", + "@datadog/browser-rum-core": "4.8.0" }, "repository": { "type": "git", diff --git a/performances/package.json b/performances/package.json index 3bb52bfc8a..9c03b3336c 100644 --- a/performances/package.json +++ b/performances/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "performances", - "version": "4.7.1", + "version": "4.8.0", "scripts": { "start": "ts-node ./src/main.ts" }, diff --git a/test/app/yarn.lock b/test/app/yarn.lock index 683c98647f..40324228dd 100644 --- a/test/app/yarn.lock +++ b/test/app/yarn.lock @@ -2,24 +2,24 @@ # yarn lockfile v1 -"@datadog/browser-core@4.7.1", "@datadog/browser-core@file:../../packages/core": - version "4.7.1" +"@datadog/browser-core@4.8.0", "@datadog/browser-core@file:../../packages/core": + version "4.8.0" "@datadog/browser-logs@file:../../packages/logs": - version "4.7.1" + version "4.8.0" dependencies: - "@datadog/browser-core" "4.7.1" + "@datadog/browser-core" "4.8.0" -"@datadog/browser-rum-core@4.7.1", "@datadog/browser-rum-core@file:../../packages/rum-core": - version "4.7.1" +"@datadog/browser-rum-core@4.8.0", "@datadog/browser-rum-core@file:../../packages/rum-core": + version "4.8.0" dependencies: - "@datadog/browser-core" "4.7.1" + "@datadog/browser-core" "4.8.0" "@datadog/browser-rum@file:../../packages/rum": - version "4.7.1" + version "4.8.0" dependencies: - "@datadog/browser-core" "4.7.1" - "@datadog/browser-rum-core" "4.7.1" + "@datadog/browser-core" "4.8.0" + "@datadog/browser-rum-core" "4.8.0" "@types/eslint-scope@^3.7.0": version "3.7.3" @@ -53,9 +53,9 @@ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/node@*": - version "17.0.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" - integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== + version "17.0.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.25.tgz#527051f3c2f77aa52e5dc74e45a3da5fb2301448" + integrity sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w== "@webassemblyjs/ast@1.11.0": version "1.11.0" @@ -244,9 +244,9 @@ buffer-from@^1.0.0: integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== caniuse-lite@^1.0.30001317: - version "1.0.30001325" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz#2b4ad19b77aa36f61f2eaf72e636d7481d55e606" - integrity sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ== + version "1.0.30001332" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" + integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== chalk@^2.3.0: version "2.4.2" @@ -285,9 +285,9 @@ core-util-is@~1.0.0: integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== electron-to-chromium@^1.4.84: - version "1.4.106" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.106.tgz#e7a3bfa9d745dd9b9e597616cb17283cc349781a" - integrity sha512-ZYfpVLULm67K7CaaGP7DmjyeMY4naxsbTy+syVVxT6QHI1Ww8XbJjmr9fDckrhq44WzCrcC5kH3zGpdusxwwqg== + version "1.4.116" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.116.tgz#cf0b106d462a78e43ef33cc269caf2ad70e3c7a8" + integrity sha512-sy2ol5DTH0sy8xvAglyHFxsNFXFsOBfa6rGmrtjiSdQOp53ossspduOzU+5Lx23H7GxEjjvtSF36XqkajV6Z5A== emojis-list@^3.0.0: version "3.0.0" @@ -304,9 +304,9 @@ enhanced-resolve@^4.0.0: tapable "^1.0.0" enhanced-resolve@^5.7.0: - version "5.9.2" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz#0224dcd6a43389ebfb2d55efee517e5466772dd9" - integrity sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA== + version "5.9.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88" + integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -442,9 +442,9 @@ json5@^1.0.1: minimist "^1.2.0" loader-runner@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== loader-utils@^1.0.2: version "1.4.0" @@ -499,9 +499,9 @@ neo-async@^2.6.2: integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== node-releases@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" - integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== + version "2.0.3" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" + integrity sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw== picocolors@^1.0.0: version "1.0.0" From bac84520755a00d8f4d8a611fb115a3eacd2ce62 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 21 Apr 2022 05:04:59 -0500 Subject: [PATCH 20/23] Update links to api key docs (#1508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update links to api key docs This updates the links to the datadog documentation. This was failing to link to a page on `npmjs.com` and on `github.com`. * 📝 update another link * 📝 add a comment to advise for absolute URLs * 🎨 fix format Co-authored-by: Benoît Zugmeyer --- packages/logs/README.md | 8 +++++--- packages/rum/README.md | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/logs/README.md b/packages/logs/README.md index 1d194ceb5e..c5665b5da5 100644 --- a/packages/logs/README.md +++ b/packages/logs/README.md @@ -666,11 +666,13 @@ window.DD_LOGS && DD_LOGS.logger.setHandler(['', '']) **Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. -[1]: /account_management/api-app-keys/#api-keys -[2]: /account_management/api-app-keys/#client-tokens + + +[1]: https://docs.datadoghq.com/account_management/api-app-keys/#api-keys +[2]: https://docs.datadoghq.com/account_management/api-app-keys/#client-tokens [3]: https://www.npmjs.com/package/@datadog/browser-logs [4]: https://github.com/DataDog/browser-sdk/blob/main/packages/logs/BROWSER_SUPPORT.md -[5]: /real_user_monitoring/guide/enrich-and-control-rum-data/ +[5]: https://docs.datadoghq.com/real_user_monitoring/guide/enrich-and-control-rum-data/ [6]: https://docs.datadoghq.com/real_user_monitoring/faq/proxy_rum_data/ [7]: https://docs.datadoghq.com/getting_started/tagging/#defining-tags [8]: https://developer.mozilla.org/en-US/docs/Web/API/Reporting_API diff --git a/packages/rum/README.md b/packages/rum/README.md index 1118c9d52b..2b06cdb6fa 100644 --- a/packages/rum/README.md +++ b/packages/rum/README.md @@ -305,6 +305,8 @@ datadogRum.init({ {{< partial name="whats-next/whats-next.html" >}} + + [1]: https://app.datadoghq.com/rum/list [2]: https://docs.datadoghq.com/real_user_monitoring/data_collected/ [3]: https://docs.datadoghq.com/real_user_monitoring/dashboards/ From bb0a5780ea2f6ca092262a01524b957fe4f7c966 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Thu, 21 Apr 2022 16:15:43 +0200 Subject: [PATCH 21/23] =?UTF-8?q?=E2=9A=97=EF=B8=8F=20[RUMF-1182]=20add=20?= =?UTF-8?q?telemetry=20sample=20rate=20(#1510)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✅ add tests on setDebug * ⚗️ add telemetry sample rate --- .../configuration/configuration.spec.ts | 25 ++++++- .../src/domain/configuration/configuration.ts | 8 ++ .../internalMonitoring.spec.ts | 73 +++++++++++++++++++ .../internalMonitoring/internalMonitoring.ts | 10 ++- test/e2e/lib/framework/createTest.ts | 2 + 5 files changed, 113 insertions(+), 5 deletions(-) diff --git a/packages/core/src/domain/configuration/configuration.spec.ts b/packages/core/src/domain/configuration/configuration.spec.ts index e9f57c201e..c5487e9c6b 100644 --- a/packages/core/src/domain/configuration/configuration.spec.ts +++ b/packages/core/src/domain/configuration/configuration.spec.ts @@ -33,6 +33,11 @@ describe('validateAndBuildConfiguration', () => { expect(displaySpy).toHaveBeenCalledOnceWith('Client Token is not configured, we will not send any data.') }) + it("shouldn't display any error if the configuration is correct", () => { + validateAndBuildConfiguration({ clientToken: 'yes' }) + expect(displaySpy).not.toHaveBeenCalled() + }) + it('requires sampleRate to be a percentage', () => { expect( validateAndBuildConfiguration({ clientToken, sampleRate: 'foo' } as unknown as InitConfiguration) @@ -44,12 +49,28 @@ describe('validateAndBuildConfiguration', () => { validateAndBuildConfiguration({ clientToken, sampleRate: 200 } as unknown as InitConfiguration) ).toBeUndefined() expect(displaySpy).toHaveBeenCalledOnceWith('Sample Rate should be a number between 0 and 100') - }) - it("shouldn't display any error if the configuration is correct", () => { + displaySpy.calls.reset() validateAndBuildConfiguration({ clientToken: 'yes', sampleRate: 1 }) expect(displaySpy).not.toHaveBeenCalled() }) + + it('requires telemetrySampleRate to be a percentage', () => { + expect( + validateAndBuildConfiguration({ clientToken, telemetrySampleRate: 'foo' } as unknown as InitConfiguration) + ).toBeUndefined() + expect(displaySpy).toHaveBeenCalledOnceWith('Telemetry Sample Rate should be a number between 0 and 100') + + displaySpy.calls.reset() + expect( + validateAndBuildConfiguration({ clientToken, telemetrySampleRate: 200 } as unknown as InitConfiguration) + ).toBeUndefined() + expect(displaySpy).toHaveBeenCalledOnceWith('Telemetry Sample Rate should be a number between 0 and 100') + + displaySpy.calls.reset() + validateAndBuildConfiguration({ clientToken: 'yes', telemetrySampleRate: 1 }) + expect(displaySpy).not.toHaveBeenCalled() + }) }) describe('cookie options', () => { diff --git a/packages/core/src/domain/configuration/configuration.ts b/packages/core/src/domain/configuration/configuration.ts index b3d286a3fe..cd218c9e1e 100644 --- a/packages/core/src/domain/configuration/configuration.ts +++ b/packages/core/src/domain/configuration/configuration.ts @@ -19,6 +19,7 @@ export interface InitConfiguration { clientToken: string beforeSend?: GenericBeforeSendCallback | undefined sampleRate?: number | undefined + telemetrySampleRate?: number | undefined silentMultipleInit?: boolean | undefined // transport options @@ -56,6 +57,7 @@ export interface Configuration extends TransportConfiguration { beforeSend: GenericBeforeSendCallback | undefined cookieOptions: CookieOptions sampleRate: number + telemetrySampleRate: number service: string | undefined silentMultipleInit: boolean @@ -81,6 +83,11 @@ export function validateAndBuildConfiguration(initConfiguration: InitConfigurati return } + if (initConfiguration.telemetrySampleRate !== undefined && !isPercentage(initConfiguration.telemetrySampleRate)) { + display.error('Telemetry Sample Rate should be a number between 0 and 100') + return + } + // Set the experimental feature flags as early as possible, so we can use them in most places updateExperimentalFeatures(initConfiguration.enableExperimentalFeatures) @@ -90,6 +97,7 @@ export function validateAndBuildConfiguration(initConfiguration: InitConfigurati initConfiguration.beforeSend && catchUserErrors(initConfiguration.beforeSend, 'beforeSend threw an error:'), cookieOptions: buildCookieOptions(initConfiguration), sampleRate: initConfiguration.sampleRate ?? 100, + telemetrySampleRate: initConfiguration.telemetrySampleRate ?? 20, service: initConfiguration.service, silentMultipleInit: !!initConfiguration.silentMultipleInit, diff --git a/packages/core/src/domain/internalMonitoring/internalMonitoring.spec.ts b/packages/core/src/domain/internalMonitoring/internalMonitoring.spec.ts index 33890fac54..2beceea2a6 100644 --- a/packages/core/src/domain/internalMonitoring/internalMonitoring.spec.ts +++ b/packages/core/src/domain/internalMonitoring/internalMonitoring.spec.ts @@ -1,4 +1,5 @@ import type { Context } from '../../tools/context' +import { display } from '../../tools/display' import type { Configuration } from '../configuration' import { updateExperimentalFeatures, resetExperimentalFeatures } from '../configuration' import type { InternalMonitoring, MonitoringMessage } from './internalMonitoring' @@ -8,11 +9,13 @@ import { resetInternalMonitoring, startInternalMonitoring, callMonitored, + setDebugMode, } from './internalMonitoring' import type { TelemetryEvent } from './telemetryEvent.types' const configuration: Partial = { maxInternalMonitoringMessagesPerPage: 7, + telemetrySampleRate: 100, } describe('internal monitoring', () => { @@ -189,6 +192,52 @@ describe('internal monitoring', () => { }) }) + describe('setDebug', () => { + let displaySpy: jasmine.Spy + + beforeEach(() => { + displaySpy = spyOn(display, 'error') + }) + + afterEach(() => { + resetInternalMonitoring() + resetExperimentalFeatures() + }) + + it('when not called, should not display error', () => { + startInternalMonitoring(configuration as Configuration) + + callMonitored(() => { + throw new Error('message') + }) + + expect(displaySpy).not.toHaveBeenCalled() + }) + + it('when called, should display error', () => { + startInternalMonitoring(configuration as Configuration) + setDebugMode(true) + + callMonitored(() => { + throw new Error('message') + }) + + expect(displaySpy).toHaveBeenCalled() + }) + + it('when called and telemetry not sampled, should display error', () => { + updateExperimentalFeatures(['telemetry']) + startInternalMonitoring({ ...configuration, telemetrySampleRate: 0 } as Configuration) + setDebugMode(true) + + callMonitored(() => { + throw new Error('message') + }) + + expect(displaySpy).toHaveBeenCalled() + }) + }) + describe('new telemetry', () => { let internalMonitoring: InternalMonitoring let notifySpy: jasmine.Spy<(event: TelemetryEvent & Context) => void> @@ -246,6 +295,30 @@ describe('internal monitoring', () => { expect(oldNotifySpy.calls.mostRecent().args[0].foo).toEqual('bar') }) + + it('should notify when sampled', () => { + spyOn(Math, 'random').and.callFake(() => 0) + internalMonitoring = startInternalMonitoring({ ...configuration, telemetrySampleRate: 50 } as Configuration) + internalMonitoring.telemetryEventObservable.subscribe(notifySpy) + + callMonitored(() => { + throw new Error('message') + }) + + expect(notifySpy).toHaveBeenCalled() + }) + + it('should not notify when not sampled', () => { + spyOn(Math, 'random').and.callFake(() => 1) + internalMonitoring = startInternalMonitoring({ ...configuration, telemetrySampleRate: 50 } as Configuration) + internalMonitoring.telemetryEventObservable.subscribe(notifySpy) + + callMonitored(() => { + throw new Error('message') + }) + + expect(notifySpy).not.toHaveBeenCalled() + }) }) describe('when disabled', () => { diff --git a/packages/core/src/domain/internalMonitoring/internalMonitoring.ts b/packages/core/src/domain/internalMonitoring/internalMonitoring.ts index 6b216b7dcd..3cabc172e3 100644 --- a/packages/core/src/domain/internalMonitoring/internalMonitoring.ts +++ b/packages/core/src/domain/internalMonitoring/internalMonitoring.ts @@ -1,7 +1,7 @@ import type { Context } from '../../tools/context' import { display } from '../../tools/display' import { toStackTraceString } from '../../tools/error' -import { assign, combine, jsonStringify } from '../../tools/utils' +import { assign, combine, jsonStringify, performDraw } from '../../tools/utils' import type { Configuration } from '../configuration' import { computeStackTrace } from '../tracekit' import { Observable } from '../../tools/observable' @@ -38,7 +38,8 @@ const monitoringConfiguration: { debugMode?: boolean maxMessagesPerPage: number sentMessageCount: number -} = { maxMessagesPerPage: 0, sentMessageCount: 0 } + telemetryEnabled: boolean +} = { maxMessagesPerPage: 0, sentMessageCount: 0, telemetryEnabled: false } let onInternalMonitoringMessageCollected: ((message: MonitoringMessage) => void) | undefined @@ -48,9 +49,11 @@ export function startInternalMonitoring(configuration: Configuration): InternalM const monitoringMessageObservable = new Observable() const telemetryEventObservable = new Observable() + monitoringConfiguration.telemetryEnabled = performDraw(configuration.telemetrySampleRate) + onInternalMonitoringMessageCollected = (message: MonitoringMessage) => { monitoringMessageObservable.notify(withContext(message)) - if (isExperimentalFeatureEnabled('telemetry')) { + if (isExperimentalFeatureEnabled('telemetry') && monitoringConfiguration.telemetryEnabled) { telemetryEventObservable.notify(toTelemetryEvent(message)) } } @@ -113,6 +116,7 @@ export function startFakeInternalMonitoring() { export function resetInternalMonitoring() { onInternalMonitoringMessageCollected = undefined + monitoringConfiguration.debugMode = undefined } export function monitored unknown>( diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 08074d472a..65ef093a93 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -15,11 +15,13 @@ import { createMockServerApp } from './serverApps/mock' const DEFAULT_RUM_CONFIGURATION = { applicationId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', clientToken: 'token', + telemetrySampleRate: 100, enableExperimentalFeatures: [], } const DEFAULT_LOGS_CONFIGURATION = { clientToken: 'token', + telemetrySampleRate: 100, } export function createTest(title: string) { From 9fa53fb55c3406525ac4a360d3490656b810bcd8 Mon Sep 17 00:00:00 2001 From: "ci.browser-sdk" Date: Mon, 25 Apr 2022 02:04:30 +0000 Subject: [PATCH 22/23] =?UTF-8?q?=F0=9F=91=B7=20Bump=20staging=20to=20stag?= =?UTF-8?q?ing-18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c2724ca0e1..0bae2d1e96 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - CURRENT_STAGING: staging-17 + CURRENT_STAGING: staging-18 APP: 'browser-sdk' CURRENT_CI_IMAGE: 31 BUILD_STABLE_REGISTRY: '486234852809.dkr.ecr.us-east-1.amazonaws.com' From 97aecd996c4d96dafd77b934a587896fbcb0297d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt?= Date: Tue, 26 Apr 2022 14:18:54 +0200 Subject: [PATCH 23/23] =?UTF-8?q?=F0=9F=90=9B=20[RUMF-1240]=20fix=20attrib?= =?UTF-8?q?ute=20mutating=20to=20an=20empty=20value=20(#1512)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/record/mutationObserver.spec.ts | 37 +++++++++++++++++++ .../rum/src/domain/record/mutationObserver.ts | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/rum/src/domain/record/mutationObserver.spec.ts b/packages/rum/src/domain/record/mutationObserver.spec.ts index 87aadef751..3458917c70 100644 --- a/packages/rum/src/domain/record/mutationObserver.spec.ts +++ b/packages/rum/src/domain/record/mutationObserver.spec.ts @@ -528,6 +528,43 @@ describe('startMutationCollection', () => { }) }) + it('emits a mutation with an empty string when an attribute is changed to an empty string', () => { + const serializedDocument = serializeDocument(document, NodePrivacyLevel.ALLOW) + const { mutationController, getLatestMutationPayload } = startMutationCollection() + + sandbox.setAttribute('foo', '') + mutationController.flush() + + const { validate, expectInitialNode } = createMutationPayloadValidator(serializedDocument) + validate(getLatestMutationPayload(), { + attributes: [ + { + node: expectInitialNode({ idAttribute: 'sandbox' }), + attributes: { foo: '' }, + }, + ], + }) + }) + + it('emits a mutation with `null` when an attribute is removed', () => { + sandbox.setAttribute('foo', 'bar') + const serializedDocument = serializeDocument(document, NodePrivacyLevel.ALLOW) + const { mutationController, getLatestMutationPayload } = startMutationCollection() + + sandbox.removeAttribute('foo') + mutationController.flush() + + const { validate, expectInitialNode } = createMutationPayloadValidator(serializedDocument) + validate(getLatestMutationPayload(), { + attributes: [ + { + node: expectInitialNode({ idAttribute: 'sandbox' }), + attributes: { foo: null }, + }, + ], + }) + }) + it('does not emit a mutation when an attribute keeps the same value', () => { sandbox.setAttribute('foo', 'bar') serializeDocument(document, NodePrivacyLevel.ALLOW) diff --git a/packages/rum/src/domain/record/mutationObserver.ts b/packages/rum/src/domain/record/mutationObserver.ts index 1de475235f..dbda47eff0 100644 --- a/packages/rum/src/domain/record/mutationObserver.ts +++ b/packages/rum/src/domain/record/mutationObserver.ts @@ -311,7 +311,7 @@ function processAttributesMutations( continue } transformedValue = inputValue - } else if (attributeValue && typeof attributeValue === 'string') { + } else if (typeof attributeValue === 'string') { transformedValue = attributeValue } else { transformedValue = null