Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Merge pull request #6267 from brave/telemetry
Browse files Browse the repository at this point in the history
Telemetry
  • Loading branch information
bsclifton authored Jan 14, 2017
2 parents 76726d5 + c298ff4 commit 4abf18d
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 0 deletions.
5 changes: 5 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ let ready = false

// Setup the crash handling
const CrashHerald = require('./crash-herald')
const telemetry = require('./telemetry')

// set initial base line checkpoint
telemetry.setCheckpoint('init')

const handleUncaughtError = (error) => {
var message, ref, stack
Expand Down Expand Up @@ -241,6 +245,7 @@ let loadAppStatePromise = SessionStore.loadAppState()

// Some settings must be set right away on startup, those settings should be handled here.
loadAppStatePromise.then((initialState) => {
telemetry.setCheckpointAndReport('state-loaded')
const {HARDWARE_ACCELERATION_ENABLED, SMOOTH_SCROLL_ENABLED, SEND_CRASH_REPORTS} = require('../js/constants/settings')
if (initialState.settings[HARDWARE_ACCELERATION_ENABLED] === false) {
app.disableHardwareAcceleration()
Expand Down
147 changes: 147 additions & 0 deletions app/telemetry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

const Immutable = require('immutable')
const _ = require('underscore')
const Channel = require('./channel')
const request = require('request')

var telemetry = Immutable.Map()
var eventList = Immutable.List()
var enabled = false
const params = {}
const DEBUG = !!process.env.TELEMETRY_DEBUG

var platforms = {
'darwin': 'osx',
'win32x64': 'winx64',
'win32ia32': 'winia32',
'linux': 'linux'
}

// Configure default payload for Telemetry calls
if (params.platform === 'win32') {
params.platform = platforms[params.platform + process.arch]
} else {
params.platform = platforms[process.platform]
}
params.version = process.env.TELEMETRY_VERSION || require('electron').app.getVersion()
params.channel = Channel.channel()
params.machine = process.env.TELEMETRY_MACHINE

// determine if fully configured
if (!!process.env.TELEMETRY_URL &&
!!process.env.TELEMETRY_TOKEN &&
params.platform &&
params.version &&
params.channel &&
!!params.machine) {
console.log('Telemetry enabled: ' + process.env.TELEMETRY_URL)
enabled = true
}

/**
* Set a checkpoint to current timestamp
* @param checkpoint {String} - name of checkpoint
* @param ts {Number} - epoch timestamp [optional]
*/
function setCheckpoint (checkpoint, ts) {
ts = ts || (new Date()).getTime()
var delta = 0

telemetry = telemetry.set(checkpoint, ts)

if (eventList.size > 0) {
delta = ts - eventList.get(eventList.size - 1)[1]
}
eventList = eventList.push([checkpoint, ts, delta / 1000])
if (DEBUG) console.log(events())
}

/**
* Return array containing timing info for all calls to setCheckpoint
*/
function events () {
return eventList.toJS()
}

/**
* Clear a checkpoint
* @param checkpoint {String} - name of checkpoint
*/
function clearCheckpoint (checkpoint) {
telemetry = telemetry.delete(checkpoint)
}

/**
* Find the difference between the timestamps of two checkpoints
* @param checkpoint1 {String} - name of earlier checkpoint
* @param checkpoint2 {String} - name of later checkpoint
*/
function deltaBetween (checkpoint1, checkpoint2) {
var ts1 = telemetry.get(checkpoint1)
var ts2 = telemetry.get(checkpoint2)
if (_.isNumber(ts1) && _.isNumber(ts2)) {
return Math.abs(ts2 - ts1)
} else {
return
}
}

/**
* Set a checkpoint and report telemetry if configured
* @param checkpoint {String} - name checkpoint
* @param initialCheckpoint {String} - name of earlier checkpoint
* @param extra {Object} - object passed to telemetry [optional]
* @param ts {Number] - epoch timestamp for checkpoint
*/
function setCheckpointAndReport (checkpoint, initialCheckpoint, extra, ts) {
initialCheckpoint = initialCheckpoint || 'init'
extra = extra || {}

setCheckpoint(checkpoint, ts)
return sendTelemetry(
checkpoint,
deltaBetween(initialCheckpoint, checkpoint),
extra
)
}

// Build payload object and send telemetry if configured
var sendTelemetry = (measure, value, extra) => {
var payload = _.extend(params, {
measure: measure,
value: value / 1000,
ts: (new Date()).getTime(),
extra: extra
})
if (enabled) {
if (DEBUG) console.log(payload)
request({
method: 'POST',
url: process.env.TELEMETRY_URL,
form: params,
headers: {
Authorization: 'Bearer ' + process.env.TELEMETRY_TOKEN
}
},
function (err, response, body) {
if (err) console.log(err)
if (DEBUG) console.log(body)
})
}
return payload
}

setCheckpoint('__baseline__')

module.exports = {
events: events,
setCheckpoint: setCheckpoint,
clearCheckpoint: clearCheckpoint,
deltaBetween: deltaBetween,
setCheckpointAndReport: setCheckpointAndReport
}
48 changes: 48 additions & 0 deletions docs/telemetry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Overview

The telemetry is used to (optionally) send timing information to a [vault-collector](https://github.com/brave/vault-collector) instance. Typically this will be used in a C.I. system to identify performance regressions.

## Setup

Quick setup: To record telemetry without sending to the collector, start browser with the `TELEMETRY_DEBUG` environment variables set to `1`.

`TELEMETRY_DEBUG=1 npm start`

The following environment variables must be set for the complete telemetry system to be enabled:

1. TELEMETRY_URL - url to [vault-collector](https://github.com/brave/vault-collector) endpoint
2. TELEMETRY_MACHINE - string identifier of the machine (i.e. MacBookPro)
3. TELEMETRY_TOKEN - string used to authenticate POST to [vault-collector](https://github.com/brave/vault-collector)

The following environment variables may be set if desired:

1. TELEMETRY_DEBUG - turn on logging to console of telemetry info
2. TELEMETRY_VERSION - string in format X.X.X used to override version number

## Usage

The browser will automatically create an `init` checkpoint before it begins its startup sequence.

Use the `setCheckpointAndReport` function to send telemetry information.

`telemetry.setCheckpointAndReport('startup-complete')`

This will issue a telemetry POST with the `measure` set to `startup-complete` and the `value` set to the amount of time since the `init` checkpoint was set.

## API

* `setCheckpoint(checkpoint, [ts])` - set a checkpoint without sending telemetry

* `clearCheckpoint(checkpoint)` - clear a previously set checkpoint

* `deltaBetween(checkpoint1, checkpoint2)` - return timing difference between two checkpoints

* `setCheckpointAndReport(checkpoint, [initialCheckpoint], [extra], [ts])` - set a checkpoint and send telemetry

* `events()` - returns an array containing timing info for each recorded event

## Extra

Each telemetry POST may contain an object with `extra` information. This should be passed as the third parameter to the `setCheckpointAndReport` function.

`setCheckpointAndReport('ready', 'init', { lastCommit: 'a3e5fa3' })`
38 changes: 38 additions & 0 deletions test/unit/app/browser/telemetryTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* global describe, it */
const assert = require('assert')

process.env.TELEMETRY_TOKEN = 'token'
process.env.TELEMETRY_VERSION = '1.2.3'
process.env.TELEMETRY_MACHINE = 'mbp'

const telemetry = require('../../../../app/telemetry')

describe('telemetry', function () {
it('records checkpoints', function () {
telemetry.setCheckpoint('foo', 1000)
telemetry.setCheckpoint('bar', 2000)

assert.equal(telemetry.deltaBetween('foo', 'bar'), 1000, 'has correct delta calculation')

telemetry.clearCheckpoint('bar')
telemetry.setCheckpoint('bar', 3000)
assert.equal(telemetry.deltaBetween('foo', 'bar'), 2000, 'clears checkpoints')
})
it('formats and sends telemetry object correctly', function () {
var payload = telemetry.setCheckpointAndReport('baz', 'foo', { attrib: 'value' }, 5000)
var expected = {
platform: payload.platform,
version: '1.2.3',
channel: payload.channel,
machine: 'mbp',
measure: 'baz',
value: 4,
ts: payload.ts,
extra: { attrib: 'value' }
}
assert.deepEqual(payload, expected, 'payload is correct')
})
it('records events correctly', function () {
assert.equal(telemetry.events().length, 5, 'correct number of events')
})
})

0 comments on commit 4abf18d

Please sign in to comment.