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

Change view logic to emit LifeCycle events #366

Merged
merged 14 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
12 changes: 4 additions & 8 deletions packages/core/src/transport.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import lodashMerge from 'lodash.merge'

import { monitor } from './internalMonitoring'
import { Context, DOM_EVENT, jsonStringify, objectValues } from './utils'
import { Context, DOM_EVENT, jsonStringify, noop, objectValues } from './utils'

/**
* Use POST request without content type to:
Expand Down Expand Up @@ -45,7 +45,8 @@ export class Batch<T> {
private bytesLimit: number,
private maxMessageSize: number,
private flushTimeout: number,
private contextProvider: () => Context
private contextProvider: () => Context,
private willUnloadCallback: () => void = noop
) {
this.flushOnVisibilityHidden()
this.flushPeriodically()
Expand Down Expand Up @@ -160,12 +161,7 @@ export class Batch<T> {
* register first to be sure to be called before flush on beforeunload
* caveat: unload can still be canceled by another listener
*/
window.addEventListener(
DOM_EVENT.BEFORE_UNLOAD,
monitor(() => {
this.beforeFlushOnUnloadHandlers.forEach((handler) => handler())
BenoitZugmeyer marked this conversation as resolved.
Show resolved Hide resolved
})
)
window.addEventListener(DOM_EVENT.BEFORE_UNLOAD, monitor(this.willUnloadCallback))
BenoitZugmeyer marked this conversation as resolved.
Show resolved Hide resolved

/**
* Only event that guarantee to fire on mobile devices when the page transitions to background state
Expand Down
12 changes: 11 additions & 1 deletion packages/rum/src/lifeCycle.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { ErrorMessage, RequestCompleteEvent, RequestStartEvent } from '@datadog/browser-core'
import { UserAction } from './userActionCollection'
import { View } from './viewCollection'

export enum LifeCycleEventType {
ERROR_COLLECTED,
PERFORMANCE_ENTRY_COLLECTED,
USER_ACTION_COLLECTED,
VIEW_COLLECTED,
REQUEST_STARTED,
REQUEST_COMPLETED,
SESSION_WILL_RENEW,
SESSION_RENEWED,
RESOURCE_ADDED_TO_BATCH,
DOM_MUTATED,
WILL_UNLOAD,
}

export interface Subscription {
Expand All @@ -24,11 +28,14 @@ export class LifeCycle {
notify(eventType: LifeCycleEventType.REQUEST_STARTED, data: RequestStartEvent): void
notify(eventType: LifeCycleEventType.REQUEST_COMPLETED, data: RequestCompleteEvent): void
notify(eventType: LifeCycleEventType.USER_ACTION_COLLECTED, data: UserAction): void
notify(eventType: LifeCycleEventType.VIEW_COLLECTED, data: View): void
notify(
eventType:
| LifeCycleEventType.SESSION_WILL_RENEW
| LifeCycleEventType.SESSION_RENEWED
| LifeCycleEventType.RESOURCE_ADDED_TO_BATCH
| LifeCycleEventType.DOM_MUTATED
| LifeCycleEventType.WILL_UNLOAD
): void
notify(eventType: LifeCycleEventType, data?: any) {
const eventCallbacks = this.callbacks[eventType]
Expand All @@ -48,11 +55,14 @@ export class LifeCycle {
callback: (data: RequestCompleteEvent) => void
): Subscription
subscribe(eventType: LifeCycleEventType.USER_ACTION_COLLECTED, callback: (data: UserAction) => void): Subscription
subscribe(eventType: LifeCycleEventType.VIEW_COLLECTED, callback: (data: View) => void): Subscription
subscribe(
eventType:
| LifeCycleEventType.SESSION_WILL_RENEW
| LifeCycleEventType.SESSION_RENEWED
| LifeCycleEventType.RESOURCE_ADDED_TO_BATCH
| LifeCycleEventType.DOM_MUTATED,
| LifeCycleEventType.DOM_MUTATED
| LifeCycleEventType.WILL_UNLOAD,
BenoitZugmeyer marked this conversation as resolved.
Show resolved Hide resolved
callback: () => void
): Subscription
subscribe(eventType: LifeCycleEventType, callback: (data?: any) => void) {
Expand Down
2 changes: 2 additions & 0 deletions packages/rum/src/rum.entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { startPerformanceCollection } from './performanceCollection'
import { startRum } from './rum'
import { startRumSession } from './rumSession'
import { startUserActionCollection, UserActionReference } from './userActionCollection'
import { startViewCollection } from './viewCollection'

export interface RumUserConfiguration extends UserConfiguration {
applicationId: string
Expand Down Expand Up @@ -72,6 +73,7 @@ datadogRum.init = monitor((userConfiguration: RumUserConfiguration) => {
const session = startRumSession(configuration, lifeCycle)
const globalApi = startRum(rumUserConfiguration.applicationId, lifeCycle, configuration, session, internalMonitoring)

startViewCollection(location, lifeCycle)
const [requestStartObservable, requestCompleteObservable] = startRequestCollection()
startPerformanceCollection(lifeCycle, session)
startDOMMutationCollection(lifeCycle)
Expand Down
41 changes: 32 additions & 9 deletions packages/rum/src/rum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
import { InternalContext, RumGlobal } from './rum.entry'
import { RumSession } from './rumSession'
import { getUserActionReference, UserActionMeasures, UserActionReference, UserActionType } from './userActionCollection'
import { trackView, viewContext, ViewMeasures } from './viewTracker'
import { viewContext, ViewMeasures } from './viewCollection'

export interface PerformancePaintTiming extends PerformanceEntry {
entryType: 'paint'
Expand Down Expand Up @@ -155,7 +155,7 @@ export function startRum(
lodashMerge(
{
application_id: applicationId,
session_id: viewContext.sessionId,
session_id: session.getId(),
view: {
id: viewContext.id,
},
Expand All @@ -175,17 +175,18 @@ export function startRum(
session: {
type: sessionTpe,
},
sessionId: viewContext.sessionId,
sessionId: session.getId(),
view: {
id: viewContext.id,
referrer: document.referrer,
url: viewContext.location.href,
},
}),
() => globalContext
() => globalContext,
() => lifeCycle.notify(LifeCycleEventType.WILL_UNLOAD)
)

trackView(window.location, lifeCycle, session, batch.upsertRumEvent, batch.beforeFlushOnUnload)
trackView(lifeCycle, batch.upsertRumEvent)
trackErrors(lifeCycle, batch.addRumEvent)
trackRequests(configuration, lifeCycle, session, batch.addRumEvent)
trackPerformanceTiming(configuration, lifeCycle, batch.addRumEvent)
Expand All @@ -203,7 +204,7 @@ export function startRum(
(): InternalContext => {
return {
application_id: applicationId,
session_id: viewContext.sessionId,
session_id: session.getId(),
user_action: getUserActionReference(),
view: {
id: viewContext.id,
Expand All @@ -221,23 +222,24 @@ function startRumBatch(
configuration: Configuration,
session: RumSession,
rumContextProvider: () => Context,
globalContextProvider: () => Context
globalContextProvider: () => Context,
willUnloadCallback: () => void
) {
const batch = new Batch<Context>(
new HttpRequest(configuration.rumEndpoint, configuration.batchBytesLimit, true),
configuration.maxBatchSize,
configuration.batchBytesLimit,
configuration.maxMessageSize,
configuration.flushTimeout,
() => lodashMerge(withSnakeCaseKeys(rumContextProvider()), globalContextProvider())
() => lodashMerge(withSnakeCaseKeys(rumContextProvider()), globalContextProvider()),
willUnloadCallback
)
return {
addRumEvent: (event: RumEvent, context?: Context) => {
if (session.isTracked()) {
batch.add({ ...context, ...withSnakeCaseKeys((event as unknown) as Context) })
}
},
beforeFlushOnUnload: (handler: () => void) => batch.beforeFlushOnUnload(handler),
upsertRumEvent: (event: RumEvent, key: string) => {
if (session.isTracked()) {
batch.upsert(withSnakeCaseKeys((event as unknown) as Context), key)
Expand All @@ -246,6 +248,27 @@ function startRumBatch(
}
}

function trackView(lifeCycle: LifeCycle, upsertRumEvent: (event: RumViewEvent, key: string) => void) {
lifeCycle.subscribe(LifeCycleEventType.VIEW_COLLECTED, (view) => {
upsertRumEvent(
{
date: getTimestamp(view.startTime),
duration: msToNs(view.duration),
evt: {
category: RumEventCategory.VIEW,
},
rum: {
documentVersion: view.documentVersion,
},
view: {
measures: view.measures,
},
},
view.id
)
})
}

function trackErrors(lifeCycle: LifeCycle, addRumEvent: (event: RumErrorEvent) => void) {
lifeCycle.subscribe(LifeCycleEventType.ERROR_COLLECTED, ({ message, startTime, context }: ErrorMessage) => {
addRumEvent({
Expand Down
46 changes: 40 additions & 6 deletions packages/rum/src/rumSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,53 @@ export enum RumSessionType {
TRACKED_WITHOUT_RESOURCES = '2',
}

class StoredSession {
BenoitZugmeyer marked this conversation as resolved.
Show resolved Hide resolved
private id: string | undefined
private type: RumSessionType | undefined

constructor(private isAlive: () => boolean) {}

store(id: string | undefined, type: RumSessionType | undefined) {
this.id = id
this.type = type
}

getId() {
this.makeSureSessionIsAlive()
return this.id
}

isTracked() {
this.makeSureSessionIsAlive()
return isTracked(this.type)
}

isTrackedWithResource() {
this.makeSureSessionIsAlive()
return this.type === RumSessionType.TRACKED_WITH_RESOURCES
}

private makeSureSessionIsAlive() {
if (!this.isAlive()) {
this.id = undefined
this.type = undefined
}
}
}

export function startRumSession(configuration: Configuration, lifeCycle: LifeCycle): RumSession {
const session = startSessionManagement(RUM_SESSION_KEY, (rawType) => computeSessionState(configuration, rawType))
const storedSession = new StoredSession(() => session.getId() !== undefined)

storedSession.store(session.getId(), session.getType())

session.renewObservable.subscribe(() => {
lifeCycle.notify(LifeCycleEventType.SESSION_WILL_RENEW)
storedSession.store(session.getId(), session.getType())
lifeCycle.notify(LifeCycleEventType.SESSION_RENEWED)
})
BenoitZugmeyer marked this conversation as resolved.
Show resolved Hide resolved

return {
getId: session.getId,
isTracked: () => session.getId() !== undefined && isTracked(session.getType()),
isTrackedWithResource: () =>
session.getId() !== undefined && session.getType() === RumSessionType.TRACKED_WITH_RESOURCES,
}
return storedSession
}

function computeSessionState(configuration: Configuration, rawSessionType?: string) {
Expand Down
8 changes: 0 additions & 8 deletions packages/rum/src/trackEventCounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,5 @@ export function trackEventCounts(lifeCycle: LifeCycle, callback: (eventCounts: E
subscriptions.forEach((s) => s.unsubscribe())
},
eventCounts,
reset() {
const eventCountsMap = eventCounts as { [key: string]: number }
for (const key in eventCountsMap) {
if (Object.prototype.hasOwnProperty.call(eventCountsMap, key)) {
eventCountsMap[key] = 0
}
}
},
}
}
Loading