Skip to content

Commit 04a746f

Browse files
committed
improvement: _experimental_allSpansExtendSession tests
1 parent 8618d50 commit 04a746f

File tree

12 files changed

+186
-57
lines changed

12 files changed

+186
-57
lines changed

.size-limit.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module.exports = [
2424

2525
{
2626
name: 'artifacts/splunk-otel-web.js',
27-
limit: '42 kB',
27+
limit: '43 kB',
2828
path: './packages/web/dist/artifacts/splunk-otel-web.js',
2929
},
3030

packages/integration-tests/src/pages/record-page.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ export class RecordPage {
4545

4646
async flushData() {
4747
await this.page.evaluate(() => {
48-
if (window.SplunkRum) {
49-
window.SplunkRum._processor.forceFlush()
48+
if ((window as any).SplunkRum) {
49+
;(window as any).SplunkRum._processor.forceFlush()
5050
}
5151
})
5252
}

packages/integration-tests/src/server/render-agent.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ export const RENDER_AGENT_TEMPLATE = `
2020
<script src="<%= otelApiGlobalsFile -%>" crossorigin="anonymous"></script>
2121
<script>
2222
const options = <%- options -%>;
23+
const customOptions = <%- customOptions -%>;
2324
24-
const samplingRatioRaw = (new URL(document.location)).searchParams.get('samplingRatio');
25-
console.log('samplingRatioRaw', samplingRatioRaw);
26-
if (samplingRatioRaw != null) {
27-
console.log('setting sampling ration:', samplingRatioRaw);
25+
if (typeof customOptions.forceSessionId === 'string') {
26+
window.__splunkRumIntegrationTestSessionId = customOptions.forceSessionId;
27+
}
28+
29+
if (typeof customOptions.samplingRatio === 'number') {
2830
options['tracer'] = {
2931
sampler: new SplunkRum.SessionBasedSampler({
30-
ratio: parseFloat(samplingRatioRaw),
32+
ratio: customOptions.samplingRatio,
3133
})
3234
}
3335
}

packages/integration-tests/src/server/server.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,11 @@ fastify.get('/', (request, reply) => {
100100

101101
fastify.get<{
102102
Querystring: {
103+
_experimental_longtaskNoStartSession?: string
103104
beaconEndpoint?: string
104105
disableInstrumentation?: string
106+
forceSessionId?: string
107+
samplingRatio?: string
105108
}
106109
}>('*', async (request, reply) => {
107110
const beaconUrl = new URL(`http://${request.headers.host}/api/v2/spans`)
@@ -110,23 +113,29 @@ fastify.get<{
110113
const parsedUrl = new URL(request.url, `http://${request.host}`)
111114
const filePath = path.join(__dirname, '../tests', parsedUrl.pathname)
112115

113-
const _experimental_longtaskNoStartSession = JSON.parse(
114-
request.query['_experimental_longtaskNoStartSession'] ?? 'null',
115-
)
116-
117116
if (fs.existsSync(filePath)) {
118117
if (parsedUrl.pathname.endsWith('.ejs')) {
119118
return reply.viewAsync(parsedUrl.pathname, {
120119
renderAgent(userOpts = {}, noInit = false, file = defaultFile, cdnVersion = null) {
121120
const options: Record<string, unknown> = {
122-
_experimental_longtaskNoStartSession,
121+
_experimental_longtaskNoStartSession:
122+
request.query._experimental_longtaskNoStartSession === 'true' ? true : false,
123123
beaconEndpoint: beaconUrl.toString(),
124124
applicationName: 'splunk-otel-js-dummy-app',
125125
debug: true,
126126
bufferTimeout: GLOBAL_TEST_BUFFER_TIMEOUT,
127127
...userOpts,
128128
}
129129

130+
const customOptions: Record<string, unknown> = {}
131+
if (typeof request.query.samplingRatio === 'string') {
132+
customOptions['samplingRatio'] = parseFloat(request.query.samplingRatio)
133+
}
134+
135+
if (typeof request.query.forceSessionId === 'string') {
136+
customOptions['forceSessionId'] = request.query.forceSessionId
137+
}
138+
130139
if (typeof request.query.disableInstrumentation === 'string') {
131140
if (!options.instrumentations) {
132141
options.instrumentations = {}
@@ -149,6 +158,7 @@ fastify.get<{
149158
file,
150159
noInit,
151160
options: JSON.stringify(options),
161+
customOptions: JSON.stringify(customOptions),
152162
otelApiGlobalsFile: '/artifacts/otel-api-globals.js',
153163
})
154164
},

packages/integration-tests/src/tests/errors/errors.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ test.describe('errors', () => {
131131
expect(errorSpans[0].tags['error.message']).toBe(errorMessages[browserName])
132132

133133
const errorStackMap = {
134-
chromium: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:76:25`,
135-
firefox: `@${url}:76:7\n`,
136-
webkit: `global code@${url}:76:15`,
134+
chromium: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:78:25`,
135+
firefox: `@${url}:78:7\n`,
136+
webkit: `global code@${url}:78:15`,
137137
}
138138

139139
expect(errorSpans[0].tags['error.stack']).toBe(errorStackMap[browserName])

packages/integration-tests/src/tests/long-task/index.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
};
2020
2121
document.querySelector('#btnLongtask').addEventListener('click', () => {
22-
window.testing = true;
22+
window.testing = true;
2323
generateLongTask();
2424
window.testing = false;
2525
});

packages/integration-tests/src/tests/long-task/long-task.spec.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,111 @@ test.describe('long task', () => {
8787

8888
expect(longTaskSpans).toHaveLength(0)
8989
})
90+
91+
test('longtask will spawn new session', async ({ recordPage, browserName }) => {
92+
if (browserName === 'webkit' || browserName === 'firefox') {
93+
test.skip()
94+
}
95+
96+
await recordPage.goTo(
97+
'/long-task/index.ejs?_experimental_longtaskNoStartSession=false&disableInstrumentation=connectivity,document,errors,fetch,interactions,postload,socketio,visibility,websocket,webvitals,xhr',
98+
)
99+
100+
await recordPage.locator('#btnLongtask').click()
101+
102+
const sessionCookie1 = await recordPage.getCookie('_splunk_rum_sid')
103+
expect(sessionCookie1).toBeTruthy()
104+
105+
const sessionCookie1Parsed = JSON.parse(decodeURIComponent(sessionCookie1.value))
106+
107+
await recordPage.waitForTimeoutAndFlushData(1000)
108+
109+
const allSpans1 = recordPage.receivedSpans
110+
111+
expect(allSpans1).toHaveLength(1)
112+
113+
// Set session as expired using expiresAt
114+
await recordPage.evaluate(
115+
([expiresAt, id, startTime]) => {
116+
globalThis[Symbol.for('opentelemetry.js.api.1')]['splunk.rum']['store'].set({
117+
expiresAt,
118+
id,
119+
startTime,
120+
})
121+
},
122+
[Date.now(), sessionCookie1Parsed.id, sessionCookie1Parsed.startTime],
123+
)
124+
125+
await recordPage.locator('#btnLongtask').click()
126+
127+
await recordPage.waitForTimeoutAndFlushData(1000)
128+
129+
const sessionCookie3 = await recordPage.getCookie('_splunk_rum_sid')
130+
expect(sessionCookie3).toBeTruthy()
131+
132+
const allSpans2 = recordPage.receivedSpans
133+
expect(allSpans2).toHaveLength(2)
134+
135+
const sessionCookie3Parsed = JSON.parse(decodeURIComponent(sessionCookie3.value))
136+
137+
expect(sessionCookie1Parsed.id).not.toBe(sessionCookie3Parsed.id)
138+
})
139+
140+
test('longtask will not spawn new session', async ({ recordPage, browserName }) => {
141+
if (browserName === 'webkit' || browserName === 'firefox') {
142+
test.skip()
143+
}
144+
145+
await recordPage.goTo(
146+
'/long-task/index.ejs?_experimental_longtaskNoStartSession=true&disableInstrumentation=connectivity,document,errors,fetch,interactions,postload,socketio,visibility,websocket,webvitals,xhr',
147+
)
148+
149+
await recordPage.locator('#btnLongtask').click()
150+
151+
const sessionCookie1 = await recordPage.getCookie('_splunk_rum_sid')
152+
153+
expect(sessionCookie1).toBeTruthy()
154+
155+
const sessionCookie1Parsed = JSON.parse(decodeURIComponent(sessionCookie1.value))
156+
157+
await recordPage.waitForTimeoutAndFlushData(1000)
158+
159+
const allSpans1 = recordPage.receivedSpans
160+
161+
expect(allSpans1).toHaveLength(1)
162+
163+
const onlyLongTask = allSpans1[0]
164+
165+
// Set session as expired using expiresAt
166+
await recordPage.evaluate(
167+
([expiresAt, id, startTime]) => {
168+
globalThis[Symbol.for('opentelemetry.js.api.1')]['splunk.rum']['store'].set({
169+
expiresAt,
170+
id,
171+
startTime,
172+
})
173+
},
174+
[Date.now(), sessionCookie1Parsed.id, sessionCookie1Parsed.startTime],
175+
)
176+
177+
await recordPage.waitForTimeout(1000)
178+
179+
await recordPage.locator('#btnLongtask').click()
180+
181+
await recordPage.waitForTimeoutAndFlushData(1000)
182+
183+
const allSpans2 = recordPage.receivedSpans
184+
185+
expect(allSpans2).toHaveLength(1)
186+
187+
expect(allSpans2[0].id).toBe(onlyLongTask.id)
188+
189+
const sessionCookie2 = await recordPage.getCookie('_splunk_rum_sid')
190+
191+
expect(sessionCookie2).toBeTruthy()
192+
193+
const sessionCookie2Parsed = JSON.parse(decodeURIComponent(sessionCookie1.value))
194+
195+
expect(sessionCookie2Parsed.id).toBe(sessionCookie1Parsed.id)
196+
})
90197
})

packages/integration-tests/src/tests/sampling/index.ejs renamed to packages/integration-tests/src/tests/sampling/sampling.ejs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
<head>
44
<meta charset="UTF-8">
55
<title>Session sampling</title>
6-
<script>
7-
window.__integrationTestSessionId = new URL(document.location).searchParams.get('forceSessionId');
8-
</script>
96
<%- renderAgent() %>
107
</head>
118
<body>

packages/integration-tests/src/tests/sampling/sampling.spec.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,42 @@ import { test } from '../../utils/test'
2020

2121
test.describe('sampling', () => {
2222
test('all spans arrive if ratio is 1', async ({ recordPage }) => {
23-
await recordPage.goTo('/sampling/index.ejs?samplingRatio=1')
23+
await recordPage.goTo('/sampling/sampling.ejs?samplingRatio=1')
2424
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
2525
})
2626

2727
test('no spans arrive if ratio is 0', async ({ recordPage }) => {
28-
await recordPage.goTo('/sampling/index.ejs?samplingRatio=0')
28+
await recordPage.goTo('/sampling/sampling.ejs?samplingRatio=0')
2929
await recordPage.waitForTimeout(1000)
3030

3131
expect(recordPage.receivedSpans).toHaveLength(0)
3232
})
3333

3434
test('all spans arrive if session was sampled', async ({ recordPage }) => {
35-
await recordPage.goTo('/sampling/index.ejs?samplingRatio=0.99&forceSessionId=a0000000000000000000000000000000')
35+
await recordPage.goTo(
36+
'/sampling/sampling.ejs?samplingRatio=0.99&forceSessionId=a0000000000000000000000000000000',
37+
)
3638
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
37-
})
3839

39-
test('no spans arrive if session was not sampled', async ({ recordPage }) => {
40-
await recordPage.goTo('/sampling/index.ejs?samplingRatio=0.01&forceSessionId=a0000000000000000000000000000000')
41-
await recordPage.waitForTimeout(1000)
40+
const sessionCookieEncoded = await recordPage.getCookie('_splunk_rum_sid')
41+
expect(sessionCookieEncoded).toBeTruthy()
4242

43-
expect(recordPage.receivedSpans).toHaveLength(0)
43+
const sessionCookieRaw = decodeURIComponent(sessionCookieEncoded.value)
44+
expect(JSON.parse(sessionCookieRaw).id).toBe('a0000000000000000000000000000000')
4445
})
4546

46-
test('all spans arrive if session was sampled even if longtasks do not start a new session', async ({
47-
recordPage,
48-
}) => {
47+
test('no spans arrive if session was not sampled', async ({ recordPage }) => {
4948
await recordPage.goTo(
50-
'/sampling/index.ejs?samplingRatio=0.99&forceSessionId=a0000000000000000000000000000000&_experimental_longtaskNoStartSession=true',
49+
'/sampling/sampling.ejs?samplingRatio=0.01&forceSessionId=a0000000000000000000000000000000',
5150
)
51+
await recordPage.waitForTimeout(1000)
5252

53-
// spans have arrived
54-
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
53+
expect(recordPage.receivedSpans).toHaveLength(0)
5554

56-
// we have a cookie
5755
const sessionCookieEncoded = await recordPage.getCookie('_splunk_rum_sid')
5856
expect(sessionCookieEncoded).toBeTruthy()
5957

60-
// and the session is active
6158
const sessionCookieRaw = decodeURIComponent(sessionCookieEncoded.value)
62-
expect(JSON.parse(sessionCookieRaw).inactive).toBeFalsy()
59+
expect(JSON.parse(sessionCookieRaw).id).toBe('a0000000000000000000000000000000')
6360
})
6461
})

packages/web/src/global-utils.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,51 +21,61 @@ import { VERSION } from './version'
2121

2222
const GLOBAL_OPENTELEMETRY_API_KEY = Symbol.for('opentelemetry.js.api.1')
2323

24+
const GLOBAL_SPLUNK_RUM_KEY = 'splunk.rum'
25+
26+
const GLOBAL_SPLUNK_RUM_VERSION_KEY = `${GLOBAL_SPLUNK_RUM_KEY}.version`
2427
/**
2528
* otel-api's global function. This isn't exported by otel/api but would be
2629
* super useful to register global components for experimental purposes...
2730
* For us, it's included to register components accessed by other packages,
2831
* eg. sharing session id manager with session recorder
2932
*/
30-
export function registerGlobal(type: string, instance: unknown, allowOverride = false): boolean {
33+
export function registerGlobal(instance: unknown, allowOverride = false): boolean {
3134
if (!globalThis[GLOBAL_OPENTELEMETRY_API_KEY]) {
3235
diag.error('SplunkRum: Tried to access global before otel setup')
3336
return false
3437
}
3538

3639
const api = globalThis[GLOBAL_OPENTELEMETRY_API_KEY]
3740

38-
if (!api['splunk.rum.version']) {
39-
api['splunk.rum.version'] = VERSION
41+
if (!api[GLOBAL_SPLUNK_RUM_VERSION_KEY]) {
42+
api[GLOBAL_SPLUNK_RUM_VERSION_KEY] = VERSION
4043
}
4144

42-
if (api['splunk.rum.version'] !== VERSION) {
45+
if (api[GLOBAL_SPLUNK_RUM_VERSION_KEY] !== VERSION) {
4346
diag.error(`SplunkRum: Global: Multiple versions detected (${VERSION} already registered)`)
4447
return false
4548
}
4649

47-
if (!allowOverride && api[type]) {
48-
diag.error(`SplunkRum: Attempted duplicate registration of otel API ${type}`)
50+
if (!allowOverride && api[GLOBAL_SPLUNK_RUM_KEY]) {
51+
diag.error(`SplunkRum: Attempted duplicate registration of otel API ${GLOBAL_SPLUNK_RUM_KEY}`)
4952
return false
5053
}
5154

52-
api[type] = instance
55+
api[GLOBAL_SPLUNK_RUM_KEY] = instance
5356
return true
5457
}
5558

56-
export function unregisterGlobal(type: string): boolean {
59+
export function unregisterGlobal(): boolean {
5760
const api = globalThis[GLOBAL_OPENTELEMETRY_API_KEY]
5861
if (!api) {
59-
diag.warn(`OTel API ref was missing while trying to unregister ${type}`)
62+
diag.warn(
63+
`OTel API ref was missing while trying to unregister ${GLOBAL_SPLUNK_RUM_KEY} or ${GLOBAL_SPLUNK_RUM_VERSION_KEY}`,
64+
)
6065
return false
6166
}
6267

63-
if (!!api['splunk.rum.version'] && api['splunk.rum.version'] !== VERSION) {
68+
if (!!api[GLOBAL_SPLUNK_RUM_VERSION_KEY] && api[GLOBAL_SPLUNK_RUM_VERSION_KEY] !== VERSION) {
6469
diag.warn(
65-
`SplunkRum version in OTel API ref (${api['splunk.rum.version']}) didn't match our version (${VERSION}).`,
70+
`SplunkRum version in OTel API ref (${api[GLOBAL_SPLUNK_RUM_VERSION_KEY]}) didn't match our version (${VERSION}).`,
6671
)
6772
}
6873

69-
delete api[type]
74+
delete api[GLOBAL_SPLUNK_RUM_KEY]
75+
delete api[GLOBAL_SPLUNK_RUM_VERSION_KEY]
7076
return true
7177
}
78+
79+
export function getGlobal<Type>(): Type | undefined {
80+
return globalThis[GLOBAL_OPENTELEMETRY_API_KEY]?.[GLOBAL_SPLUNK_RUM_KEY]
81+
}

packages/web/src/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ export const SplunkRum: SplunkOtelWebType = {
291291
// touches otel globals, our registerGlobal requires this first
292292
diag.setLogger(new DiagConsoleLogger(), options?.debug ? DiagLogLevel.DEBUG : DiagLogLevel.WARN)
293293

294-
const registered = registerGlobal('splunk.rum', this)
294+
const registered = registerGlobal(this)
295295
if (!registered) {
296296
return
297297
}
@@ -499,8 +499,7 @@ export const SplunkRum: SplunkOtelWebType = {
499499
eventTarget = undefined
500500

501501
diag.disable()
502-
unregisterGlobal('splunk.rum')
503-
unregisterGlobal('splunk.rum.version')
502+
unregisterGlobal()
504503

505504
inited = false
506505
},

0 commit comments

Comments
 (0)