Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Capture Internal Metrics for Session Replay Configurations #879

Merged
merged 1 commit into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions src/features/session_replay/aggregate/index.component-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe('Session Replay', () => {
global.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass)

setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.initialized).toBeTruthy()
Expand All @@ -100,6 +101,7 @@ describe('Session Replay', () => {

test('When Session Is Paused/Resumed', async () => {
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.initialized).toBeTruthy()
Expand All @@ -112,20 +114,23 @@ describe('Session Replay', () => {

test('Session SR mode matches SR mode -- FULL', async () => {
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(session.state.sessionReplayMode).toEqual(sr.mode)
})

test('Session SR mode matches SR mode -- ERROR', async () => {
setConfiguration(agentIdentifier, { session_replay: { sampling_rate: 0, error_sampling_rate: 100 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(session.state.sessionReplayMode).toEqual(sr.mode)
})

test('Session SR mode matches SR mode -- OFF', async () => {
setConfiguration(agentIdentifier, { session_replay: { sampling_rate: 0, error_sampling_rate: 0 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(session.state.sessionReplayMode).toEqual(sr.mode)
Expand All @@ -135,6 +140,7 @@ describe('Session Replay', () => {
describe('Session Replay Initialization Behavior', () => {
test('Waits for SR', async () => {
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
// do not emit sr flag
await wait(1000)
expect(sr.initialized).toEqual(false)
Expand Down Expand Up @@ -169,27 +175,31 @@ describe('Session Replay', () => {
describe('Session Replay Sample -> Mode Behaviors', () => {
test('New Session -- Full 1 Error 1 === FULL', async () => {
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 100 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.mode).toEqual(MODE.FULL)
})

test('New Session -- Full 1 Error 0 === FULL', async () => {
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 0, sampling_rate: 100 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.mode).toEqual(MODE.FULL)
})

test('New Session -- Full 0 Error 1 === ERROR', async () => {
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.mode).toEqual(MODE.ERROR)
})

test('New Session -- Full 0 Error 0 === OFF', async () => {
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 0, sampling_rate: 0 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.mode).toEqual(MODE.OFF)
Expand All @@ -202,6 +212,7 @@ describe('Session Replay', () => {
primeSessionAndReplay(session)
// configure to get "error" sample ---> but should inherit FULL from session manager
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.mode).toEqual(MODE.FULL)
Expand All @@ -211,6 +222,7 @@ describe('Session Replay', () => {
describe('Session Replay Error Mode Behaviors', () => {
test('An error BEFORE rrweb import starts running in FULL from beginning', async () => {
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('errorAgg')
sr.ee.emit('rumresp-sr', [true])
await wait(1)
Expand All @@ -220,6 +232,7 @@ describe('Session Replay', () => {

test('An error AFTER rrweb import changes mode and starts harvester', async () => {
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.mode).toEqual(MODE.ERROR)
Expand All @@ -236,6 +249,7 @@ describe('Session Replay', () => {
session = new SessionEntity({ agentIdentifier, key: 'SESSION', storage })
primeSessionAndReplay(session)
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
const harvestContents = sr.getHarvestContents()
Expand All @@ -256,6 +270,7 @@ describe('Session Replay', () => {
test('Compressed payload is provided to harvester', async () => {
const { gunzipSync, strFromU8 } = await import('fflate')
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
const [harvestContents] = sr.prepareHarvest()
Expand All @@ -273,6 +288,7 @@ describe('Session Replay', () => {
}))

setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)

Expand All @@ -290,6 +306,7 @@ describe('Session Replay', () => {

test('Clears the event buffer when staged for harvesting', async () => {
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)

Expand All @@ -300,11 +317,12 @@ describe('Session Replay', () => {

test('Harvests early if exceeds limit', async () => {
let after = 0
const spy = jest.spyOn(sr.scheduler, 'runHarvest').mockImplementation(() => { after = Date.now() })
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.recorder = new Recorder(sr)
sr.recorder.currentBufferTarget.payloadBytesEstimation = IDEAL_PAYLOAD_SIZE / AVG_COMPRESSION
const before = Date.now()
const spy = jest.spyOn(sr.scheduler, 'runHarvest').mockImplementation(() => { after = Date.now() })
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(spy).toHaveBeenCalled()
Expand All @@ -318,18 +336,20 @@ describe('Session Replay', () => {
}))
const spy = jest.spyOn(sr.scheduler.harvest, '_send')
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.recorder = new Recorder(sr)
Array.from({ length: 100000 }).forEach(() => sr.recorder.currentBufferTarget.add({ test: 1 })) // fill the events array with tons of events
sr.recorder.currentBufferTarget.payloadBytesEstimation = sr.recorder.currentBufferTarget.events.join('').length
sr.ee.emit('rumresp-sr', [true])
await wait(100)
await wait(1)
expect(spy).not.toHaveBeenCalled()
expect(sr.blocked).toEqual(true)
expect(sr.mode).toEqual(MODE.OFF)
})

test('Aborts if 429 response', async () => {
setConfiguration(agentIdentifier, { ...init })
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
sr.ee.emit('rumresp-sr', [true])
await wait(1)
expect(sr.mode).toEqual(MODE.FULL)
Expand All @@ -349,5 +369,4 @@ function wait (ms = 0) {
function primeSessionAndReplay (sess = new SessionEntity({ agentIdentifier, key: 'SESSION', storage: new LocalMemory() })) {
session = sess
configure({ agentIdentifier }, { info, runtime: { session }, init: {} }, 'test', true)
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
}
16 changes: 13 additions & 3 deletions src/features/session_replay/aggregate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
this.recorder = args?.recorder
if (this.recorder) this.recorder.parent = this

handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, FEATURE_NAMES.metrics, this.ee)

const shouldSetup = (
getConfigurationValue(agentIdentifier, 'privacy.cookies_enabled') === true &&
getConfigurationValue(agentIdentifier, 'session_trace.enabled') === true
Expand Down Expand Up @@ -122,15 +124,23 @@
}
}, this.featureName, this.ee)

const errorSamplingRate = getConfigurationValue(this.agentIdentifier, 'session_replay.error_sampling_rate')
const samplingRate = getConfigurationValue(this.agentIdentifier, 'session_replay.sampling_rate')

this.waitForFlags(['sr']).then(([flagOn]) => {
this.entitled = flagOn
if (!this.entitled && this.recorder?.recording) this.recorder.abort(ABORT_REASONS.ENTITLEMENTS)
if (!this.entitled && this.recorder?.recording) {
this.recorder.abort(ABORT_REASONS.ENTITLEMENTS)
handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, FEATURE_NAMES.metrics, this.ee)

Check warning on line 134 in src/features/session_replay/aggregate/index.js

View check run for this annotation

Codecov / codecov/patch

src/features/session_replay/aggregate/index.js#L133-L134

Added lines #L133 - L134 were not covered by tests
}
this.initializeRecording(
(Math.random() * 100) < getConfigurationValue(this.agentIdentifier, 'session_replay.error_sampling_rate'),
(Math.random() * 100) < getConfigurationValue(this.agentIdentifier, 'session_replay.sampling_rate')
(Math.random() * 100) < errorSamplingRate,
(Math.random() * 100) < samplingRate
)
}).then(() => sharedChannel.onReplayReady(this.mode)) // notify watchers that replay started with the mode

handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', samplingRate], undefined, FEATURE_NAMES.metrics, this.ee)
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', errorSamplingRate], undefined, FEATURE_NAMES.metrics, this.ee)
this.drain()
}
}
Expand Down
Loading