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

Add additional app and device fields #881

Merged
merged 17 commits into from
Jun 23, 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
5 changes: 5 additions & 0 deletions packages/core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ class Client {
this.Breadcrumb = Breadcrumb
this.Session = Session

// store the time we were constructed so we can calculate the duration that
// the application has been running when an error is thrown
this._startTime = new Date()
imjoehaines marked this conversation as resolved.
Show resolved Hide resolved

this._config = this._configure(configuration, internalPlugins)
map(internalPlugins.concat(this._config.plugins), pl => {
if (pl) this._loadPlugin(pl)
Expand Down Expand Up @@ -265,6 +269,7 @@ class Client {
_notify (event, onError, cb = noop) {
event.app = assign({}, event.app, {
releaseStage: this._config.releaseStage,
duration: new Date() - this._startTime,
version: this._config.appVersion,
type: this._config.appType
})
Expand Down
24 changes: 24 additions & 0 deletions packages/core/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,30 @@ describe('@bugsnag/core/client', () => {
client.notify(new Error('oh em eff gee'))
})

it('includes duration in event.app', done => {
const start = +new Date()
const client = new Client({ apiKey: 'API_KEY_YEAH', enabledReleaseStages: ['staging'], releaseStage: 'staging' })

// Delay sending the notification by this many milliseconds to ensure
// 'duration' will always have a useful value
const delayMs = 10

client._setDelivery(client => ({
sendEvent: (payload) => {
// The maximum number of milliseconds 'duration' should be
const maximum = +new Date() - start

expect(payload.events[0].app.duration).toBeGreaterThanOrEqual(delayMs)
expect(payload.events[0].app.duration).toBeLessThanOrEqual(maximum)

done()
},
sendSession: () => {}
}))

setTimeout(() => client.notify(new Error('oh em eff gee')), delayMs)
imjoehaines marked this conversation as resolved.
Show resolved Hide resolved
})

it('can handle all kinds of bad input', () => {
const payloads: any[] = []
const client = new Client({ apiKey: 'API_KEY_YEAH' })
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-browser-device/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @bugsnag/plugin-browser-device

This plugin adds the device time, locale and user agent to the `device` section of each event, and adds the user agent to session payloads. It is included in the browser notifier.
This plugin adds the device time, locale, user agent and screen orientation to the `device` section of each event, and adds the locale, user agent and screen orientation to session payloads. It is included in the browser notifier.

## License
MIT
6 changes: 5 additions & 1 deletion packages/plugin-browser-device/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ const assign = require('@bugsnag/core/lib/es-utils/assign')
/*
* Automatically detects browser device details
*/
module.exports = (nav = navigator) => ({
module.exports = (nav = navigator, _screen = window.screen) => ({
load: (client) => {
const device = {
locale: nav.browserLanguage || nav.systemLanguage || nav.userLanguage || nav.language,
userAgent: nav.userAgent
}

if (_screen && _screen.orientation && _screen.orientation.type) {
device.orientation = _screen.orientation.type
imjoehaines marked this conversation as resolved.
Show resolved Hide resolved
}

client.addOnSession(session => {
session.device = assign({}, session.device, device)
})
Expand Down
41 changes: 41 additions & 0 deletions packages/plugin-browser-device/test/device.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import plugin from '../device'
import Client, { SessionDeliveryPayload, EventDeliveryPayload } from '@bugsnag/core/client'

const navigator = { language: 'en-GB', userAgent: 'testing browser 1.2.3' } as unknown as Navigator
const screen = { orientation: { type: 'landscape-primary' } } as unknown as Screen

describe('plugin: device', () => {
it('should add an onError callback which captures device information', () => {
Expand All @@ -19,6 +20,24 @@ describe('plugin: device', () => {
expect(payloads[0].events[0].device.time instanceof Date).toBe(true)
expect(payloads[0].events[0].device.locale).toBe(navigator.language)
expect(payloads[0].events[0].device.userAgent).toBe(navigator.userAgent)
expect(payloads[0].events[0].device.orientation).toBe(undefined)
})

it('should capture the screen orientation if possible and add it to the event', () => {
const client = new Client({ apiKey: 'API_KEY_YEAH', plugins: [plugin(navigator, screen)] })
const payloads: EventDeliveryPayload[] = []

expect(client._cbs.e.length).toBe(1)

client._setDelivery(client => ({ sendEvent: (payload) => payloads.push(payload), sendSession: () => {} }))
client.notify(new Error('noooo'))

expect(payloads.length).toEqual(1)
expect(payloads[0].events[0].device).toBeDefined()
expect(payloads[0].events[0].device.time instanceof Date).toBe(true)
expect(payloads[0].events[0].device.locale).toBe(navigator.language)
expect(payloads[0].events[0].device.userAgent).toBe(navigator.userAgent)
expect(payloads[0].events[0].device.orientation).toBe('landscape-primary')
})

it('should add an onSession callback which captures device information', () => {
Expand All @@ -39,5 +58,27 @@ describe('plugin: device', () => {
expect(payloads[0].device).toBeDefined()
expect(payloads[0].device && payloads[0].device.locale).toBe(navigator.language)
expect(payloads[0].device && payloads[0].device.userAgent).toBe(navigator.userAgent)
expect(payloads[0].device && payloads[0].device.orientation).toBe(undefined)
})

it('should capture the screen orientation if possible and add it to the session', () => {
const client = new Client({ apiKey: 'API_KEY_YEAH', plugins: [plugin(navigator, screen)] })
const payloads: SessionDeliveryPayload[] = []
client._sessionDelegate = {
startSession: (client, session) => {
client._delivery.sendSession(session, () => {})
}
}

expect(client._cbs.s.length).toBe(1)

client._setDelivery(client => ({ sendEvent: () => {}, sendSession: (payload) => payloads.push(payload) }))
client.startSession()

expect(payloads.length).toEqual(1)
expect(payloads[0].device).toBeDefined()
expect(payloads[0].device && payloads[0].device.locale).toBe(navigator.language)
expect(payloads[0].device && payloads[0].device.userAgent).toBe(navigator.userAgent)
expect(payloads[0].device && payloads[0].device.orientation).toBe('landscape-primary')
})
})
5 changes: 1 addition & 4 deletions packages/plugin-expo-app/app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
const Constants = require('expo-constants').default
const { AppState } = require('react-native')

const appStart = new Date()
imjoehaines marked this conversation as resolved.
Show resolved Hide resolved

module.exports = {
load: client => {
let lastEnteredForeground = appStart
let lastEnteredForeground = new Date()
let lastState = AppState.currentState

AppState.addEventListener('change', newState => {
Expand Down Expand Up @@ -35,7 +33,6 @@ module.exports = {
const now = new Date()
const inForeground = AppState.currentState === 'active'
event.app.inForeground = inForeground
event.app.duration = now - appStart
if (inForeground) {
event.app.durationInForeground = now - lastEnteredForeground
}
Expand Down
24 changes: 20 additions & 4 deletions packages/plugin-expo-device/device.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const Device = require('expo-device')
const FileSystem = require('expo-file-system')
const Constants = require('expo-constants').default
const { Dimensions, Platform } = require('react-native')
const rnVersion = require('react-native/package.json').version
Expand All @@ -20,10 +22,22 @@ module.exports = {
// get the initial orientation
updateOrientation()

// Fetch the free disk space up front because it's an async API so we don't
// want to do this in the onError callback. This means the reported free
imjoehaines marked this conversation as resolved.
Show resolved Hide resolved
// disk space could be inaccurate as it may change between now and when an
// error occurs, however it's unlikely to be drastically different
let freeDisk
FileSystem.getFreeDiskStorageAsync().then(freeDiskStorage => { freeDisk = freeDiskStorage })
imjoehaines marked this conversation as resolved.
Show resolved Hide resolved

const device = {
id: Constants.installationId,
manufacturer: Constants.platform.ios ? 'Apple' : undefined,
model: Constants.platform.ios ? Constants.platform.ios.model : undefined,
manufacturer: Device.manufacturer,
// On a real device these two seem equivalent, however on a simulator
// 'Constants' is a bit more useful as it returns 'Simulator' whereas
// 'Device' just returns 'iPhone'
model: Constants.platform.ios
? Constants.platform.ios.model
: Device.modelName,
modelNumber: Constants.platform.ios ? Constants.platform.ios.platform : undefined,
osName: Platform.OS,
osVersion: Constants.platform.ios ? Constants.platform.ios.systemVersion : Constants.systemVersion,
Expand All @@ -32,7 +46,8 @@ module.exports = {
expoApp: Constants.expoVersion,
expoSdk: Constants.manifest.sdkVersion,
androidApiLevel: Constants.platform.android ? String(Platform.Version) : undefined
}
},
totalMemory: Device.totalMemory
}

client.addOnSession(session => {
Expand All @@ -43,7 +58,8 @@ module.exports = {
event.device = { ...event.device, time: new Date(), orientation, ...device }
event.addMetadata('device', {
isDevice: Constants.isDevice,
appOwnership: Constants.appOwnership
appOwnership: Constants.appOwnership,
freeDisk
})
}, true)
}
Expand Down
76 changes: 76 additions & 0 deletions packages/plugin-expo-device/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion packages/plugin-expo-device/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
"nyc": "^12.0.2"
},
"dependencies": {
"expo-constants": "^6.0.0"
"expo-constants": "^6.0.0",
"expo-device": "^2.1.0",
"expo-file-system": "^8.1.0"
}
}
Loading