Skip to content

Commit 7186051

Browse files
authored
⚗️[MFE] Source code context event enrichment (#3926)
1 parent fd73d9e commit 7186051

24 files changed

+564
-76
lines changed

packages/core/src/tools/experimentalFeatures.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export enum ExperimentalFeature {
2020
SHORT_SESSION_INVESTIGATION = 'short_session_investigation',
2121
AVOID_FETCH_KEEPALIVE = 'avoid_fetch_keepalive',
2222
USE_CHANGE_RECORDS = 'use_change_records',
23+
SOURCE_CODE_CONTEXT = 'source_code_context',
2324
}
2425

2526
const enabledExperimentalFeatures: Set<ExperimentalFeature> = new Set()

packages/core/src/tools/stackTrace/capturedExceptions.specHelper.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,13 @@ export const CHROME_111_SNIPPET = {
247247
at snippet:///snippet_file:1:13`,
248248
}
249249

250+
export const CHROME_141_HTML_ANONYMOUS_LISTENER = {
251+
message: 'message string',
252+
name: 'Error',
253+
stack: `Error: message string
254+
at HTMLButtonElement.<anonymous> @ http://path/to/file.js:1:4287`,
255+
}
256+
250257
export const PHANTOMJS_1_19 = {
251258
stack: `Error: foo
252259
at file:///path/to/file.js:878

packages/core/src/tools/stackTrace/computeStackTrace.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { isSafari } from '../utils/browserDetection'
22
import * as CapturedExceptions from './capturedExceptions.specHelper'
3+
import { CHROME_141_HTML_ANONYMOUS_LISTENER } from './capturedExceptions.specHelper'
34
import { computeStackTrace } from './computeStackTrace'
45

56
describe('computeStackTrace', () => {
@@ -1004,4 +1005,17 @@ Error: foo
10041005
column: undefined,
10051006
})
10061007
})
1008+
1009+
it('should parse stack from html button click listener', () => {
1010+
const stackFrames = computeStackTrace(CHROME_141_HTML_ANONYMOUS_LISTENER)
1011+
1012+
expect(stackFrames.stack.length).toEqual(1)
1013+
expect(stackFrames.stack[0]).toEqual({
1014+
args: [],
1015+
column: 4287,
1016+
func: 'HTMLButtonElement.<anonymous>',
1017+
line: 1,
1018+
url: 'http://path/to/file.js',
1019+
})
1020+
})
10071021
})

packages/core/src/tools/stackTrace/computeStackTrace.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ function parseChromeLine(line: string): StackFrame | undefined {
113113
}
114114
}
115115

116-
const CHROME_ANONYMOUS_FUNCTION_RE = new RegExp(`^\\s*at ?${fileUrl}${filePosition}?${filePosition}??\\s*$`, 'i')
116+
const htmlAnonymousPart = '(?:(.*)?(?: @))'
117+
const CHROME_ANONYMOUS_FUNCTION_RE = new RegExp(
118+
`^\\s*at\\s*${htmlAnonymousPart}?\\s*${fileUrl}${filePosition}?${filePosition}??\\s*$`,
119+
'i'
120+
)
117121

118122
function parseChromeAnonymousLine(line: string): StackFrame | undefined {
119123
const parts = CHROME_ANONYMOUS_FUNCTION_RE.exec(line)
@@ -124,10 +128,10 @@ function parseChromeAnonymousLine(line: string): StackFrame | undefined {
124128

125129
return {
126130
args: [],
127-
column: parts[3] ? +parts[3] : undefined,
128-
func: UNKNOWN_FUNCTION,
129-
line: parts[2] ? +parts[2] : undefined,
130-
url: parts[1],
131+
column: parts[4] ? +parts[4] : undefined,
132+
func: parts[1] || UNKNOWN_FUNCTION,
133+
line: parts[3] ? +parts[3] : undefined,
134+
url: parts[2],
131135
}
132136
}
133137

packages/rum-core/src/boot/startRum.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import type { Hooks } from '../domain/hooks'
5555
import { createHooks } from '../domain/hooks'
5656
import { startEventCollection } from '../domain/event/eventCollection'
5757
import { startInitialViewMetricsTelemetry } from '../domain/view/viewMetrics/startInitialViewMetricsTelemetry'
58+
import { startSourceCodeContext } from '../domain/contexts/sourceCodeContext'
5859
import type { RecorderApi, ProfilerApi } from './rumPublicApi'
5960

6061
export type StartRum = typeof startRum
@@ -230,6 +231,9 @@ export function startRumEventCollection(
230231
viewHistory,
231232
initialViewOptions
232233
)
234+
235+
startSourceCodeContext(hooks)
236+
233237
cleanupTasks.push(stopViewCollection)
234238

235239
const { stop: stopResourceCollection } = startResourceCollection(lifeCycle, configuration, pageStateHistory)

packages/rum-core/src/domain/action/actionCollection.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { RawRumActionEvent, RawRumEvent } from '../../rawRumEvent.types'
66
import { RumEventType, ActionType } from '../../rawRumEvent.types'
77
import type { RawRumEventCollectedData } from '../lifeCycle'
88
import { LifeCycle, LifeCycleEventType } from '../lifeCycle'
9-
import type { DefaultTelemetryEventAttributes, Hooks } from '../hooks'
9+
import type { AssembleHookParams, DefaultTelemetryEventAttributes, Hooks } from '../hooks'
1010
import { createHooks } from '../hooks'
1111
import type { RumMutationRecord } from '../../browser/domMutationObservable'
1212
import type { ActionContexts } from './actionCollection'
@@ -174,7 +174,7 @@ describe('actionCollection', () => {
174174
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
175175
eventType,
176176
startTime: 0 as RelativeTime,
177-
})
177+
} as AssembleHookParams)
178178

179179
expect(defaultRumEventAttributes).toEqual({ type: eventType, action: { id: actionId } })
180180
})
@@ -186,7 +186,7 @@ describe('actionCollection', () => {
186186
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
187187
eventType,
188188
startTime: 0 as RelativeTime,
189-
})
189+
} as AssembleHookParams)
190190

191191
expect(defaultRumEventAttributes).toEqual(undefined)
192192
})
@@ -200,7 +200,7 @@ describe('actionCollection', () => {
200200
eventType: RumEventType.LONG_TASK,
201201
startTime: longTaskStartTime,
202202
duration: 50 as Duration,
203-
})
203+
} as AssembleHookParams)
204204

205205
const [correctedStartTime] = findActionIdSpy.calls.mostRecent().args
206206
expect(correctedStartTime).toEqual(addDuration(longTaskStartTime, LONG_TASK_START_TIME_CORRECTION))

packages/rum-core/src/domain/assembly.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { LifeCycleEventType } from './lifeCycle'
1616
import type { RumConfiguration } from './configuration'
1717
import type { ModifiableFieldPaths } from './limitModification'
1818
import { limitModification } from './limitModification'
19-
import type { Hooks } from './hooks'
19+
import type { Hooks, AssembleHookParams } from './hooks'
2020

2121
const VIEW_MODIFIABLE_FIELD_PATHS: ModifiableFieldPaths = {
2222
'view.name': 'string',
@@ -95,9 +95,11 @@ export function startRumAssembly(
9595
({ startTime, duration, rawRumEvent, domainContext }) => {
9696
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
9797
eventType: rawRumEvent.type,
98+
rawRumEvent,
99+
domainContext,
98100
startTime,
99101
duration,
100-
})!
102+
} as AssembleHookParams)!
101103

102104
if (defaultRumEventAttributes === DISCARDED) {
103105
return

packages/rum-core/src/domain/contexts/ciVisibilityContext.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { HookNames, Observable } from '@datadog/browser-core'
33
import { mockCiVisibilityValues } from '../../../test'
44
import type { CookieObservable } from '../../browser/cookieObservable'
55
import { SessionType } from '../rumSessionManager'
6-
import type { Hooks } from '../hooks'
6+
import type { AssembleHookParams, Hooks } from '../hooks'
77
import { createHooks } from '../hooks'
88
import { startCiVisibilityContext } from './ciVisibilityContext'
99

@@ -29,7 +29,7 @@ describe('startCiVisibilityContext', () => {
2929
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
3030
eventType: 'view',
3131
startTime: 0 as RelativeTime,
32-
})
32+
} as AssembleHookParams)
3333

3434
expect(defaultRumEventAttributes).toEqual({
3535
type: 'view',
@@ -49,7 +49,7 @@ describe('startCiVisibilityContext', () => {
4949
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
5050
eventType: 'view',
5151
startTime: 0 as RelativeTime,
52-
})
52+
} as AssembleHookParams)
5353

5454
expect(defaultRumEventAttributes).toEqual({
5555
type: 'view',
@@ -70,7 +70,7 @@ describe('startCiVisibilityContext', () => {
7070
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
7171
eventType: 'view',
7272
startTime: 0 as RelativeTime,
73-
})
73+
} as AssembleHookParams)
7474

7575
expect(defaultRumEventAttributes).toEqual({
7676
type: 'view',
@@ -90,7 +90,7 @@ describe('startCiVisibilityContext', () => {
9090
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
9191
eventType: 'view',
9292
startTime: 0 as RelativeTime,
93-
})
93+
} as AssembleHookParams)
9494

9595
expect(defaultRumEventAttributes).toBeUndefined()
9696
})
@@ -102,7 +102,7 @@ describe('startCiVisibilityContext', () => {
102102
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
103103
eventType: 'view',
104104
startTime: 0 as RelativeTime,
105-
})
105+
} as AssembleHookParams)
106106

107107
expect(defaultRumEventAttributes).toBeUndefined()
108108
})

packages/rum-core/src/domain/contexts/connectivityContext.spec.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { setNavigatorOnLine, setNavigatorConnection } from '@datadog/browser-core/test'
22
import { HookNames } from '@datadog/browser-core'
33
import type { RelativeTime } from '@datadog/browser-core'
4-
import type { Hooks } from '../hooks'
4+
import type { AssembleHookParams, Hooks } from '../hooks'
55
import { createHooks } from '../hooks'
66
import { startConnectivityContext } from './connectivityContext'
77

@@ -17,7 +17,10 @@ describe('startConnectivityContext', () => {
1717
startConnectivityContext(hooks)
1818
setNavigatorOnLine(true)
1919
setNavigatorConnection({ effectiveType: '2g' })
20-
const event = hooks.triggerHook(HookNames.Assemble, { eventType: 'view', startTime: 0 as RelativeTime })
20+
const event = hooks.triggerHook(HookNames.Assemble, {
21+
eventType: 'view',
22+
startTime: 0 as RelativeTime,
23+
} as AssembleHookParams)
2124

2225
expect(event).toEqual({
2326
type: 'view',

packages/rum-core/src/domain/contexts/defaultContext.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { mockClock, mockEventBridge } from '@datadog/browser-core/test'
22
import { HookNames, timeStampNow } from '@datadog/browser-core'
33
import type { RelativeTime } from '@datadog/browser-core'
44
import { mockRumConfiguration } from '../../../test'
5-
import type { DefaultRumEventAttributes, DefaultTelemetryEventAttributes, Hooks } from '../hooks'
5+
import type { AssembleHookParams, DefaultRumEventAttributes, DefaultTelemetryEventAttributes, Hooks } from '../hooks'
66
import { createHooks } from '../hooks'
77
import { startDefaultContext } from './defaultContext'
88

@@ -20,7 +20,7 @@ describe('startDefaultContext', () => {
2020
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
2121
eventType: 'view',
2222
startTime: 0 as RelativeTime,
23-
})
23+
} as AssembleHookParams)
2424

2525
expect(defaultRumEventAttributes).toEqual({
2626
type: 'view',
@@ -41,14 +41,14 @@ describe('startDefaultContext', () => {
4141
const eventWithoutEventBridge = hooks.triggerHook(HookNames.Assemble, {
4242
eventType: 'view',
4343
startTime: 0 as RelativeTime,
44-
}) as DefaultRumEventAttributes
44+
} as AssembleHookParams) as DefaultRumEventAttributes
4545

4646
mockEventBridge()
4747

4848
const eventWithEventBridge = hooks.triggerHook(HookNames.Assemble, {
4949
eventType: 'view',
5050
startTime: 0 as RelativeTime,
51-
}) as DefaultRumEventAttributes
51+
} as AssembleHookParams) as DefaultRumEventAttributes
5252

5353
expect(eventWithEventBridge._dd!.browser_sdk_version).toBeDefined()
5454
expect(eventWithoutEventBridge._dd!.browser_sdk_version).toBeUndefined()
@@ -64,7 +64,7 @@ describe('startDefaultContext', () => {
6464
const event = hooks.triggerHook(HookNames.Assemble, {
6565
eventType: 'view',
6666
startTime: 0 as RelativeTime,
67-
}) as DefaultRumEventAttributes
67+
} as AssembleHookParams) as DefaultRumEventAttributes
6868

6969
expect(event._dd!.configuration!.session_sample_rate).toBe(10)
7070
expect(event._dd!.configuration!.session_replay_sample_rate).toBe(20)

0 commit comments

Comments
 (0)