Skip to content

Commit

Permalink
backbone of user journey tracking poc
Browse files Browse the repository at this point in the history
  • Loading branch information
metal-messiah committed Feb 2, 2025
1 parent 8893f2d commit 5635176
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 23 deletions.
3 changes: 2 additions & 1 deletion src/common/config/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ const model = () => {
},
session: {
expiresMs: DEFAULT_EXPIRES_MS,
inactiveMs: DEFAULT_INACTIVE_MS
inactiveMs: DEFAULT_INACTIVE_MS,
user_journey: true
},
session_replay: {
// feature settings
Expand Down
4 changes: 3 additions & 1 deletion src/common/session/session-entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { windowAddEventListener } from '../event-listener/event-listener-opts'

// this is what can be stored in local storage (not enforced but probably should be)
// these values should sync between local storage and the parent class props
const model = {
export const model = {
value: '',
inactiveAt: 0,
expiresAt: 0,
Expand All @@ -29,6 +29,8 @@ const model = {
sessionTraceMode: MODE.OFF,
traceHarvestStarted: false,
serverTimeDiff: null, // set by TimeKeeper; "undefined" value will not be stringified and stored but "null" will
userJourneyPaths: '',
userJourneyTimestamps: '',
custom: {}
}

Expand Down
41 changes: 39 additions & 2 deletions src/features/generic_events/aggregate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ export class Aggregate extends AggregateBase {
this.eventsPerHarvest = 1000
this.referrerUrl = (isBrowserScope && document.referrer) ? cleanURL(document.referrer) : undefined

this.beforeUnloadFns = []
this.harvestOpts.beforeUnload = () => {
this.beforeUnloadFns.forEach(fn => fn())
}

const { userJourneyPaths, userJourneyTimestamps } = agentRef.runtime.session.read()
this.userJourney = {
host: globalScope.location?.host,
paths: userJourneyPaths,
timestamps: userJourneyTimestamps
}

this.waitForFlags(['ins']).then(([ins]) => {
if (!ins) {
this.blocked = true
Expand All @@ -34,6 +46,27 @@ export class Aggregate extends AggregateBase {

this.trackSupportabilityMetrics()

registerHandler('user-journey', (timestamp, url) => {
const { hash, pathname, search } = new URL(url)
if (this.userJourney.paths.length + pathname.length + hash.length + search.length > 4096) return
if (this.userJourney.timestamps.length + ('' + timestamp).length > 4096) return

if (this.userJourney.paths) this.userJourney.paths += '>'
this.userJourney.paths += pathname + search + hash
if (this.userJourney.timestamps) this.userJourney.timestamps += '>'
this.userJourney.timestamps += this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp)
this.syncWithSessionManager({ userJourneyPaths: this.userJourney.paths, userJourneyTimestamps: this.userJourney.timestamps })
}, this.featureName, this.ee)
this.beforeUnloadFns.push(() => {
this.addEvent({
eventType: 'SessionMetadata',

host: this.userJourney.host,
paths: this.userJourney.paths,
timestamps: this.userJourney.timestamps
})
})

registerHandler('api-recordCustomEvent', (timestamp, eventType, attributes) => {
if (RESERVED_EVENT_TYPES.includes(eventType)) return warn(46)
this.addEvent({
Expand Down Expand Up @@ -63,7 +96,7 @@ export class Aggregate extends AggregateBase {
let addUserAction
if (isBrowserScope && agentRef.init.user_actions.enabled) {
this.userActionAggregator = new UserActionsAggregator()
this.harvestOpts.beforeUnload = () => addUserAction?.(this.userActionAggregator.aggregationEvent)
this.beforeUnloadFns.push(() => addUserAction?.(this.userActionAggregator.aggregationEvent))

addUserAction = (aggregatedUserAction) => {
try {
Expand Down Expand Up @@ -237,7 +270,7 @@ export class Aggregate extends AggregateBase {

const defaultEventAttributes = {
/** should be overridden by the event-specific attributes, but just in case -- set it to now() */
timestamp: Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(now())),
timestamp: this.toEpoch(now()),
/** all generic events require pageUrl(s) */
pageUrl: cleanURL('' + initialLocation),
currentUrl: cleanURL('' + location)
Expand Down Expand Up @@ -286,4 +319,8 @@ export class Aggregate extends AggregateBase {
if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed'], undefined, FEATURE_NAMES.metrics, this.ee)
if (this.agentRef.init.performance.resources.ignore_newrelic === false) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed'], undefined, FEATURE_NAMES.metrics, this.ee)
}

syncWithSessionManager (state = {}) {
this.agentRef.runtime.session.write(state)
}
}
13 changes: 13 additions & 0 deletions src/features/generic_events/instrument/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import { globalScope, isBrowserScope } from '../../../common/constants/runtime'
import { handle } from '../../../common/event-emitter/handle'
import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts'
import { now } from '../../../common/timing/now'
import { debounce } from '../../../common/util/invoke'
import { wrapHistory } from '../../../common/wrap/wrap-history'
import { InstrumentBase } from '../../utils/instrument-base'
import { FEATURE_NAME, OBSERVED_EVENTS, OBSERVED_WINDOW_EVENTS } from '../constants'

Expand Down Expand Up @@ -43,6 +45,17 @@ export class Instrument extends InstrumentBase {
})
observer.observe({ type: 'resource', buffered: true })
}
if (agentRef.init.session.user_journey) {
const trackUserJourney = (timestamp = now()) => {
handle('user-journey', [timestamp, location], undefined, this.featureName, this.ee)
}

trackUserJourney()
const historyEE = wrapHistory(this.ee)
historyEE.on('pushState-end', trackUserJourney)
historyEE.on('replaceState-end', trackUserJourney)
windowAddEventListener('popstate', (evt) => trackUserJourney(evt.timeStamp), true, this.removeOnAbort?.signal)
}
}

/** If any of the sources are active, import the aggregator. otherwise deregister */
Expand Down
4 changes: 2 additions & 2 deletions tests/components/session-entity.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PREFIX } from '../../src/common/session/constants'
import { SessionEntity } from '../../src/common/session/session-entity'
import { LocalMemory, model } from './session-helpers'
import { SessionEntity, model } from '../../src/common/session/session-entity'
import { LocalMemory } from './session-helpers'
import * as runtimeModule from '../../src/common/constants/runtime'

jest.useFakeTimers()
Expand Down
12 changes: 0 additions & 12 deletions tests/components/session-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,3 @@ export class LocalMemory {
}
}
}
export const model = {
value: '',
inactiveAt: 0,
expiresAt: 0,
updatedAt: Date.now(),
sessionReplayMode: 0,
sessionReplaySentFirstChunk: false,
sessionTraceMode: 0,
traceHarvestStarted: false,
serverTimeDiff: null,
custom: {}
}
3 changes: 2 additions & 1 deletion tests/unit/common/config/init.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ test('init props exist and return expected defaults', () => {
})
expect(config.session).toEqual({
expiresMs: 14400000,
inactiveMs: 1800000
inactiveMs: 1800000,
user_journey: true
})
expect(config.session_replay).toEqual({
autoStart: true,
Expand Down
2 changes: 1 addition & 1 deletion tools/test-builds/browser-agent-wrapper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.279.1.tgz"
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.280.0.tgz"
}
}
2 changes: 1 addition & 1 deletion tools/test-builds/library-wrapper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"license": "ISC",
"dependencies": {
"@apollo/client": "^3.8.8",
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.279.1.tgz",
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.280.0.tgz",
"graphql": "^16.8.1"
}
}
2 changes: 1 addition & 1 deletion tools/test-builds/raw-src-wrapper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.279.1.tgz"
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.280.0.tgz"
}
}
2 changes: 1 addition & 1 deletion tools/test-builds/vite-react-wrapper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"main": "index.js",
"license": "MIT",
"dependencies": {
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.279.1.tgz",
"@newrelic/browser-agent": "file:../../../temp/newrelic-browser-agent-1.280.0.tgz",
"react": "18.2.0",
"react-dom": "18.2.0"
},
Expand Down

0 comments on commit 5635176

Please sign in to comment.