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';
|
||||
|
||||
export type EventOptions = {
|
||||
export type Options = {
|
||||
telemetric?: boolean,
|
||||
silent?: boolean,
|
||||
displayFields?: Array<string> | true,
|
||||
};
|
||||
|
||||
export type Event = {
|
||||
id: number,
|
||||
startTimeStamp: [number, number],
|
||||
durationMs?: number,
|
||||
name: string,
|
||||
data?: any,
|
||||
options: EventOptions,
|
||||
type EventFieldDescriptor = {
|
||||
type: 'int' | 'normal',
|
||||
value: number | string | boolean,
|
||||
};
|
||||
|
||||
export type NormalisedEventData = {[key: string]: EventFieldDescriptor};
|
||||
|
||||
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 DATA = {someData: 42};
|
||||
|
||||
Activity.startEvent(EVENT_NAME, DATA);
|
||||
Activity.startEvent(EVENT_NAME, DATA, {displayFields: ['someData']});
|
||||
jest.runOnlyPendingTimers();
|
||||
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
|
@ -41,7 +41,7 @@ describe('Activity', () => {
|
|||
const consoleMsg = console.log.mock.calls[0][0];
|
||||
expect(consoleMsg).toContain('START');
|
||||
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', () => {
|
||||
|
@ -61,7 +61,7 @@ describe('Activity', () => {
|
|||
const EVENT_NAME = 'EVENT_NAME';
|
||||
const DATA = {someData: 42};
|
||||
|
||||
const eventID = Activity.startEvent(EVENT_NAME, DATA);
|
||||
const eventID = Activity.startEvent(EVENT_NAME, DATA, {displayFields: ['someData']});
|
||||
Activity.endEvent(eventID);
|
||||
jest.runOnlyPendingTimers();
|
||||
|
||||
|
@ -71,7 +71,7 @@ describe('Activity', () => {
|
|||
const consoleMsg = console.log.mock.calls[1][0];
|
||||
expect(consoleMsg).toContain('END');
|
||||
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', () => {
|
||||
|
|
|
@ -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';
|
||||
|
||||
import type {EventOptions} from './Types';
|
||||
import type {Event} from './Types';
|
||||
import type {Event, EventData, Options} from './Types';
|
||||
|
||||
const chalk = require('chalk');
|
||||
const events = require('events');
|
||||
const formatData = require('./formatData');
|
||||
const normaliseEventData = require('./normaliseEventData');
|
||||
|
||||
let ENABLED = true;
|
||||
let UUID = 1;
|
||||
|
@ -23,24 +24,21 @@ let UUID = 1;
|
|||
const EVENT_INDEX: {[key: number]: Event} = Object.create(null);
|
||||
const EVENT_EMITTER = new events.EventEmitter();
|
||||
|
||||
function startEvent(
|
||||
name: string,
|
||||
data: any = null,
|
||||
options?: EventOptions = {},
|
||||
): number {
|
||||
function startEvent(name: string, data: EventData = {}, options: Options = {}): number {
|
||||
if (name == null) {
|
||||
throw new Error('No event name specified!');
|
||||
}
|
||||
|
||||
const id = UUID++;
|
||||
EVENT_INDEX[id] = {
|
||||
data: normaliseEventData(data),
|
||||
id,
|
||||
startTimeStamp: process.hrtime(),
|
||||
name,
|
||||
data,
|
||||
options,
|
||||
name,
|
||||
startTimeStamp: process.hrtime(),
|
||||
};
|
||||
logEvent(id, 'startEvent');
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -74,12 +72,11 @@ function logEvent(id: number, phase: 'startEvent' | 'endEvent'): void {
|
|||
const {
|
||||
name,
|
||||
durationMs,
|
||||
data,
|
||||
options,
|
||||
} = event;
|
||||
|
||||
const logTimeStamp = new Date().toLocaleString();
|
||||
const dataString = data ? ': ' + JSON.stringify(data) : '';
|
||||
const dataString = formatData(event);
|
||||
const {telemetric, silent} = options;
|
||||
|
||||
switch (phase) {
|
||||
|
@ -94,15 +91,15 @@ function logEvent(id: number, phase: 'startEvent' | 'endEvent'): void {
|
|||
if (!silent) {
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
console.log(
|
||||
chalk.dim(`[${logTimeStamp}] <END> ${name}${dataString} `) +
|
||||
(telemetric ? chalk.reset.cyan(`(${+durationMs}ms)`) : chalk.dim(`(${+durationMs}ms)`))
|
||||
chalk.dim(`[${logTimeStamp}] <END> ${name}${dataString}`) +
|
||||
(telemetric ? chalk.reset.cyan(` (${+durationMs}ms)`) : chalk.dim(` (${+durationMs}ms)`))
|
||||
);
|
||||
}
|
||||
forgetEvent(id);
|
||||
break;
|
||||
|
||||
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(
|
||||
'Transforming modules',
|
||||
null,
|
||||
{
|
||||
entry_point: entryFile,
|
||||
environment: dev ? 'dev' : 'prod',
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
entryPoint: entryFile,
|
||||
details: dev ? 'dev' : 'prod',
|
||||
},
|
||||
);
|
||||
const modulesByName = Object.create(null);
|
||||
|
|
|
@ -114,7 +114,9 @@ class Transformer {
|
|||
debug('transforming file', fileName);
|
||||
const transformEventId = Activity.startEvent(
|
||||
'Transforming file',
|
||||
fileName,
|
||||
{
|
||||
file_name: fileName,
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
silent: true,
|
||||
|
|
|
@ -499,7 +499,15 @@ class Server {
|
|||
_processAssetsRequest(req, res) {
|
||||
const urlObj = url.parse(decodeURI(req.url), true);
|
||||
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)
|
||||
.then(
|
||||
data => {
|
||||
|
@ -538,10 +546,11 @@ class Server {
|
|||
Activity.startEvent(
|
||||
'Updating existing bundle',
|
||||
{
|
||||
outdatedModules: outdated.size,
|
||||
outdated_modules: outdated.size,
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
displayFields: true,
|
||||
},
|
||||
);
|
||||
debug('Attempt to update existing bundle');
|
||||
|
@ -650,11 +659,11 @@ class Server {
|
|||
'Requesting bundle',
|
||||
{
|
||||
url: req.url,
|
||||
entry_point: options.entryFile,
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
entryPoint: options.entryFile,
|
||||
details: req.url,
|
||||
displayFields: ['url'],
|
||||
},
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in New Issue