Skip to content

Commit c3337fb

Browse files
🔊 add telemetry for unexpected session id changes
1 parent e1dfb7e commit c3337fb

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

packages/core/src/domain/session/sessionManager.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Observable } from '../../tools/observable'
22
import type { Context } from '../../tools/serialisation/context'
33
import { createValueHistory } from '../../tools/valueHistory'
44
import type { RelativeTime } from '../../tools/utils/timeUtils'
5-
import { clocksOrigin, ONE_MINUTE, relativeNow } from '../../tools/utils/timeUtils'
5+
import { clocksOrigin, dateNow, ONE_MINUTE, relativeNow } from '../../tools/utils/timeUtils'
66
import { addEventListener, addEventListeners, DOM_EVENT } from '../../browser/addEventListener'
77
import { clearInterval, setInterval } from '../../tools/timer'
88
import type { Configuration } from '../configuration'
@@ -11,10 +11,14 @@ import { addTelemetryDebug } from '../telemetry'
1111
import { isSyntheticsTest } from '../synthetics/syntheticsWorkerValues'
1212
import type { CookieStore } from '../../browser/browser.types'
1313
import { getCurrentSite } from '../../browser/cookie'
14+
import { ExperimentalFeature, isExperimentalFeatureEnabled } from '../../tools/experimentalFeatures'
15+
import { findLast } from '../../tools/utils/polyfills'
1416
import { SESSION_NOT_TRACKED, SESSION_TIME_OUT_DELAY } from './sessionConstants'
1517
import { startSessionStore } from './sessionStore'
1618
import type { SessionState } from './sessionState'
19+
import { toSessionState } from './sessionState'
1720
import { retrieveSessionCookie } from './storeStrategies/sessionInCookie'
21+
import { SESSION_STORE_KEY } from './storeStrategies/sessionStoreStrategy'
1822

1923
export interface SessionManager<TrackingType extends string> {
2024
findSession: (
@@ -75,6 +79,12 @@ export function startSessionManager<TrackingType extends string>(
7579
// manager is started.
7680
sessionStore.expandOrRenewSession()
7781
sessionContextHistory.add(buildSessionContext(), clocksOrigin().relative)
82+
if (isExperimentalFeatureEnabled(ExperimentalFeature.SHORT_SESSION_INVESTIGATION)) {
83+
const session = sessionStore.getSession()
84+
if (session) {
85+
detectSessionIdChange(configuration, session)
86+
}
87+
}
7888

7989
trackingConsentState.observable.subscribe(() => {
8090
if (trackingConsentState.isGranted()) {
@@ -166,9 +176,9 @@ async function reportUnexpectedSessionState() {
166176
let sessionCookies: string[] | Awaited<ReturnType<CookieStore['getAll']>> = []
167177

168178
if ('cookieStore' in window) {
169-
sessionCookies = await (window as { cookieStore: CookieStore }).cookieStore.getAll('_dd_s')
179+
sessionCookies = await (window as { cookieStore: CookieStore }).cookieStore.getAll(SESSION_STORE_KEY)
170180
} else {
171-
sessionCookies = document.cookie.split(/\s*;\s*/).filter((cookie) => cookie.startsWith('_dd_s'))
181+
sessionCookies = document.cookie.split(/\s*;\s*/).filter((cookie) => cookie.startsWith(SESSION_STORE_KEY))
172182
}
173183

174184
addTelemetryDebug('Unexpected session state', {
@@ -184,3 +194,39 @@ async function reportUnexpectedSessionState() {
184194
currentDomain: `${window.location.protocol}//${window.location.hostname}`,
185195
})
186196
}
197+
198+
function detectSessionIdChange(configuration: Configuration, initialSessionState: SessionState) {
199+
if (!window.cookieStore || !initialSessionState.created) {
200+
return
201+
}
202+
203+
const sessionCreatedTime = Number(initialSessionState.created)
204+
const sdkInitTime = dateNow()
205+
206+
const { stop } = addEventListener(configuration, cookieStore as CookieStore, DOM_EVENT.CHANGE, listener)
207+
stopCallbacks.push(stop)
208+
209+
function listener(event: CookieChangeEvent) {
210+
const changed = findLast(event.changed, (change): change is CookieListItem => change.name === SESSION_STORE_KEY)
211+
if (!changed) {
212+
return
213+
}
214+
215+
const sessionAge = dateNow() - sessionCreatedTime
216+
if (sessionAge > 14 * ONE_MINUTE) {
217+
// The session might have expired just because it's too old or lack activity
218+
stop()
219+
} else {
220+
const newSessionState = toSessionState(changed.value)
221+
if (newSessionState.id !== initialSessionState.id) {
222+
addTelemetryDebug('Session cookie changed', {
223+
time: dateNow() - sdkInitTime,
224+
session_age: sessionAge,
225+
old: initialSessionState,
226+
new: newSessionState,
227+
})
228+
stop()
229+
}
230+
}
231+
}
232+
}

packages/core/src/tools/experimentalFeatures.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export enum ExperimentalFeature {
1919
EARLY_REQUEST_COLLECTION = 'early_request_collection',
2020
WATCH_COOKIE_WITHOUT_LOCK = 'watch_cookie_without_lock',
2121
USE_TREE_WALKER_FOR_ACTION_NAME = 'use_tree_walker_for_action_name',
22+
SHORT_SESSION_INVESTIGATION = 'short_session_investigation',
2223
}
2324

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

packages/core/src/tools/utils/polyfills.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export function findLast<T, S extends T>(
2-
array: T[],
3-
predicate: (item: T, index: number, array: T[]) => item is S
2+
array: readonly T[],
3+
predicate: (item: T, index: number, array: readonly T[]) => item is S
44
): S | undefined {
55
for (let i = array.length - 1; i >= 0; i -= 1) {
66
const item = array[i]

0 commit comments

Comments
 (0)