Skip to content

Commit

Permalink
Merge branch 'master' into benoit/import-rrweb-integration-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
BenoitZugmeyer authored Feb 23, 2021
2 parents 7c79c83 + 7480da5 commit 908ba4f
Show file tree
Hide file tree
Showing 20 changed files with 298 additions and 45 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
---

## v2.5.4

- 🔊 Add clock drift monitoring ([#736](https://github.com/DataDog/browser-sdk/pull/736))
- ✨ Implement a developer extension ([#686](https://github.com/DataDog/browser-sdk/pull/686))

## v2.5.3

- ⚗ Remove mutation buffer global instance ([#728](https://github.com/DataDog/browser-sdk/pull/728))
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This repository contains several packages:
| browser-logs | [![npm version][01]][02] | [![bundle size][03]][04] | [datadog-logs][05] | [![API][1]][07] [![product][2]][08] |
| browser-rum | [![npm version][11]][12] | [![bundle size][13]][14] | [datadog-rum][15] | [![API][1]][17] [![product][2]][18] |
| browser-rum-recorder | [![npm version][21]][22] | [![bundle size][23]][24] | [datadog-rum-recorder][25] | [![API][1]][27] [![product][2]][28] |
| browser-rum-core | [![npm version][41]][42] | [![bundle size][43]][44] | |
| browser-core | [![npm version][31]][32] | [![bundle size][33]][34] | |

[1]: https://github.githubassets.com/favicons/favicon.png
Expand Down Expand Up @@ -40,3 +41,7 @@ This repository contains several packages:
[32]: https://badge.fury.io/js/%40datadog%2Fbrowser-core
[33]: https://badgen.net/bundlephobia/minzip/@datadog/browser-core
[34]: https://bundlephobia.com/result?p=@datadog/browser-core
[41]: https://badge.fury.io/js/%40datadog%2Fbrowser-rum-core.svg
[42]: https://badge.fury.io/js/%40datadog%2Fbrowser-rum-core
[43]: https://badgen.net/bundlephobia/minzip/@datadog/browser-rum-core
[44]: https://bundlephobia.com/result?p=@datadog/browser-rum-core
2 changes: 1 addition & 1 deletion developer-extension/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@datadog/browser-sdk-developer-extension",
"version": "1.0.0",
"version": "2.5.4",
"private": true,
"scripts": {
"build": "rm -rf dist && webpack --mode production",
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "2.5.3",
"version": "2.5.4",
"publishConfig": {
"access": "public"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@datadog/browser-core",
"version": "2.5.3",
"version": "2.5.4",
"license": "Apache-2.0",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/domain/internalMonitoring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export function startInternalMonitoring(configuration: Configuration): InternalM
maxMessagesPerPage: configuration.maxInternalMonitoringMessagesPerPage,
sentMessageCount: 0,
})

startMonitoringClockDrift()
}
return {
setExternalContextProvider: (provider: () => Context) => {
Expand Down Expand Up @@ -95,6 +97,26 @@ export function resetInternalMonitoring() {
monitoringConfiguration.batch = undefined
}

function startMonitoringClockDrift() {
const interval = setInterval(
monitor(() => {
const drift = Date.now() - utils.getTimestamp(performance.now())
if (Math.abs(drift) > utils.ONE_SECOND) {
clearInterval(interval)
const navigationStart = utils.getTimestamp(0)
addMonitoringMessage('clock drift detected', {
debug: {
navigationStartUTC: new Date(navigationStart).toUTCString(),
timeSpent: Date.now() - navigationStart,
drift,
},
})
}
}),
utils.ONE_MINUTE
)
}

export function monitored<T extends (...params: any[]) => unknown>(
_: any,
__: string,
Expand Down
4 changes: 2 additions & 2 deletions packages/logs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@datadog/browser-logs",
"version": "2.5.3",
"version": "2.5.4",
"license": "Apache-2.0",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand All @@ -13,7 +13,7 @@
"replace-build-env": "node ../../scripts/replace-build-env.js"
},
"dependencies": {
"@datadog/browser-core": "2.5.3",
"@datadog/browser-core": "2.5.4",
"tslib": "^1.10.0"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/rum-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@datadog/browser-rum-core",
"version": "2.5.3",
"version": "2.5.4",
"license": "Apache-2.0",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand All @@ -12,7 +12,7 @@
"replace-build-env": "node ../../scripts/replace-build-env.js"
},
"dependencies": {
"@datadog/browser-core": "2.5.3",
"@datadog/browser-core": "2.5.4",
"tslib": "^1.10.0"
},
"devDependencies": {
Expand Down
21 changes: 17 additions & 4 deletions packages/rum-core/src/boot/rum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,22 @@ import { startRumBatch } from '../transport/batch'
import { buildEnv } from './buildEnv'
import { RumUserConfiguration } from './rumPublicApi'

export function startRum(userConfiguration: RumUserConfiguration, getCommonContext: () => CommonContext) {
export type NewLocationListener = (
newLocation: Location,
oldLocation?: Location
) => undefined | { shouldCreateView?: boolean; viewName?: string }

export function startRum(
userConfiguration: RumUserConfiguration & { onNewLocation?: NewLocationListener },
getCommonContext: () => CommonContext
) {
const lifeCycle = new LifeCycle()

const { configuration, internalMonitoring } = commonInit(userConfiguration, buildEnv)
const session = startRumSession(configuration, lifeCycle)
if (!configuration.isEnabled('onNewLocation')) {
userConfiguration.onNewLocation = undefined
}

internalMonitoring.setExternalContextProvider(() =>
combine(
Expand All @@ -40,7 +51,8 @@ export function startRum(userConfiguration: RumUserConfiguration, getCommonConte
lifeCycle,
configuration,
session,
getCommonContext
getCommonContext,
userConfiguration.onNewLocation
)

startRequestCollection(lifeCycle, configuration)
Expand All @@ -67,14 +79,15 @@ export function startRumEventCollection(
lifeCycle: LifeCycle,
configuration: Configuration,
session: RumSession,
getCommonContext: () => CommonContext
getCommonContext: () => CommonContext,
onNewLocation?: NewLocationListener
) {
const parentContexts = startParentContexts(lifeCycle, session)
const batch = startRumBatch(configuration, lifeCycle)
startRumAssembly(applicationId, configuration, lifeCycle, session, parentContexts, getCommonContext)
startLongTaskCollection(lifeCycle)
startResourceCollection(lifeCycle, session)
const { addTiming } = startViewCollection(lifeCycle, location)
const { addTiming } = startViewCollection(lifeCycle, location, onNewLocation)
const { addError } = startErrorCollection(lifeCycle, configuration)
const { addAction } = startActionCollection(lifeCycle, configuration)

Expand Down
7 changes: 7 additions & 0 deletions packages/rum-core/src/domain/parentContexts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ describe('parentContexts', () => {
expect(parentContexts.findView()!.view.url).toBe('http://fake-url.com/foo')
})

it('should return the view name with the view', () => {
const { lifeCycle } = setupBuilder.build()

lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, buildViewCreatedEvent({ name: 'Fake name' }))
expect(parentContexts.findView()!.view.name).toBe('Fake name')
})

it('should update session id only on VIEW_CREATED', () => {
const { lifeCycle } = setupBuilder.build()

Expand Down
1 change: 1 addition & 0 deletions packages/rum-core/src/domain/parentContexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function startParentContexts(lifeCycle: LifeCycle, session: RumSession):
},
view: {
id: currentView!.id,
name: currentView!.name,
referrer: currentView!.referrer,
url: currentView!.location.href,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createRawRumEvent } from '../../../../test/fixtures'
import { setup, TestSetupBuilder } from '../../../../test/specHelper'
import { NewLocationListener } from '../../../boot/rum'
import {
RumLargestContentfulPaintTiming,
RumPerformanceNavigationTiming,
Expand Down Expand Up @@ -208,6 +209,150 @@ describe('rum track url change', () => {
})
})

describe('rum use onNewLocation callback to rename/ignore views', () => {
let setupBuilder: TestSetupBuilder
let handler: jasmine.Spy
let getViewEvent: (index: number) => View
let onNewLocation: NewLocationListener

beforeEach(() => {
;({ handler, getViewEvent } = spyOnViews())

setupBuilder = setup()
.withFakeLocation('/foo')
.beforeBuild(({ location, lifeCycle }) => {
lifeCycle.subscribe(LifeCycleEventType.VIEW_UPDATED, handler)
return trackViews(location, lifeCycle, onNewLocation)
})
})

afterEach(() => {
setupBuilder.cleanup()
})

it('should set the view name to the returned viewName', () => {
onNewLocation = (location) => {
switch (location.pathname) {
case '/foo':
return { viewName: 'Foo' }
case '/bar':
return { viewName: 'Bar' }
}
}
setupBuilder.build()
history.pushState({}, '', '/bar')
history.pushState({}, '', '/baz')

expect(getViewEvent(0).name).toBe('Foo')
expect(getViewEvent(2).name).toBe('Bar')
expect(getViewEvent(4).name).toBeUndefined()
})

it('should allow customer to consider other location changes as new views', () => {
onNewLocation = (location) => ({ viewName: `Foo ${location.search}`, shouldCreateView: true })
setupBuilder.build()
history.pushState({}, '', '/foo?view=bar')
history.pushState({}, '', '/foo?view=baz')

expect(getViewEvent(0).name).toBe('Foo ')
expect(getViewEvent(2).name).toBe('Foo ?view=bar')
expect(getViewEvent(4).name).toBe('Foo ?view=baz')
})

it('pass current and old locations to onNewLocation', () => {
onNewLocation = (location, oldLocation) => ({
viewName: `old: ${oldLocation?.pathname || 'undefined'}, new: ${location.pathname}`,
})
setupBuilder.build()
history.pushState({}, '', '/bar')

expect(getViewEvent(0).name).toBe('old: undefined, new: /foo')
expect(getViewEvent(2).name).toBe('old: /foo, new: /bar')
})

it('should use our own new view detection rules when shouldCreateView is undefined', () => {
onNewLocation = (location) => {
switch (location.pathname) {
case '/foo':
return { viewName: 'Foo' }
case '/bar':
return { viewName: 'Bar' }
}
}
setupBuilder.build()
history.pushState({}, '', '/foo')
history.pushState({}, '', '/bar')
history.pushState({}, '', '/bar')
history.pushState({}, '', '/foo')

expect(getViewEvent(0).name).toBe('Foo')
expect(getViewEvent(2).id).toBe(getViewEvent(0).id)
expect(getViewEvent(3).name).toBe('Bar')
expect(getViewEvent(5).id).toBe(getViewEvent(3).id)
expect(getViewEvent(6).name).toBe('Foo')
})

it('should ignore the view when shouldCreateView is false', () => {
onNewLocation = (location) => {
switch (location.pathname) {
case '/foo':
return { viewName: 'Foo', shouldCreateView: true }
case '/bar':
return { shouldCreateView: false }
case '/baz':
return { viewName: 'Baz', shouldCreateView: true }
}
}
setupBuilder.build()
history.pushState({}, '', '/bar')
history.pushState({}, '', '/baz')

const initialViewId = getViewEvent(0).id
expect(getViewEvent(0).name).toBe('Foo')
expect(getViewEvent(2).name).toBe('Foo')
expect(getViewEvent(2).id).toBe(initialViewId)
expect(getViewEvent(3).name).toBe('Baz')
expect(getViewEvent(3).id).not.toBe(initialViewId)
})

it('should create the initial view even when shouldCreateView is false', () => {
onNewLocation = (location) => {
if (location.pathname === '/foo') {
return { shouldCreateView: false }
}
if (location.pathname === '/bar') {
return { shouldCreateView: true }
}
}
setupBuilder.build()
history.pushState({}, '', '/bar')
history.pushState({}, '', '/foo')

expect(getViewEvent(0).location.pathname).toBe('/foo')
expect(getViewEvent(2).location.pathname).toBe('/bar')
expect(getViewEvent(4)).toBeUndefined()
})

it('should catch thrown errors', () => {
const fooError = 'Error on /foo path'
const barError = 'Error on /bar path'
onNewLocation = (location) => {
if (location.pathname === '/foo') {
throw fooError
}
if (location.pathname === '/bar') {
throw barError
}
return undefined
}
const consoleErrorSpy = spyOn(console, 'error')
setupBuilder.build()
expect(consoleErrorSpy).toHaveBeenCalledWith('onNewLocation threw an error:', fooError)
history.pushState({}, '', '/bar')
expect(consoleErrorSpy).toHaveBeenCalledWith('onNewLocation threw an error:', barError)
})
})

describe('rum view referrer', () => {
let setupBuilder: TestSetupBuilder
let initialViewCreatedEvent: ViewCreatedEvent
Expand Down
Loading

0 comments on commit 908ba4f

Please sign in to comment.