mirror of https://github.com/status-im/metro.git
Instrumentation API
Reviewed By: cpojer Differential Revision: D4074413 fbshipit-source-id: fd2dff17168f426680fd9dc8456f8b1ae3f55318
This commit is contained in:
parent
cda4129dd3
commit
4d23c1f423
|
@ -10,11 +10,11 @@
|
|||
|
||||
require('../babelRegisterOnly')([/react-packager\/src/]);
|
||||
|
||||
var debug = require('debug');
|
||||
var Activity = require('./src/Activity');
|
||||
const debug = require('debug');
|
||||
const Logger = require('./src/Logger');
|
||||
|
||||
exports.createServer = createServer;
|
||||
exports.Activity = Activity;
|
||||
exports.Logger = Logger;
|
||||
exports.getOrderedDependencyPaths = function(options, bundleOptions) {
|
||||
var server = createNonPersistentServer(options);
|
||||
return server.getOrderedDependencyPaths(bundleOptions)
|
||||
|
@ -50,7 +50,7 @@ function createServer(options) {
|
|||
}
|
||||
|
||||
function createNonPersistentServer(options) {
|
||||
Activity.disable();
|
||||
Logger.disablePrinting();
|
||||
// Don't start the filewatcher or the cache.
|
||||
if (options.nonPersistent == null) {
|
||||
options.nonPersistent = true;
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* 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';
|
||||
|
||||
export type Options = {
|
||||
telemetric?: boolean,
|
||||
silent?: boolean,
|
||||
displayFields?: Array<string> | true,
|
||||
};
|
||||
|
||||
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,
|
||||
session: string,
|
||||
startTimeStamp: [number, number],
|
||||
};
|
|
@ -1,101 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest.disableAutomock();
|
||||
|
||||
var Activity = require('../');
|
||||
|
||||
describe('Activity', () => {
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
const origConsoleLog = console.log;
|
||||
|
||||
beforeEach(() => {
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
console.log = jest.fn();
|
||||
jest.runOnlyPendingTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
console.log = origConsoleLog;
|
||||
});
|
||||
|
||||
describe('startEvent', () => {
|
||||
it('writes the "START" phase of non-silent events to the console', () => {
|
||||
const EVENT_NAME = 'EVENT_NAME';
|
||||
const DATA = {someData: 42};
|
||||
|
||||
Activity.startEvent(EVENT_NAME, DATA, {displayFields: ['someData']});
|
||||
jest.runOnlyPendingTimers();
|
||||
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
expect(console.log.mock.calls.length).toBe(1);
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
const consoleMsg = console.log.mock.calls[0][0];
|
||||
expect(consoleMsg).toContain('START');
|
||||
expect(consoleMsg).toContain(EVENT_NAME);
|
||||
expect(consoleMsg).toContain('someData: 42');
|
||||
});
|
||||
|
||||
it('does not write the "START" phase of silent events to the console', () => {
|
||||
const EVENT_NAME = 'EVENT_NAME';
|
||||
const DATA = {someData: 42};
|
||||
|
||||
Activity.startEvent(EVENT_NAME, DATA, {silent: true});
|
||||
jest.runOnlyPendingTimers();
|
||||
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
expect(console.log.mock.calls.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('endEvent', () => {
|
||||
it('writes the "END" phase of non-silent events to the console', () => {
|
||||
const EVENT_NAME = 'EVENT_NAME';
|
||||
const DATA = {someData: 42};
|
||||
|
||||
const eventID = Activity.startEvent(EVENT_NAME, DATA, {displayFields: ['someData']});
|
||||
Activity.endEvent(eventID);
|
||||
jest.runOnlyPendingTimers();
|
||||
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
expect(console.log.mock.calls.length).toBe(2);
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
const consoleMsg = console.log.mock.calls[1][0];
|
||||
expect(consoleMsg).toContain('END');
|
||||
expect(consoleMsg).toContain(EVENT_NAME);
|
||||
expect(consoleMsg).toContain('someData: 42');
|
||||
});
|
||||
|
||||
it('does not write the "END" phase of silent events to the console', () => {
|
||||
const EVENT_NAME = 'EVENT_NAME';
|
||||
const DATA = {someData: 42};
|
||||
|
||||
const eventID = Activity.startEvent(EVENT_NAME, DATA, {silent: true});
|
||||
Activity.endEvent(eventID);
|
||||
jest.runOnlyPendingTimers();
|
||||
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
expect(console.log.mock.calls.length).toBe(0);
|
||||
});
|
||||
|
||||
it('throws when called with an invalid eventId', () => {
|
||||
expect(() => Activity.endEvent(42)).toThrow();
|
||||
});
|
||||
|
||||
it('throws when called with an expired eventId', () => {
|
||||
const eid = Activity.startEvent('', '');
|
||||
Activity.endEvent(eid);
|
||||
expect(() => {
|
||||
Activity.endEvent(eid);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* 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;
|
|
@ -1,125 +0,0 @@
|
|||
/**
|
||||
* 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, EventData, Options} from './Types';
|
||||
|
||||
const chalk = require('chalk');
|
||||
const events = require('events');
|
||||
const formatData = require('./formatData');
|
||||
const normaliseEventData = require('./normaliseEventData');
|
||||
const os = require('os');
|
||||
|
||||
let ENABLED = true;
|
||||
let UUID = 1;
|
||||
|
||||
const session = `${os.hostname()}-${Date.now()}`;
|
||||
const EVENT_INDEX: {[key: number]: Event} = Object.create(null);
|
||||
const EVENT_EMITTER = new events.EventEmitter();
|
||||
|
||||
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,
|
||||
options,
|
||||
name,
|
||||
session,
|
||||
startTimeStamp: process.hrtime(),
|
||||
};
|
||||
logEvent(id, 'startEvent');
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
function endEvent(id: number): void {
|
||||
const event = getEvent(id);
|
||||
const delta = process.hrtime(event.startTimeStamp);
|
||||
event.durationMs = Math.round((delta[0] * 1e9 + delta[1]) / 1e6);
|
||||
logEvent(id, 'endEvent');
|
||||
}
|
||||
|
||||
function getEvent(id: number): Event {
|
||||
if (!EVENT_INDEX[id]) {
|
||||
throw new Error(`Event(${id}) either ended or never started`);
|
||||
}
|
||||
|
||||
return EVENT_INDEX[id];
|
||||
}
|
||||
|
||||
function forgetEvent(id: number): void {
|
||||
delete EVENT_INDEX[id];
|
||||
}
|
||||
|
||||
function logEvent(id: number, phase: 'startEvent' | 'endEvent'): void {
|
||||
const event = getEvent(id);
|
||||
EVENT_EMITTER.emit(phase, id);
|
||||
|
||||
if (!ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
name,
|
||||
durationMs,
|
||||
options,
|
||||
} = event;
|
||||
|
||||
const logTimeStamp = new Date().toLocaleString();
|
||||
const dataString = formatData(event);
|
||||
const {telemetric, silent} = options;
|
||||
|
||||
switch (phase) {
|
||||
case 'startEvent':
|
||||
if (!silent) {
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
console.log(chalk.dim(`[${logTimeStamp}] <START> ${name}${dataString}`));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'endEvent':
|
||||
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)`))
|
||||
);
|
||||
}
|
||||
forgetEvent(id);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected event phase "${phase}"!`);
|
||||
}
|
||||
}
|
||||
|
||||
function enable(): void {
|
||||
ENABLED = true;
|
||||
}
|
||||
|
||||
function disable(): void {
|
||||
ENABLED = false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
startEvent,
|
||||
endEvent,
|
||||
getEvent,
|
||||
forgetEvent,
|
||||
enable,
|
||||
disable,
|
||||
eventEmitter: EVENT_EMITTER,
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
* 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;
|
|
@ -24,7 +24,7 @@ jest
|
|||
.mock('../Bundle')
|
||||
.mock('../PrepackBundle')
|
||||
.mock('../HMRBundle')
|
||||
.mock('../../Activity')
|
||||
.mock('../../Logger')
|
||||
.mock('../../lib/declareOpts');
|
||||
|
||||
var Bundler = require('../');
|
||||
|
|
|
@ -19,7 +19,6 @@ const Resolver = require('../Resolver');
|
|||
const Bundle = require('./Bundle');
|
||||
const HMRBundle = require('./HMRBundle');
|
||||
const PrepackBundle = require('./PrepackBundle');
|
||||
const Activity = require('../Activity');
|
||||
const ModuleTransport = require('../lib/ModuleTransport');
|
||||
const declareOpts = require('../lib/declareOpts');
|
||||
const imageSize = require('image-size');
|
||||
|
@ -29,6 +28,13 @@ const sizeOf = Promise.denodeify(imageSize);
|
|||
|
||||
const noop = () => {};
|
||||
|
||||
const {
|
||||
createActionStartEntry,
|
||||
createActionEndEntry,
|
||||
log,
|
||||
print,
|
||||
} = require('../Logger');
|
||||
|
||||
const validateOpts = declareOpts({
|
||||
projectRoots: {
|
||||
type: 'array',
|
||||
|
@ -364,16 +370,13 @@ class Bundler {
|
|||
finalizeBundle = noop,
|
||||
onProgress = noop,
|
||||
}) {
|
||||
const findEventId = Activity.startEvent(
|
||||
'Transforming modules',
|
||||
{
|
||||
const transformingFilesLogEntry =
|
||||
print(log(createActionStartEntry({
|
||||
action_name: 'Transforming files',
|
||||
entry_point: entryFile,
|
||||
environment: dev ? 'dev' : 'prod',
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
},
|
||||
);
|
||||
})));
|
||||
|
||||
const modulesByName = Object.create(null);
|
||||
|
||||
if (!resolutionResponse) {
|
||||
|
@ -392,7 +395,7 @@ class Bundler {
|
|||
return Promise.resolve(resolutionResponse).then(response => {
|
||||
bundle.setRamGroups(response.transformOptions.transform.ramGroups);
|
||||
|
||||
Activity.endEvent(findEventId);
|
||||
print(log(createActionEndEntry(transformingFilesLogEntry)));
|
||||
onResolutionResponse(response);
|
||||
|
||||
// get entry file complete path (`entryFile` is relative to roots)
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
const Activity = require('../Activity');
|
||||
const Promise = require('promise');
|
||||
|
||||
const declareOpts = require('../lib/declareOpts');
|
||||
const os = require('os');
|
||||
const util = require('util');
|
||||
|
@ -112,21 +112,10 @@ class Transformer {
|
|||
return Promise.reject(new Error('No transform module'));
|
||||
}
|
||||
debug('transforming file', fileName);
|
||||
const transformEventId = Activity.startEvent(
|
||||
'Transforming file',
|
||||
{
|
||||
file_name: fileName,
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
silent: true,
|
||||
},
|
||||
);
|
||||
return this
|
||||
._transform(this._transformModulePath, fileName, code, options)
|
||||
.then(result => {
|
||||
debug('done transforming file', fileName);
|
||||
Activity.endEvent(transformEventId);
|
||||
return result;
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* 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';
|
||||
|
||||
export type ActionLogEntryData = {
|
||||
action_name: string,
|
||||
};
|
||||
|
||||
export type ActionStartLogEntry = {
|
||||
action_name?: string,
|
||||
action_phase?: string,
|
||||
log_entry_label: string,
|
||||
log_session?: string,
|
||||
start_timestamp?: [number, number],
|
||||
};
|
||||
|
||||
export type LogEntry = {
|
||||
action_name?: string,
|
||||
action_phase?: string,
|
||||
duration_ms?: number,
|
||||
log_entry_label: string,
|
||||
log_session?: string,
|
||||
start_timestamp?: [number, number],
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* eslint-disable no-console-disallow
|
||||
*
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest.disableAutomock();
|
||||
|
||||
const {
|
||||
createEntry,
|
||||
createActionStartEntry,
|
||||
createActionEndEntry,
|
||||
enablePrinting,
|
||||
} = require('../');
|
||||
|
||||
describe('Logger', () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
beforeEach(() => {
|
||||
console.log = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.log = originalConsoleLog;
|
||||
enablePrinting();
|
||||
});
|
||||
|
||||
it('creates simple log entries', () => {
|
||||
const logEntry = createEntry('Test');
|
||||
expect(logEntry).toEqual({
|
||||
log_entry_label: 'Test',
|
||||
log_session: jasmine.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('creates action start log entries', () => {
|
||||
const actionStartLogEntry = createActionStartEntry('Test');
|
||||
expect(actionStartLogEntry).toEqual({
|
||||
action_name: 'Test',
|
||||
action_phase: 'start',
|
||||
log_entry_label: 'Test',
|
||||
log_session: jasmine.any(String),
|
||||
start_timestamp: jasmine.any(Object),
|
||||
});
|
||||
});
|
||||
|
||||
it('creates action end log entries', () => {
|
||||
const actionEndLogEntry = createActionEndEntry(createActionStartEntry('Test'));
|
||||
expect(actionEndLogEntry).toEqual({
|
||||
action_name: 'Test',
|
||||
action_phase: 'end',
|
||||
duration_ms: jasmine.any(Number),
|
||||
log_entry_label: 'Test',
|
||||
log_session: jasmine.any(String),
|
||||
start_timestamp: jasmine.any(Object),
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* 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 {
|
||||
ActionLogEntryData,
|
||||
ActionStartLogEntry,
|
||||
LogEntry,
|
||||
} from './Types';
|
||||
|
||||
const {EventEmitter} = require('events');
|
||||
|
||||
const chalk = require('chalk');
|
||||
const os = require('os');
|
||||
|
||||
let PRINT_LOG_ENTRIES = true;
|
||||
const log_session = `${os.hostname()}-${Date.now()}`;
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
function on(event: string, handler: (logEntry: LogEntry) => void): void {
|
||||
eventEmitter.on(event, handler);
|
||||
}
|
||||
|
||||
function createEntry(data: LogEntry | string): LogEntry {
|
||||
const logEntry = typeof data === 'string' ? {log_entry_label: data} : data;
|
||||
|
||||
return {
|
||||
...logEntry,
|
||||
log_session,
|
||||
};
|
||||
}
|
||||
|
||||
function createActionStartEntry(data: ActionLogEntryData | string): LogEntry {
|
||||
const logEntry = typeof data === 'string' ? {action_name: data} : data;
|
||||
const {action_name} = logEntry;
|
||||
|
||||
return createEntry({
|
||||
...logEntry,
|
||||
action_name,
|
||||
action_phase: 'start',
|
||||
log_entry_label: action_name,
|
||||
start_timestamp: process.hrtime(),
|
||||
});
|
||||
}
|
||||
|
||||
function createActionEndEntry(logEntry: ActionStartLogEntry): LogEntry {
|
||||
const {
|
||||
action_name,
|
||||
action_phase,
|
||||
start_timestamp,
|
||||
} = logEntry;
|
||||
|
||||
if (action_phase !== 'start' || !Array.isArray(start_timestamp)) {
|
||||
throw new Error('Action has not started or has already ended');
|
||||
}
|
||||
|
||||
const timeDelta = process.hrtime(start_timestamp);
|
||||
const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6);
|
||||
|
||||
return createEntry({
|
||||
...logEntry,
|
||||
action_name,
|
||||
action_phase: 'end',
|
||||
duration_ms,
|
||||
log_entry_label: action_name,
|
||||
});
|
||||
}
|
||||
|
||||
function log(logEntry: LogEntry): LogEntry {
|
||||
eventEmitter.emit('log', logEntry);
|
||||
return logEntry;
|
||||
}
|
||||
|
||||
function print(
|
||||
logEntry: LogEntry,
|
||||
printFields?: Array<string> = [],
|
||||
): LogEntry {
|
||||
if (!PRINT_LOG_ENTRIES) {
|
||||
return logEntry;
|
||||
}
|
||||
|
||||
const {log_entry_label, action_phase, duration_ms} = logEntry;
|
||||
const timeStamp = new Date().toLocaleString();
|
||||
const logEntryDataList = [];
|
||||
let logEntryString, logEntryDataString;
|
||||
|
||||
for (let i = 0, len = printFields.length; i < len; i++) {
|
||||
const field = printFields[i];
|
||||
const value = logEntry[field];
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
logEntryDataList.push(`${field}: ${value.toString()}`);
|
||||
}
|
||||
|
||||
logEntryDataString = logEntryDataList.join(' | ');
|
||||
|
||||
if (logEntryDataString) {
|
||||
logEntryDataString = ` ${logEntryDataString}`;
|
||||
}
|
||||
|
||||
switch (action_phase) {
|
||||
case 'start':
|
||||
logEntryString = chalk.dim(`[${timeStamp}] <START> ${log_entry_label}${logEntryDataString}`);
|
||||
break;
|
||||
case 'end':
|
||||
logEntryString = chalk.dim(`[${timeStamp}] <END> ${log_entry_label}${logEntryDataString}`) +
|
||||
chalk.cyan(` (${+duration_ms}ms)`);
|
||||
break;
|
||||
default:
|
||||
logEntryString = chalk.dim(`[${timeStamp}] ${log_entry_label}${logEntryDataString}`);
|
||||
break;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console-disallow
|
||||
console.log(logEntryString);
|
||||
|
||||
return logEntry;
|
||||
}
|
||||
|
||||
function enablePrinting(): void {
|
||||
PRINT_LOG_ENTRIES = true;
|
||||
}
|
||||
|
||||
function disablePrinting(): void {
|
||||
PRINT_LOG_ENTRIES = false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
on,
|
||||
createEntry,
|
||||
createActionStartEntry,
|
||||
createActionEndEntry,
|
||||
log,
|
||||
print,
|
||||
enablePrinting,
|
||||
disablePrinting,
|
||||
};
|
|
@ -9,7 +9,6 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
const Activity = require('../Activity');
|
||||
const DependencyGraph = require('../node-haste');
|
||||
|
||||
const declareOpts = require('../lib/declareOpts');
|
||||
|
@ -89,7 +88,6 @@ class Resolver {
|
|||
const opts = validateOpts(options);
|
||||
|
||||
this._depGraph = new DependencyGraph({
|
||||
activity: Activity,
|
||||
roots: opts.projectRoots,
|
||||
assetRoots_DEPRECATED: opts.assetRoots,
|
||||
assetExts: opts.assetExts,
|
||||
|
|
|
@ -19,7 +19,7 @@ jest.setMock('worker-farm', function() { return () => {}; })
|
|||
.mock('../../AssetServer')
|
||||
.mock('../../lib/declareOpts')
|
||||
.mock('../../node-haste')
|
||||
.mock('../../Activity');
|
||||
.mock('../../Logger');
|
||||
|
||||
let FileWatcher;
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
const Activity = require('../Activity');
|
||||
const AssetServer = require('../AssetServer');
|
||||
const FileWatcher = require('../node-haste').FileWatcher;
|
||||
const getPlatformExtension = require('../node-haste').getPlatformExtension;
|
||||
|
@ -26,6 +25,13 @@ const url = require('url');
|
|||
|
||||
const debug = require('debug')('ReactNativePackager:Server');
|
||||
|
||||
const {
|
||||
createActionStartEntry,
|
||||
createActionEndEntry,
|
||||
log,
|
||||
print,
|
||||
} = require('../Logger');
|
||||
|
||||
function debounceAndBatch(fn, delay) {
|
||||
let timeout, args = [];
|
||||
return (value) => {
|
||||
|
@ -496,15 +502,13 @@ 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',
|
||||
{
|
||||
|
||||
const processingAssetRequestLogEntry =
|
||||
print(log(createActionStartEntry({
|
||||
action_name: 'Processing asset request',
|
||||
asset: assetPath[1],
|
||||
},
|
||||
{
|
||||
displayFields: true,
|
||||
},
|
||||
);
|
||||
})), ['asset']);
|
||||
|
||||
this._assetServer.get(assetPath[1], urlObj.query.platform)
|
||||
.then(
|
||||
data => {
|
||||
|
@ -518,7 +522,9 @@ class Server {
|
|||
res.writeHead('404');
|
||||
res.end('Asset not found');
|
||||
}
|
||||
).done(() => Activity.endEvent(assetEvent));
|
||||
).done(() => {
|
||||
print(log(createActionEndEntry(processingAssetRequestLogEntry)), ['asset']);
|
||||
});
|
||||
}
|
||||
|
||||
optionsHash(options) {
|
||||
|
@ -539,18 +545,15 @@ class Server {
|
|||
const deps = bundleDeps.get(bundle);
|
||||
const {dependencyPairs, files, idToIndex, outdated} = deps;
|
||||
if (outdated.size) {
|
||||
const updateExistingBundleEventId =
|
||||
Activity.startEvent(
|
||||
'Updating existing bundle',
|
||||
{
|
||||
|
||||
const updatingExistingBundleLogEntry =
|
||||
print(log(createActionStartEntry({
|
||||
action_name: 'Updating existing bundle',
|
||||
outdated_modules: outdated.size,
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
displayFields: true,
|
||||
},
|
||||
);
|
||||
})), ['outdated_modules']);
|
||||
|
||||
debug('Attempt to update existing bundle');
|
||||
|
||||
const changedModules =
|
||||
Array.from(outdated, this.getModuleForPath, this);
|
||||
deps.outdated = new Set();
|
||||
|
@ -604,8 +607,13 @@ class Server {
|
|||
}
|
||||
|
||||
bundle.invalidateSource();
|
||||
|
||||
print(
|
||||
log(createActionEndEntry(updatingExistingBundleLogEntry)),
|
||||
['outdated_modules'],
|
||||
);
|
||||
|
||||
debug('Successfully updated existing bundle');
|
||||
Activity.endEvent(updateExistingBundleEventId);
|
||||
return bundle;
|
||||
});
|
||||
}).catch(e => {
|
||||
|
@ -652,17 +660,12 @@ class Server {
|
|||
}
|
||||
|
||||
const options = this._getOptionsFromUrl(req.url);
|
||||
const startReqEventId = Activity.startEvent(
|
||||
'Requesting bundle',
|
||||
{
|
||||
url: req.url,
|
||||
const requestingBundleLogEntry =
|
||||
print(log(createActionStartEntry({
|
||||
action_name: 'Requesting bundle',
|
||||
bundle_url: req.url,
|
||||
entry_point: options.entryFile,
|
||||
},
|
||||
{
|
||||
telemetric: true,
|
||||
displayFields: ['url'],
|
||||
},
|
||||
);
|
||||
})), ['bundle_url']);
|
||||
|
||||
let consoleProgress = () => {};
|
||||
if (process.stdout.isTTY && !this._opts.silent) {
|
||||
|
@ -705,7 +708,7 @@ class Server {
|
|||
mres.end(bundleSource);
|
||||
}
|
||||
debug('Finished response');
|
||||
Activity.endEvent(startReqEventId);
|
||||
print(log(createActionEndEntry(requestingBundleLogEntry)), ['bundle_url']);
|
||||
} else if (requestType === 'map') {
|
||||
let sourceMap = p.getSourceMap({
|
||||
minify: options.minify,
|
||||
|
@ -718,12 +721,12 @@ class Server {
|
|||
|
||||
mres.setHeader('Content-Type', 'application/json');
|
||||
mres.end(sourceMap);
|
||||
Activity.endEvent(startReqEventId);
|
||||
print(log(createActionEndEntry(requestingBundleLogEntry)), ['bundle_url']);
|
||||
} else if (requestType === 'assets') {
|
||||
const assetsList = JSON.stringify(p.getAssets());
|
||||
mres.setHeader('Content-Type', 'application/json');
|
||||
mres.end(assetsList);
|
||||
Activity.endEvent(startReqEventId);
|
||||
print(log(createActionEndEntry(requestingBundleLogEntry)), ['bundle_url']);
|
||||
}
|
||||
},
|
||||
error => this._handleError(mres, this.optionsHash(options), error)
|
||||
|
@ -735,13 +738,9 @@ class Server {
|
|||
}
|
||||
|
||||
_symbolicate(req, res) {
|
||||
const startReqEventId = Activity.startEvent(
|
||||
'Symbolicating',
|
||||
null,
|
||||
{
|
||||
telemetric: true,
|
||||
},
|
||||
);
|
||||
const symbolicatingLogEntry =
|
||||
print(log(createActionStartEntry('Symbolicating')));
|
||||
|
||||
new Promise.resolve(req.rawBody).then(body => {
|
||||
const stack = JSON.parse(body).stack;
|
||||
|
||||
|
@ -795,7 +794,7 @@ class Server {
|
|||
res.end(JSON.stringify({error: error.message}));
|
||||
}
|
||||
).done(() => {
|
||||
Activity.endEvent(startReqEventId);
|
||||
print(log(createActionEndEntry(symbolicatingLogEntry)));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
|
||||
jest.autoMockOff();
|
||||
jest.useRealTimers();
|
||||
jest.mock('fs');
|
||||
jest
|
||||
.mock('fs')
|
||||
.mock('../../Logger');
|
||||
|
||||
// This is an ugly hack:
|
||||
// * jest-haste-map uses `find` for fast file system crawling which won't work
|
||||
|
|
|
@ -27,6 +27,13 @@ interface FileWatcher {
|
|||
on(event: 'all', handler: (type: string, filePath: string, rootPath: string, fstat: fs.Stats) => void): void,
|
||||
}
|
||||
|
||||
const {
|
||||
createActionStartEntry,
|
||||
createActionEndEntry,
|
||||
log,
|
||||
print,
|
||||
} = require('../Logger');
|
||||
|
||||
class Fastfs extends EventEmitter {
|
||||
|
||||
_name: string;
|
||||
|
@ -62,18 +69,10 @@ class Fastfs extends EventEmitter {
|
|||
return new File(root, true);
|
||||
});
|
||||
this._fastPaths = Object.create(null);
|
||||
this._activity = activity;
|
||||
|
||||
let fastfsActivity;
|
||||
if (activity) {
|
||||
fastfsActivity = activity.startEvent(
|
||||
'Building in-memory fs for ' + this._name,
|
||||
null,
|
||||
{
|
||||
telemetric: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
const buildingInMemoryFSLogEntry =
|
||||
print(log(createActionStartEntry('Building in-memory fs for ' + this._name)));
|
||||
|
||||
files.forEach(filePath => {
|
||||
const root = this._getRoot(filePath);
|
||||
if (root) {
|
||||
|
@ -88,9 +87,8 @@ class Fastfs extends EventEmitter {
|
|||
}
|
||||
}
|
||||
});
|
||||
if (activity) {
|
||||
activity.endEvent(fastfsActivity);
|
||||
}
|
||||
|
||||
print(log(createActionEndEntry(buildingInMemoryFSLogEntry)));
|
||||
|
||||
if (this._fileWatcher) {
|
||||
this._fileWatcher.on('all', this._processFileChange.bind(this));
|
||||
|
|
|
@ -33,14 +33,15 @@ const util = require('util');
|
|||
|
||||
const ERROR_BUILDING_DEP_GRAPH = 'DependencyGraphError';
|
||||
|
||||
const defaultActivity = {
|
||||
startEvent: () => {},
|
||||
endEvent: () => {},
|
||||
};
|
||||
const {
|
||||
createActionStartEntry,
|
||||
createActionEndEntry,
|
||||
log,
|
||||
print,
|
||||
} = require('../Logger');
|
||||
|
||||
class DependencyGraph {
|
||||
constructor({
|
||||
activity,
|
||||
roots,
|
||||
ignoreFilePath,
|
||||
fileWatcher,
|
||||
|
@ -65,7 +66,6 @@ class DependencyGraph {
|
|||
resetCache,
|
||||
}) {
|
||||
this._opts = {
|
||||
activity: activity || defaultActivity,
|
||||
roots,
|
||||
ignoreFilePath: ignoreFilePath || (() => {}),
|
||||
fileWatcher,
|
||||
|
@ -116,14 +116,8 @@ class DependencyGraph {
|
|||
});
|
||||
|
||||
this._loading = haste.build().then(hasteMap => {
|
||||
const {activity} = this._opts;
|
||||
const depGraphActivity = activity.startEvent(
|
||||
'Initializing Packager',
|
||||
null,
|
||||
{
|
||||
telemetric: true,
|
||||
},
|
||||
);
|
||||
const initializingPackagerLogEntry =
|
||||
print(log(createActionStartEntry('Initializing Packager')));
|
||||
|
||||
const hasteFSFiles = hasteMap.hasteFS.getAllFiles();
|
||||
|
||||
|
@ -134,7 +128,6 @@ class DependencyGraph {
|
|||
hasteFSFiles,
|
||||
{
|
||||
ignore: this._opts.ignoreFilePath,
|
||||
activity: activity,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -181,17 +174,13 @@ class DependencyGraph {
|
|||
}
|
||||
});
|
||||
|
||||
const hasteActivity = activity.startEvent(
|
||||
'Building Haste Map',
|
||||
null,
|
||||
{
|
||||
telemetric: true,
|
||||
},
|
||||
);
|
||||
const buildingHasteMapLogEntry =
|
||||
print(log(createActionStartEntry('Building Haste Map')));
|
||||
|
||||
return this._hasteMap.build().then(
|
||||
map => {
|
||||
activity.endEvent(hasteActivity);
|
||||
activity.endEvent(depGraphActivity);
|
||||
print(log(createActionEndEntry(buildingHasteMapLogEntry)));
|
||||
print(log(createActionEndEntry(initializingPackagerLogEntry)));
|
||||
return map;
|
||||
},
|
||||
err => {
|
||||
|
|
Loading…
Reference in New Issue