mirror of https://github.com/status-im/metro.git
Add details to Activity events
Summary: Update Activity API to allow adding more details to events for telemetry purposes. Reviewed By: davidaurelio Differential Revision: D3982691 fbshipit-source-id: 07f3ed5d1ec4eddbbdeb00feb02ea75e1168705e
This commit is contained in:
parent
308ab1001e
commit
dc0f7875c8
|
@ -11,16 +11,26 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
export type EventOptions = {
|
export type Options = {
|
||||||
telemetric?: boolean,
|
telemetric?: boolean,
|
||||||
silent?: boolean,
|
silent?: boolean,
|
||||||
|
displayFields?: Array<string> | true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Event = {
|
type EventFieldDescriptor = {
|
||||||
id: number,
|
type: 'int' | 'normal',
|
||||||
startTimeStamp: [number, number],
|
value: number | string | boolean,
|
||||||
durationMs?: number,
|
};
|
||||||
name: string,
|
|
||||||
data?: any,
|
export type NormalisedEventData = {[key: string]: EventFieldDescriptor};
|
||||||
options: EventOptions,
|
|
||||||
|
export type EventData = {[key: string]: number | string | boolean};
|
||||||
|
|
||||||
|
export type Event = {
|
||||||
|
data: NormalisedEventData,
|
||||||
|
durationMs?: number,
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
options: Options,
|
||||||
|
startTimeStamp: [number, number],
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,7 +32,7 @@ describe('Activity', () => {
|
||||||
const EVENT_NAME = 'EVENT_NAME';
|
const EVENT_NAME = 'EVENT_NAME';
|
||||||
const DATA = {someData: 42};
|
const DATA = {someData: 42};
|
||||||
|
|
||||||
Activity.startEvent(EVENT_NAME, DATA);
|
Activity.startEvent(EVENT_NAME, DATA, {displayFields: ['someData']});
|
||||||
jest.runOnlyPendingTimers();
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
// eslint-disable-next-line no-console-disallow
|
// eslint-disable-next-line no-console-disallow
|
||||||
|
@ -41,7 +41,7 @@ describe('Activity', () => {
|
||||||
const consoleMsg = console.log.mock.calls[0][0];
|
const consoleMsg = console.log.mock.calls[0][0];
|
||||||
expect(consoleMsg).toContain('START');
|
expect(consoleMsg).toContain('START');
|
||||||
expect(consoleMsg).toContain(EVENT_NAME);
|
expect(consoleMsg).toContain(EVENT_NAME);
|
||||||
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
expect(consoleMsg).toContain('someData: 42');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not write the "START" phase of silent events to the console', () => {
|
it('does not write the "START" phase of silent events to the console', () => {
|
||||||
|
@ -61,7 +61,7 @@ describe('Activity', () => {
|
||||||
const EVENT_NAME = 'EVENT_NAME';
|
const EVENT_NAME = 'EVENT_NAME';
|
||||||
const DATA = {someData: 42};
|
const DATA = {someData: 42};
|
||||||
|
|
||||||
const eventID = Activity.startEvent(EVENT_NAME, DATA);
|
const eventID = Activity.startEvent(EVENT_NAME, DATA, {displayFields: ['someData']});
|
||||||
Activity.endEvent(eventID);
|
Activity.endEvent(eventID);
|
||||||
jest.runOnlyPendingTimers();
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ describe('Activity', () => {
|
||||||
const consoleMsg = console.log.mock.calls[1][0];
|
const consoleMsg = console.log.mock.calls[1][0];
|
||||||
expect(consoleMsg).toContain('END');
|
expect(consoleMsg).toContain('END');
|
||||||
expect(consoleMsg).toContain(EVENT_NAME);
|
expect(consoleMsg).toContain(EVENT_NAME);
|
||||||
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
expect(consoleMsg).toContain('someData: 42');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not write the "END" phase of silent events to the console', () => {
|
it('does not write the "END" phase of silent events to the console', () => {
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import type {Event} from './Types';
|
||||||
|
|
||||||
|
function getDataString(event: Event): string {
|
||||||
|
const {options, data} = event;
|
||||||
|
const {displayFields} = options;
|
||||||
|
|
||||||
|
if (!Object.keys(data).length ||
|
||||||
|
!(Array.isArray(displayFields) || displayFields === true)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = Array.isArray(displayFields) ? displayFields : Object.keys(data);
|
||||||
|
const dataList = fields.map(field => {
|
||||||
|
if (data[field] === undefined) {
|
||||||
|
throw new Error(`"${field}" is not defined for event ""${event.name}"!`);
|
||||||
|
}
|
||||||
|
return `${field}: ${data[field].value.toString()}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
let dataString = dataList.join(' | ');
|
||||||
|
|
||||||
|
if (dataString) {
|
||||||
|
dataString = ` ${dataString} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataString;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getDataString;
|
|
@ -11,11 +11,12 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import type {EventOptions} from './Types';
|
import type {Event, EventData, Options} from './Types';
|
||||||
import type {Event} from './Types';
|
|
||||||
|
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const events = require('events');
|
const events = require('events');
|
||||||
|
const formatData = require('./formatData');
|
||||||
|
const normaliseEventData = require('./normaliseEventData');
|
||||||
|
|
||||||
let ENABLED = true;
|
let ENABLED = true;
|
||||||
let UUID = 1;
|
let UUID = 1;
|
||||||
|
@ -23,24 +24,21 @@ let UUID = 1;
|
||||||
const EVENT_INDEX: {[key: number]: Event} = Object.create(null);
|
const EVENT_INDEX: {[key: number]: Event} = Object.create(null);
|
||||||
const EVENT_EMITTER = new events.EventEmitter();
|
const EVENT_EMITTER = new events.EventEmitter();
|
||||||
|
|
||||||
function startEvent(
|
function startEvent(name: string, data: EventData = {}, options: Options = {}): number {
|
||||||
name: string,
|
|
||||||
data: any = null,
|
|
||||||
options?: EventOptions = {},
|
|
||||||
): number {
|
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
throw new Error('No event name specified!');
|
throw new Error('No event name specified!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = UUID++;
|
const id = UUID++;
|
||||||
EVENT_INDEX[id] = {
|
EVENT_INDEX[id] = {
|
||||||
|
data: normaliseEventData(data),
|
||||||
id,
|
id,
|
||||||
startTimeStamp: process.hrtime(),
|
|
||||||
name,
|
|
||||||
data,
|
|
||||||
options,
|
options,
|
||||||
|
name,
|
||||||
|
startTimeStamp: process.hrtime(),
|
||||||
};
|
};
|
||||||
logEvent(id, 'startEvent');
|
logEvent(id, 'startEvent');
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,12 +72,11 @@ function logEvent(id: number, phase: 'startEvent' | 'endEvent'): void {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
durationMs,
|
durationMs,
|
||||||
data,
|
|
||||||
options,
|
options,
|
||||||
} = event;
|
} = event;
|
||||||
|
|
||||||
const logTimeStamp = new Date().toLocaleString();
|
const logTimeStamp = new Date().toLocaleString();
|
||||||
const dataString = data ? ': ' + JSON.stringify(data) : '';
|
const dataString = formatData(event);
|
||||||
const {telemetric, silent} = options;
|
const {telemetric, silent} = options;
|
||||||
|
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
|
@ -94,15 +91,15 @@ function logEvent(id: number, phase: 'startEvent' | 'endEvent'): void {
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
// eslint-disable-next-line no-console-disallow
|
// eslint-disable-next-line no-console-disallow
|
||||||
console.log(
|
console.log(
|
||||||
chalk.dim(`[${logTimeStamp}] <END> ${name}${dataString} `) +
|
chalk.dim(`[${logTimeStamp}] <END> ${name}${dataString}`) +
|
||||||
(telemetric ? chalk.reset.cyan(`(${+durationMs}ms)`) : chalk.dim(`(${+durationMs}ms)`))
|
(telemetric ? chalk.reset.cyan(` (${+durationMs}ms)`) : chalk.dim(` (${+durationMs}ms)`))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
forgetEvent(id);
|
forgetEvent(id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error('Unexpected scheduled event type: ' + name);
|
throw new Error(`Unexpected event phase "${phase}"!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import type {EventData, NormalisedEventData} from './Types';
|
||||||
|
|
||||||
|
function normaliseEventData(eventData: EventData): NormalisedEventData {
|
||||||
|
if (!eventData) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalisedEventData = {};
|
||||||
|
|
||||||
|
Object.keys(eventData).forEach(field => {
|
||||||
|
const value = eventData[field];
|
||||||
|
let type;
|
||||||
|
|
||||||
|
if (typeof value === 'string' || typeof value === 'boolean') {
|
||||||
|
type = 'normal';
|
||||||
|
} else if (typeof value === 'number') {
|
||||||
|
type = 'int';
|
||||||
|
} else {
|
||||||
|
throw new Error(`Disallowed value for event field "${field}""!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalisedEventData[field] = {type, value};
|
||||||
|
});
|
||||||
|
|
||||||
|
return normalisedEventData;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = normaliseEventData;
|
|
@ -366,11 +366,12 @@ class Bundler {
|
||||||
}) {
|
}) {
|
||||||
const findEventId = Activity.startEvent(
|
const findEventId = Activity.startEvent(
|
||||||
'Transforming modules',
|
'Transforming modules',
|
||||||
null,
|
{
|
||||||
|
entry_point: entryFile,
|
||||||
|
environment: dev ? 'dev' : 'prod',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
telemetric: true,
|
telemetric: true,
|
||||||
entryPoint: entryFile,
|
|
||||||
details: dev ? 'dev' : 'prod',
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const modulesByName = Object.create(null);
|
const modulesByName = Object.create(null);
|
||||||
|
|
|
@ -114,7 +114,9 @@ class Transformer {
|
||||||
debug('transforming file', fileName);
|
debug('transforming file', fileName);
|
||||||
const transformEventId = Activity.startEvent(
|
const transformEventId = Activity.startEvent(
|
||||||
'Transforming file',
|
'Transforming file',
|
||||||
fileName,
|
{
|
||||||
|
file_name: fileName,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
telemetric: true,
|
telemetric: true,
|
||||||
silent: true,
|
silent: true,
|
||||||
|
|
|
@ -499,7 +499,15 @@ class Server {
|
||||||
_processAssetsRequest(req, res) {
|
_processAssetsRequest(req, res) {
|
||||||
const urlObj = url.parse(decodeURI(req.url), true);
|
const urlObj = url.parse(decodeURI(req.url), true);
|
||||||
const assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
const assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
||||||
const assetEvent = Activity.startEvent('Processing asset request', {asset: assetPath[1]});
|
const assetEvent = Activity.startEvent(
|
||||||
|
'Processing asset request',
|
||||||
|
{
|
||||||
|
asset: assetPath[1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayFields: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
this._assetServer.get(assetPath[1], urlObj.query.platform)
|
this._assetServer.get(assetPath[1], urlObj.query.platform)
|
||||||
.then(
|
.then(
|
||||||
data => {
|
data => {
|
||||||
|
@ -538,10 +546,11 @@ class Server {
|
||||||
Activity.startEvent(
|
Activity.startEvent(
|
||||||
'Updating existing bundle',
|
'Updating existing bundle',
|
||||||
{
|
{
|
||||||
outdatedModules: outdated.size,
|
outdated_modules: outdated.size,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
telemetric: true,
|
telemetric: true,
|
||||||
|
displayFields: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
debug('Attempt to update existing bundle');
|
debug('Attempt to update existing bundle');
|
||||||
|
@ -650,11 +659,11 @@ class Server {
|
||||||
'Requesting bundle',
|
'Requesting bundle',
|
||||||
{
|
{
|
||||||
url: req.url,
|
url: req.url,
|
||||||
|
entry_point: options.entryFile,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
telemetric: true,
|
telemetric: true,
|
||||||
entryPoint: options.entryFile,
|
displayFields: ['url'],
|
||||||
details: req.url,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue