Skip to content

Commit

Permalink
💥 [RUMF-1589] automatically start recording (#2275)
Browse files Browse the repository at this point in the history
* ♻️ buffer start/stop recorder API calls before init

* ✨add option to opt out of automatic recording

* 💥automatically start recording by default

* ♻️ cleanup scenarios using replay

* 👌fix test description
  • Loading branch information
bcaudan authored Jun 5, 2023
1 parent de514ba commit f875c2b
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 117 deletions.
35 changes: 32 additions & 3 deletions packages/rum-core/src/boot/rumPublicApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,23 +829,25 @@ describe('rum public api', () => {
let recorderApiOnRumStartSpy: jasmine.Spy<RecorderApi['onRumStart']>
let setupBuilder: TestSetupBuilder
let rumPublicApi: RumPublicApi
let recorderApi: RecorderApi

beforeEach(() => {
recorderApiOnRumStartSpy = jasmine.createSpy('recorderApiOnRumStart')
rumPublicApi = makeRumPublicApi(noopStartRum, { ...noopRecorderApi, onRumStart: recorderApiOnRumStartSpy })
recorderApi = { ...noopRecorderApi, onRumStart: recorderApiOnRumStartSpy }
rumPublicApi = makeRumPublicApi(noopStartRum, recorderApi)
setupBuilder = setup()
})

afterEach(() => {
setupBuilder.cleanup()
})

it('recording is started with the default defaultPrivacyLevel', () => {
it('is started with the default defaultPrivacyLevel', () => {
rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)
expect(recorderApiOnRumStartSpy.calls.mostRecent().args[1].defaultPrivacyLevel).toBe(DefaultPrivacyLevel.MASK)
})

it('recording is started with the configured defaultPrivacyLevel', () => {
it('is started with the configured defaultPrivacyLevel', () => {
rumPublicApi.init({
...DEFAULT_INIT_CONFIGURATION,
defaultPrivacyLevel: DefaultPrivacyLevel.MASK_USER_INPUT,
Expand All @@ -854,6 +856,33 @@ describe('rum public api', () => {
DefaultPrivacyLevel.MASK_USER_INPUT
)
})

it('api calls before init are performed after onRumStart', () => {
// in order to let recording initial state to be defined by init configuration
const callOrders: string[] = []
spyOn(recorderApi, 'start').and.callFake(() => callOrders.push('start'))
spyOn(recorderApi, 'stop').and.callFake(() => callOrders.push('stop'))
recorderApiOnRumStartSpy.and.callFake(() => callOrders.push('onRumStart'))

rumPublicApi.startSessionReplayRecording()
rumPublicApi.stopSessionReplayRecording()
rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)

expect(callOrders).toEqual(['onRumStart', 'start', 'stop'])
})

it('is started with the default startSessionReplayRecordingManually', () => {
rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)
expect(recorderApiOnRumStartSpy.calls.mostRecent().args[1].startSessionReplayRecordingManually).toBe(false)
})

it('is started with the configured startSessionReplayRecordingManually', () => {
rumPublicApi.init({
...DEFAULT_INIT_CONFIGURATION,
startSessionReplayRecordingManually: true,
})
expect(recorderApiOnRumStartSpy.calls.mostRecent().args[1].startSessionReplayRecordingManually).toBe(true)
})
})

it('should provide sdk version', () => {
Expand Down
16 changes: 13 additions & 3 deletions packages/rum-core/src/boot/rumPublicApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ export function makeRumPublicApi(
bufferApiCalls.add(() => addErrorStrategy(providedError, commonContext))
}

let recorderStartStrategy: typeof recorderApi.start = () => {
bufferApiCalls.add(() => recorderStartStrategy())
}

let recorderStopStrategy: typeof recorderApi.stop = () => {
bufferApiCalls.add(() => recorderStopStrategy())
}

let addFeatureFlagEvaluationStrategy: StartRumResult['addFeatureFlagEvaluation'] = (key: string, value: any) => {
bufferApiCalls.add(() => addFeatureFlagEvaluationStrategy(key, value))
}
Expand Down Expand Up @@ -158,6 +166,8 @@ export function makeRumPublicApi(
)
getSessionReplayLinkStrategy = () =>
recorderApi.getSessionReplayLink(configuration, startRumResults.session, startRumResults.viewContexts)
recorderStartStrategy = recorderApi.start
recorderStopStrategy = recorderApi.stop
;({
startView: startViewStrategy,
addAction: addActionStrategy,
Expand All @@ -167,14 +177,14 @@ export function makeRumPublicApi(
getInternalContext: getInternalContextStrategy,
stopSession: stopSessionStrategy,
} = startRumResults)
bufferApiCalls.drain()

recorderApi.onRumStart(
startRumResults.lifeCycle,
configuration,
startRumResults.session,
startRumResults.viewContexts
)
bufferApiCalls.drain()
}

const startView: {
Expand Down Expand Up @@ -249,8 +259,8 @@ export function makeRumPublicApi(
stopSessionStrategy()
}),

startSessionReplayRecording: monitor(recorderApi.start),
stopSessionReplayRecording: monitor(recorderApi.stop),
startSessionReplayRecording: monitor(() => recorderStartStrategy()),
stopSessionReplayRecording: monitor(() => recorderStopStrategy()),

/**
* This feature is currently in beta. For more information see the full [feature flag tracking guide](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/).
Expand Down
28 changes: 28 additions & 0 deletions packages/rum-core/src/domain/configuration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,34 @@ describe('validateAndBuildRumConfiguration', () => {
})
})

describe('startSessionReplayRecordingManually', () => {
it('defaults to false', () => {
expect(
validateAndBuildRumConfiguration(DEFAULT_INIT_CONFIGURATION)!.startSessionReplayRecordingManually
).toBeFalse()
})

it('is set to provided value', () => {
expect(
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, startSessionReplayRecordingManually: true })!
.startSessionReplayRecordingManually
).toBeTrue()
expect(
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, startSessionReplayRecordingManually: false })!
.startSessionReplayRecordingManually
).toBeFalse()
})

it('the provided value is cast to boolean', () => {
expect(
validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
startSessionReplayRecordingManually: 'foo' as any,
})!.startSessionReplayRecordingManually
).toBeTrue()
})
})

describe('actionNameAttribute', () => {
it('defaults to undefined', () => {
expect(validateAndBuildRumConfiguration(DEFAULT_INIT_CONFIGURATION)!.actionNameAttribute).toBeUndefined()
Expand Down
4 changes: 4 additions & 0 deletions packages/rum-core/src/domain/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface RumInitConfiguration extends InitConfiguration {
defaultPrivacyLevel?: DefaultPrivacyLevel | undefined
subdomain?: string
sessionReplaySampleRate?: number | undefined
startSessionReplayRecordingManually?: boolean | undefined

// action options
trackUserInteractions?: boolean | undefined
Expand All @@ -53,6 +54,7 @@ export interface RumConfiguration extends Configuration {
applicationId: string
defaultPrivacyLevel: DefaultPrivacyLevel
sessionReplaySampleRate: number
startSessionReplayRecordingManually: boolean
trackUserInteractions: boolean
trackViewsManually: boolean
trackResources: boolean
Expand Down Expand Up @@ -104,6 +106,7 @@ export function validateAndBuildRumConfiguration(
version: initConfiguration.version,
actionNameAttribute: initConfiguration.actionNameAttribute,
sessionReplaySampleRate: initConfiguration.sessionReplaySampleRate ?? 0,
startSessionReplayRecordingManually: !!initConfiguration.startSessionReplayRecordingManually,
traceSampleRate: initConfiguration.traceSampleRate,
allowedTracingUrls,
excludedActivityUrls: initConfiguration.excludedActivityUrls ?? [],
Expand Down Expand Up @@ -181,6 +184,7 @@ export function serializeRumConfiguration(configuration: RumInitConfiguration):
return assign(
{
session_replay_sample_rate: configuration.sessionReplaySampleRate,
start_session_replay_recording_manually: configuration.startSessionReplayRecordingManually,
trace_sample_rate: configuration.traceSampleRate,
action_name_attribute: configuration.actionNameAttribute,
use_allowed_tracing_urls:
Expand Down
Loading

0 comments on commit f875c2b

Please sign in to comment.