mirror of
https://github.com/embarklabs/embark.git
synced 2025-01-11 14:24:24 +00:00
Merge pull request #143 from status-im/bug_fix/backend_tab/logs-blockchain-broken
Embark blockchain logs when running standalone
This commit is contained in:
commit
7461d0b4a8
@ -32,7 +32,7 @@ class EmbarkController {
|
|||||||
|
|
||||||
blockchain(env, client) {
|
blockchain(env, client) {
|
||||||
this.context = [constants.contexts.blockchain];
|
this.context = [constants.contexts.blockchain];
|
||||||
return require('../lib/modules/blockchain_process/blockchain.js')(this.config.blockchainConfig, client, env).run();
|
return require('../lib/modules/blockchain_process/blockchain.js')(this.config.blockchainConfig, client, env, null, null, this.logger, this.events, true).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator(options) {
|
simulator(options) {
|
||||||
@ -131,7 +131,8 @@ class EmbarkController {
|
|||||||
|
|
||||||
engine.startService("processManager");
|
engine.startService("processManager");
|
||||||
engine.startService("coreProcess");
|
engine.startService("coreProcess");
|
||||||
engine.startService("loggerApi");
|
engine.startService("embarkListener");
|
||||||
|
engine.startService("blockchainListener");
|
||||||
engine.startService("serviceMonitor");
|
engine.startService("serviceMonitor");
|
||||||
engine.startService("libraryManager");
|
engine.startService("libraryManager");
|
||||||
engine.startService("codeRunner");
|
engine.startService("codeRunner");
|
||||||
|
@ -39,18 +39,10 @@ class HomeContainer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTab(processName = EMBARK_PROCESS_NAME) {
|
updateTab(processName = EMBARK_PROCESS_NAME) {
|
||||||
if (!this.isEmbark()){
|
|
||||||
this.props.stopProcessLogs(this.state.activeProcess)
|
this.props.stopProcessLogs(this.state.activeProcess)
|
||||||
}
|
|
||||||
|
|
||||||
if (processName === EMBARK_PROCESS_NAME) {
|
|
||||||
if (this.props.processLogs.length === 0) {
|
|
||||||
this.props.fetchProcessLogs(processName, LOG_LIMIT);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.props.fetchProcessLogs(processName, LOG_LIMIT);
|
this.props.fetchProcessLogs(processName, LOG_LIMIT);
|
||||||
this.props.listenToProcessLogs(processName);
|
this.props.listenToProcessLogs(processName);
|
||||||
}
|
|
||||||
|
|
||||||
this.props.fetchContracts();
|
this.props.fetchContracts();
|
||||||
this.setState({activeProcess: processName});
|
this.setState({activeProcess: processName});
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"gasLimit": 6000000
|
"gasLimit": 6000000
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"logPath": ".embark/logs/"
|
"logPath": ".embark/logs/",
|
||||||
|
"maxLogLength": 1500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,9 @@ class Engine {
|
|||||||
"codeCoverage": this.codeCoverageService,
|
"codeCoverage": this.codeCoverageService,
|
||||||
"scaffolding": this.scaffoldingService,
|
"scaffolding": this.scaffoldingService,
|
||||||
"coreProcess": this.coreProcessService,
|
"coreProcess": this.coreProcessService,
|
||||||
"loggerApi": this.loggerApiService
|
"processApi": this.processApiService,
|
||||||
|
"blockchainListener": this.blockchainListenerService,
|
||||||
|
"embarkListener": this.embarkListenerService
|
||||||
};
|
};
|
||||||
|
|
||||||
let service = services[serviceName];
|
let service = services[serviceName];
|
||||||
@ -92,14 +94,24 @@ class Engine {
|
|||||||
return service.apply(this, [options]);
|
return service.apply(this, [options]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
embarkListenerService(_options){
|
||||||
|
this.registerModule('embark_listener');
|
||||||
|
}
|
||||||
|
|
||||||
|
blockchainListenerService(_options){
|
||||||
|
this.registerModule('blockchain_listener', {
|
||||||
|
ipc: this.ipc
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
coreProcessService(_options){
|
coreProcessService(_options){
|
||||||
this.registerModule('core_process', {
|
this.registerModule('core_process', {
|
||||||
events: this.events
|
events: this.events
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loggerApiService(_options){
|
processApiService(_options){
|
||||||
this.registerModule('logger_api', {
|
this.registerModule('process_api', {
|
||||||
logger: this.logger
|
logger: this.logger
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,6 @@ class Logger {
|
|||||||
this.logFunction = options.logFunction || console.log;
|
this.logFunction = options.logFunction || console.log;
|
||||||
this.logFile = options.logFile;
|
this.logFile = options.logFile;
|
||||||
this.context = options.context;
|
this.context = options.context;
|
||||||
// Use a default logFile if none is specified in the cli,
|
|
||||||
// in the format .embark/logs/embark_<context>.log.
|
|
||||||
if (!this.logFile) {
|
|
||||||
this.logFile = fs.dappPath(`${constants.logs.logPath}embark_${this.context}.log`);
|
|
||||||
// creates log dir if it doesn't exist, and overwrites existing log file if it exists
|
|
||||||
fs.outputFileSync(this.logFile, '');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,6 +79,9 @@ Logger.prototype.registerAPICall = function (plugins) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Logger.prototype.writeToFile = function (_txt) {
|
Logger.prototype.writeToFile = function (_txt) {
|
||||||
|
if (!this.logFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formattedDate = [`[${date.format(new Date(), DATE_FORMAT)}]`]; // adds a timestamp to the logs in the logFile
|
const formattedDate = [`[${date.format(new Date(), DATE_FORMAT)}]`]; // adds a timestamp to the logs in the logFile
|
||||||
fs.appendFileSync(this.logFile, "\n" + formattedDate.concat(Array.from(arguments)).join(' '));
|
fs.appendFileSync(this.logFile, "\n" + formattedDate.concat(Array.from(arguments)).join(' '));
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const child_process = require('child_process');
|
const child_process = require('child_process');
|
||||||
const constants = require('../../constants');
|
const constants = require('../../constants');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const utils = require('../../utils/utils');
|
const ProcessLogsApi = require('../../modules/process_logs_api');
|
||||||
|
|
||||||
let processCount = 1;
|
let processCount = 1;
|
||||||
class ProcessLauncher {
|
class ProcessLauncher {
|
||||||
@ -30,12 +30,10 @@ class ProcessLauncher {
|
|||||||
this.exitCallback = options.exitCallback;
|
this.exitCallback = options.exitCallback;
|
||||||
this.embark = options.embark;
|
this.embark = options.embark;
|
||||||
this.logs = [];
|
this.logs = [];
|
||||||
|
this.processLogsApi = new ProcessLogsApi({embark: this.embark, processName: this.name, silent: this.silent});
|
||||||
|
|
||||||
this.subscriptions = {};
|
this.subscriptions = {};
|
||||||
this._subscribeToMessages();
|
this._subscribeToMessages();
|
||||||
if (this.embark) {
|
|
||||||
this._registerAsPlugin();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_isDebug() {
|
_isDebug() {
|
||||||
@ -51,7 +49,7 @@ class ProcessLauncher {
|
|||||||
self.logger.error(msg.error);
|
self.logger.error(msg.error);
|
||||||
}
|
}
|
||||||
if (msg.result === constants.process.log) {
|
if (msg.result === constants.process.log) {
|
||||||
return self._handleLog(msg);
|
return self.processLogsApi.logHandler.handleLog(msg);
|
||||||
}
|
}
|
||||||
if (msg.event) {
|
if (msg.event) {
|
||||||
return self._handleEvent(msg);
|
return self._handleEvent(msg);
|
||||||
@ -69,68 +67,6 @@ class ProcessLauncher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerAsPlugin() {
|
|
||||||
const self = this;
|
|
||||||
const apiRoute = '/embark-api/process-logs/' + self.name;
|
|
||||||
self.embark.registerAPICall(
|
|
||||||
'ws',
|
|
||||||
apiRoute,
|
|
||||||
(ws, _req) => {
|
|
||||||
self.events.on('process-log-' + self.name, function(id, log) {
|
|
||||||
ws.send(JSON.stringify(Object.assign(log, {id})), () => {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
self.embark.registerAPICall(
|
|
||||||
'get',
|
|
||||||
apiRoute,
|
|
||||||
(req, res) => {
|
|
||||||
let limit = parseInt(req.query.limit, 10);
|
|
||||||
if(!Number.isInteger(limit)) limit = 0;
|
|
||||||
const result = self.logs.map((log, id) => Object.assign(log, {id})).slice(limit);
|
|
||||||
res.send(JSON.stringify(result));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translates logs from the child process to the logger
|
|
||||||
_handleLog(msg) {
|
|
||||||
// Sometimes messages come in with line breaks, so we need to break them up accordingly.
|
|
||||||
let processedMessages = [];
|
|
||||||
|
|
||||||
// Ensure that `msg.message` is an array, so we process this consistently. Sometimes it
|
|
||||||
// is an Array, sometimes it is a string.
|
|
||||||
if(typeof msg.message === 'string') {
|
|
||||||
processedMessages = [msg.message];
|
|
||||||
} else {
|
|
||||||
msg.message.forEach(message => {
|
|
||||||
let lines = message.split("\n");
|
|
||||||
lines.forEach(line => processedMessages.push(line));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const timestamp = new Date().getTime();
|
|
||||||
|
|
||||||
processedMessages.forEach((message) => {
|
|
||||||
const log = {
|
|
||||||
msg: message,
|
|
||||||
msg_clear: message.stripColors,
|
|
||||||
logLevel: msg.logLevel,
|
|
||||||
name: this.name,
|
|
||||||
timestamp
|
|
||||||
};
|
|
||||||
const id = this.logs.push(log) - 1;
|
|
||||||
this.events.emit(`process-log-${this.name}`, id, log);
|
|
||||||
if (this.silent && msg.type !== 'error') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.logger[msg.type]) {
|
|
||||||
return this.logger[msg.type](utils.normalizeInput(message));
|
|
||||||
}
|
|
||||||
this.logger.debug(utils.normalizeInput(message));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle event calls from the child process
|
// Handle event calls from the child process
|
||||||
_handleEvent(msg) {
|
_handleEvent(msg) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
45
lib/modules/blockchain_listener/index.js
Normal file
45
lib/modules/blockchain_listener/index.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const ProcessLogsApi = require('../../modules/process_logs_api');
|
||||||
|
|
||||||
|
const PROCESS_NAME = 'blockchain';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BlockchainListener has two functions:
|
||||||
|
* 1. Register API endpoints (HTTP GET and WS) to retrieve blockchain logs
|
||||||
|
* when in standalone mode (ie `embark blockchain`).
|
||||||
|
* 2. Listen to log events from the IPC connection (to `embark blockchain`)
|
||||||
|
* and ensure they are processed through the LogHandler.
|
||||||
|
*/
|
||||||
|
class BlockchainListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Plugin} embark Embark module plugin object
|
||||||
|
* @param {Object} options Options object containing:
|
||||||
|
* - {Ipc} ipc IPC started by embark (in server role) used for communication
|
||||||
|
* with the standalone blockchain process.
|
||||||
|
*/
|
||||||
|
constructor(embark, {ipc}) {
|
||||||
|
this.embark = embark;
|
||||||
|
this.events = embark.events;
|
||||||
|
this.logger = embark.logger;
|
||||||
|
this.ipc = ipc;
|
||||||
|
this.processLogsApi = new ProcessLogsApi({embark: this.embark, processName: PROCESS_NAME, silent: true});
|
||||||
|
|
||||||
|
if (this.ipc.isServer()) {
|
||||||
|
this._listenToBlockchainLogs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens to log events emitted by the standalone blockchain and ensures
|
||||||
|
* they are processed through the LogHandler.
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
_listenToBlockchainLogs() {
|
||||||
|
this.ipc.on('blockchain:log', ({logLevel, message}) => {
|
||||||
|
this.processLogsApi.logHandler.handleLog({logLevel, message});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BlockchainListener;
|
@ -1,27 +1,31 @@
|
|||||||
const async = require('async');
|
const async = require('async');
|
||||||
const {spawn, exec} = require('child_process');
|
const {spawn, exec} = require('child_process');
|
||||||
|
|
||||||
const fs = require('../../core/fs.js');
|
const fs = require('../../core/fs.js');
|
||||||
const constants = require('../../constants.json');
|
const constants = require('../../constants.json');
|
||||||
const utils = require('../../utils/utils.js');
|
const utils = require('../../utils/utils.js');
|
||||||
|
|
||||||
const GethClient = require('./gethClient.js');
|
const GethClient = require('./gethClient.js');
|
||||||
const ParityClient = require('./parityClient.js');
|
const ParityClient = require('./parityClient.js');
|
||||||
const DevFunds = require('./dev_funds.js');
|
const DevFunds = require('./dev_funds.js');
|
||||||
|
|
||||||
const proxy = require('./proxy');
|
const proxy = require('./proxy');
|
||||||
const Ipc = require('../../core/ipc');
|
const Ipc = require('../../core/ipc');
|
||||||
|
|
||||||
const {defaultHost, dockerHostSwap} = require('../../utils/host');
|
const {defaultHost, dockerHostSwap} = require('../../utils/host');
|
||||||
|
const Logger = require('../../core/logger');
|
||||||
|
|
||||||
/*eslint complexity: ["error", 36]*/
|
// time between IPC connection attmpts (in ms)
|
||||||
|
const IPC_CONNECT_INTERVAL = 2000;
|
||||||
|
|
||||||
|
/*eslint complexity: ["error", 38]*/
|
||||||
var Blockchain = function(userConfig, clientClass) {
|
var Blockchain = function(userConfig, clientClass) {
|
||||||
this.userConfig = userConfig;
|
this.userConfig = userConfig;
|
||||||
this.env = userConfig.env || 'development';
|
this.env = userConfig.env || 'development';
|
||||||
this.isDev = userConfig.isDev;
|
this.isDev = userConfig.isDev;
|
||||||
this.onReadyCallback = userConfig.onReadyCallback || (() => {});
|
this.onReadyCallback = userConfig.onReadyCallback || (() => {});
|
||||||
this.onExitCallback = userConfig.onExitCallback;
|
this.onExitCallback = userConfig.onExitCallback;
|
||||||
|
this.logger = userConfig.logger || new Logger({logLevel: 'debug', context: constants.contexts.blockchain}); // do not pass in events as we don't want any log events emitted
|
||||||
|
this.events = userConfig.events;
|
||||||
this.proxyIpc = null;
|
this.proxyIpc = null;
|
||||||
|
this.isStandalone = userConfig.isStandalone;
|
||||||
|
|
||||||
let defaultWsApi = clientClass.DEFAULTS.WS_API;
|
let defaultWsApi = clientClass.DEFAULTS.WS_API;
|
||||||
if (this.isDev) defaultWsApi = clientClass.DEFAULTS.DEV_WS_API;
|
if (this.isDev) defaultWsApi = clientClass.DEFAULTS.DEV_WS_API;
|
||||||
@ -75,19 +79,52 @@ var Blockchain = function(userConfig, clientClass) {
|
|||||||
|
|
||||||
const spaceMessage = 'The path for %s in blockchain config contains spaces, please remove them';
|
const spaceMessage = 'The path for %s in blockchain config contains spaces, please remove them';
|
||||||
if (this.config.datadir && this.config.datadir.indexOf(' ') > 0) {
|
if (this.config.datadir && this.config.datadir.indexOf(' ') > 0) {
|
||||||
console.error(__(spaceMessage, 'datadir'));
|
this.logger.error(__(spaceMessage, 'datadir'));
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
if (this.config.account.password && this.config.account.password.indexOf(' ') > 0) {
|
if (this.config.account.password && this.config.account.password.indexOf(' ') > 0) {
|
||||||
console.error(__(spaceMessage, 'account.password'));
|
this.logger.error(__(spaceMessage, 'account.password'));
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
if (this.config.genesisBlock && this.config.genesisBlock.indexOf(' ') > 0) {
|
if (this.config.genesisBlock && this.config.genesisBlock.indexOf(' ') > 0) {
|
||||||
console.error(__(spaceMessage, 'genesisBlock'));
|
this.logger.error(__(spaceMessage, 'genesisBlock'));
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
this.initProxy();
|
this.initProxy();
|
||||||
this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev});
|
this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev});
|
||||||
|
|
||||||
|
this.initStandaloneProcess();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polls for a connection to an IPC server (generally this is set up
|
||||||
|
* in the Embark process). Once connected, any logs logged to the
|
||||||
|
* Logger will be shipped off to the IPC server. In the case of `embark
|
||||||
|
* run`, the BlockchainListener module is listening for these logs.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
Blockchain.prototype.initStandaloneProcess = function () {
|
||||||
|
if (this.isStandalone) {
|
||||||
|
// on every log logged in logger (say that 3x fast), send the log
|
||||||
|
// to the IPC serve listening (only if we're connected of course)
|
||||||
|
this.events.on('log', (logLevel, message) => {
|
||||||
|
if (this.ipc.connected) {
|
||||||
|
this.ipc.request('blockchain:log', {logLevel, message});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.ipc = new Ipc({ipcRole: 'client'});
|
||||||
|
|
||||||
|
// Wait for an IPC server to start (ie `embark run`) by polling `.connect()`.
|
||||||
|
// Do not kill this interval as the IPC server may restart (ie restart
|
||||||
|
// `embark run` without restarting `embark blockchain`)
|
||||||
|
setInterval(() => {
|
||||||
|
if (!this.ipc.connected) {
|
||||||
|
this.ipc.connect(() => {});
|
||||||
|
}
|
||||||
|
}, IPC_CONNECT_INTERVAL);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Blockchain.prototype.initProxy = function () {
|
Blockchain.prototype.initProxy = function () {
|
||||||
@ -118,7 +155,7 @@ Blockchain.prototype.shutdownProxy = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Blockchain.prototype.runCommand = function (cmd, options, callback) {
|
Blockchain.prototype.runCommand = function (cmd, options, callback) {
|
||||||
console.log(__("running: %s", cmd.underline).green);
|
this.logger.info(__("running: %s", cmd.underline).green);
|
||||||
if (this.config.silent) {
|
if (this.config.silent) {
|
||||||
options.silent = true;
|
options.silent = true;
|
||||||
}
|
}
|
||||||
@ -127,11 +164,11 @@ Blockchain.prototype.runCommand = function(cmd, options, callback) {
|
|||||||
|
|
||||||
Blockchain.prototype.run = function () {
|
Blockchain.prototype.run = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
console.log("===============================================================================".magenta);
|
this.logger.info("===============================================================================".magenta);
|
||||||
console.log("===============================================================================".magenta);
|
this.logger.info("===============================================================================".magenta);
|
||||||
console.log(__("Embark Blockchain using %s", self.client.prettyName.underline).magenta);
|
this.logger.info(__("Embark Blockchain using %s", self.client.prettyName.underline).magenta);
|
||||||
console.log("===============================================================================".magenta);
|
this.logger.info("===============================================================================".magenta);
|
||||||
console.log("===============================================================================".magenta);
|
this.logger.info("===============================================================================".magenta);
|
||||||
|
|
||||||
if (self.client.name === 'geth') this.checkPathLength();
|
if (self.client.name === 'geth') this.checkPathLength();
|
||||||
|
|
||||||
@ -163,29 +200,29 @@ Blockchain.prototype.run = function() {
|
|||||||
}
|
}
|
||||||
], function(err, cmd, args) {
|
], function(err, cmd, args) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err.message);
|
self.logger.error(err.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
args = utils.compact(args);
|
args = utils.compact(args);
|
||||||
|
|
||||||
let full_cmd = cmd + " " + args.join(' ');
|
let full_cmd = cmd + " " + args.join(' ');
|
||||||
console.log(__("running: %s", full_cmd.underline).green);
|
self.logger.info(__("running: %s", full_cmd.underline).green);
|
||||||
self.child = spawn(cmd, args, {cwd: process.cwd()});
|
self.child = spawn(cmd, args, {cwd: process.cwd()});
|
||||||
|
|
||||||
self.child.on('error', (err) => {
|
self.child.on('error', (err) => {
|
||||||
err = err.toString();
|
err = err.toString();
|
||||||
console.error('Blockchain error: ', err);
|
self.logger.error('Blockchain error: ', err);
|
||||||
if (self.env === 'development' && err.indexOf('Failed to unlock') > 0) {
|
if (self.env === 'development' && err.indexOf('Failed to unlock') > 0) {
|
||||||
console.error('\n' + __('Development blockchain has changed to use the --dev option.').yellow);
|
self.logger.error('\n' + __('Development blockchain has changed to use the --dev option.').yellow);
|
||||||
console.error(__('You can reset your workspace to fix the problem with').yellow + ' embark reset'.cyan);
|
self.logger.error(__('You can reset your workspace to fix the problem with').yellow + ' embark reset'.cyan);
|
||||||
console.error(__('Otherwise, you can change your data directory in blockchain.json (datadir)').yellow);
|
self.logger.error(__('Otherwise, you can change your data directory in blockchain.json (datadir)').yellow);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TOCHECK I don't understand why stderr and stdout are reverted.
|
// TOCHECK I don't understand why stderr and stdout are reverted.
|
||||||
// This happens with Geth and Parity, so it does not seems a client problem
|
// This happens with Geth and Parity, so it does not seems a client problem
|
||||||
self.child.stdout.on('data', (data) => {
|
self.child.stdout.on('data', (data) => {
|
||||||
console.error(`${self.client.name} error: ${data}`);
|
self.logger.info(`${self.client.name} error: ${data}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.child.stderr.on('data', async (data) => {
|
self.child.stderr.on('data', async (data) => {
|
||||||
@ -194,7 +231,7 @@ Blockchain.prototype.run = function() {
|
|||||||
self.readyCalled = true;
|
self.readyCalled = true;
|
||||||
if (self.isDev) {
|
if (self.isDev) {
|
||||||
self.fundAccounts((err) => {
|
self.fundAccounts((err) => {
|
||||||
if (err) console.error('Error funding accounts', err);
|
if (err) this.logger.error('Error funding accounts', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (self.config.proxy) {
|
if (self.config.proxy) {
|
||||||
@ -202,7 +239,7 @@ Blockchain.prototype.run = function() {
|
|||||||
}
|
}
|
||||||
self.readyCallback();
|
self.readyCallback();
|
||||||
}
|
}
|
||||||
console.log(`${self.client.name}: ${data}`);
|
self.logger.info(`${self.client.name}: ${data}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.child.on('exit', (code) => {
|
self.child.on('exit', (code) => {
|
||||||
@ -212,14 +249,14 @@ Blockchain.prototype.run = function() {
|
|||||||
} else {
|
} else {
|
||||||
strCode = 'with no error code (manually killed?)';
|
strCode = 'with no error code (manually killed?)';
|
||||||
}
|
}
|
||||||
console.error(self.client.name + ' exited ' + strCode);
|
self.logger.error(self.client.name + ' exited ' + strCode);
|
||||||
if (self.onExitCallback) {
|
if (self.onExitCallback) {
|
||||||
self.onExitCallback();
|
self.onExitCallback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.child.on('uncaughtException', (err) => {
|
self.child.on('uncaughtException', (err) => {
|
||||||
console.error('Uncaught ' + self.client.name + ' exception', err);
|
self.logger.error('Uncaught ' + self.client.name + ' exception', err);
|
||||||
if (self.onExitCallback) {
|
if (self.onExitCallback) {
|
||||||
self.onExitCallback();
|
self.onExitCallback();
|
||||||
}
|
}
|
||||||
@ -254,11 +291,11 @@ Blockchain.prototype.kill = function() {
|
|||||||
Blockchain.prototype.checkPathLength = function () {
|
Blockchain.prototype.checkPathLength = function () {
|
||||||
let dappPath = fs.dappPath('');
|
let dappPath = fs.dappPath('');
|
||||||
if (dappPath.length > 66) {
|
if (dappPath.length > 66) {
|
||||||
// console.error is captured and sent to the console output regardless of silent setting
|
// this.logger.error is captured and sent to the console output regardless of silent setting
|
||||||
console.error("===============================================================================".yellow);
|
this.logger.error("===============================================================================".yellow);
|
||||||
console.error("===========> ".yellow + __('WARNING! ÐApp path length is too long: ').yellow + dappPath.yellow);
|
this.logger.error("===========> ".yellow + __('WARNING! ÐApp path length is too long: ').yellow + dappPath.yellow);
|
||||||
console.error("===========> ".yellow + __('This is known to cause issues with starting geth, please consider reducing your ÐApp path\'s length to 66 characters or less.').yellow);
|
this.logger.error("===========> ".yellow + __('This is known to cause issues with starting geth, please consider reducing your ÐApp path\'s length to 66 characters or less.').yellow);
|
||||||
console.error("===============================================================================".yellow);
|
this.logger.error("===============================================================================".yellow);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -358,7 +395,7 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
|
|||||||
function listAccounts(next) {
|
function listAccounts(next) {
|
||||||
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => {
|
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => {
|
||||||
if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) {
|
if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) {
|
||||||
console.log(__("no accounts found").green);
|
this.logger.info(__("no accounts found").green);
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
let firstAccountFound = self.client.parseListAccountsCommandResultToAddress(stdout);
|
let firstAccountFound = self.client.parseListAccountsCommandResultToAddress(stdout);
|
||||||
@ -366,7 +403,7 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
|
|||||||
console.log(__("no accounts found").green);
|
console.log(__("no accounts found").green);
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
console.log(__("already initialized").green);
|
this.logger.info(__("already initialized").green);
|
||||||
address = firstAccountFound;
|
address = firstAccountFound;
|
||||||
next(ALREADY_INITIALIZED);
|
next(ALREADY_INITIALIZED);
|
||||||
});
|
});
|
||||||
@ -376,7 +413,7 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
|
|||||||
if (!self.config.genesisBlock || self.client.name === 'parity') {
|
if (!self.config.genesisBlock || self.client.name === 'parity') {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
console.log(__("initializing genesis block").green);
|
this.logger.info(__("initializing genesis block").green);
|
||||||
self.runCommand(self.client.initGenesisCommmand(), {}, (err, _stdout, _stderr) => {
|
self.runCommand(self.client.initGenesisCommmand(), {}, (err, _stdout, _stderr) => {
|
||||||
next(err);
|
next(err);
|
||||||
});
|
});
|
||||||
@ -398,9 +435,9 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var BlockchainClient = function(userConfig, clientName, env, onReadyCallback, onExitCallback) {
|
var BlockchainClient = function(userConfig, clientName, env, onReadyCallback, onExitCallback, logger, events, isStandalone) {
|
||||||
if ((userConfig === {} || JSON.stringify(userConfig) === '{"enabled":true}') && env !== 'development') {
|
if ((userConfig === {} || JSON.stringify(userConfig) === '{"enabled":true}') && env !== 'development') {
|
||||||
console.log("===> " + __("warning: running default config on a non-development environment"));
|
logger.info("===> " + __("warning: running default config on a non-development environment"));
|
||||||
}
|
}
|
||||||
// if client is not set in preferences, default is geth
|
// if client is not set in preferences, default is geth
|
||||||
if (!userConfig.ethereumClientName) userConfig.ethereumClientName = 'geth';
|
if (!userConfig.ethereumClientName) userConfig.ethereumClientName = 'geth';
|
||||||
@ -416,7 +453,7 @@ var BlockchainClient = function(userConfig, clientName, env, onReadyCallback, on
|
|||||||
case 'parity':
|
case 'parity':
|
||||||
clientClass = ParityClient;
|
clientClass = ParityClient;
|
||||||
break;
|
break;
|
||||||
|
return new Blockchain({blockchainConfig, client: GethCommands, env, isDev, onReadyCallback, onExitCallback, logger, events, isStandalone});
|
||||||
default:
|
default:
|
||||||
console.error(__('Unknow client "%s". Please use one of the following: %s', userConfig.ethereumClientName, 'geth, parity'));
|
console.error(__('Unknow client "%s". Please use one of the following: %s', userConfig.ethereumClientName, 'geth, parity'));
|
||||||
process.exit();
|
process.exit();
|
||||||
|
37
lib/modules/embark_listener/index.js
Normal file
37
lib/modules/embark_listener/index.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
const ProcessLogsApi = require('../process_logs_api');
|
||||||
|
|
||||||
|
const EMBARK_PROCESS_NAME = 'embark';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmbarkListener has two functions:
|
||||||
|
* 1. Register API endpoints (HTTP GET and WS) to retrieve embark logs.
|
||||||
|
* 2. Listen to log events in Embark and ensure they are processed
|
||||||
|
* through the LogHandler.
|
||||||
|
*/
|
||||||
|
class EmbarkListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Plugin} embark EmbarkListener module plugin object
|
||||||
|
*/
|
||||||
|
constructor(embark) {
|
||||||
|
this.embark = embark;
|
||||||
|
this.events = embark.events;
|
||||||
|
this.logger = embark.logger;
|
||||||
|
this.processLogsApi = new ProcessLogsApi({embark: this.embark, processName: EMBARK_PROCESS_NAME, silent: false});
|
||||||
|
|
||||||
|
this._listenToEmbarkLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens to log events emitted by the Embark application and ensures
|
||||||
|
* they are processed through the LogHandler.
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
_listenToEmbarkLogs() {
|
||||||
|
this.events.on("log", (logLevel, message) => {
|
||||||
|
this.processLogsApi.logHandler.handleLog({logLevel, message}, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = EmbarkListener;
|
@ -1,22 +0,0 @@
|
|||||||
class LoggerApi {
|
|
||||||
constructor(embark) {
|
|
||||||
this.embark = embark;
|
|
||||||
this.logger = embark.logger;
|
|
||||||
|
|
||||||
this.registerAPICalls();
|
|
||||||
}
|
|
||||||
|
|
||||||
registerAPICalls(){
|
|
||||||
this.embark.registerAPICall(
|
|
||||||
'get',
|
|
||||||
'/embark-api/process-logs/embark',
|
|
||||||
(req, res) => {
|
|
||||||
let limit = parseInt(req.query.limit, 10);
|
|
||||||
if(!Number.isInteger(limit)) limit = 0;
|
|
||||||
res.send(this.logger.parseLogFile(limit));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = LoggerApi;
|
|
@ -8,6 +8,7 @@ const WebpackConfigReader = require('../pipeline/webpackConfigReader');
|
|||||||
|
|
||||||
class Pipeline {
|
class Pipeline {
|
||||||
constructor(embark, options) {
|
constructor(embark, options) {
|
||||||
|
this.embark = embark;
|
||||||
this.env = embark.config.env;
|
this.env = embark.config.env;
|
||||||
this.buildDir = embark.config.buildDir;
|
this.buildDir = embark.config.buildDir;
|
||||||
this.contractsFiles = embark.config.contractsFiles;
|
this.contractsFiles = embark.config.contractsFiles;
|
||||||
@ -194,6 +195,8 @@ class Pipeline {
|
|||||||
});
|
});
|
||||||
let built = false;
|
let built = false;
|
||||||
const webpackProcess = new ProcessLauncher({
|
const webpackProcess = new ProcessLauncher({
|
||||||
|
embark: self.embark,
|
||||||
|
plugins: self.plugins,
|
||||||
modulePath: utils.joinPath(__dirname, 'webpackProcess.js'),
|
modulePath: utils.joinPath(__dirname, 'webpackProcess.js'),
|
||||||
logger: self.logger,
|
logger: self.logger,
|
||||||
events: self.events,
|
events: self.events,
|
||||||
|
38
lib/modules/process_logs_api/index.js
Normal file
38
lib/modules/process_logs_api/index.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const LogHandler = require('../../utils/logHandler');
|
||||||
|
|
||||||
|
class ProcessLogsApi {
|
||||||
|
constructor({embark, processName, silent}) {
|
||||||
|
this.embark = embark;
|
||||||
|
this.processName = processName;
|
||||||
|
this.logger = this.embark.logger;
|
||||||
|
this.events = this.embark.events;
|
||||||
|
this.logHandler = new LogHandler({events: this.events, logger: this.logger, processName: this.processName, silent});
|
||||||
|
|
||||||
|
this.registerAPICalls();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerAPICalls() {
|
||||||
|
const apiRoute = '/embark-api/process-logs/' + this.processName;
|
||||||
|
this.embark.registerAPICall(
|
||||||
|
'ws',
|
||||||
|
apiRoute,
|
||||||
|
(ws, _req) => {
|
||||||
|
this.events.on('process-log-' + this.processName, function (log) {
|
||||||
|
ws.send(JSON.stringify(log), () => {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.embark.registerAPICall(
|
||||||
|
'get',
|
||||||
|
'/embark-api/process-logs/' + this.processName,
|
||||||
|
(req, res) => {
|
||||||
|
let limit = parseInt(req.query.limit, 10);
|
||||||
|
if (!Number.isInteger(limit)) limit = 0;
|
||||||
|
const result = this.logHandler.logs.slice(limit * -1);
|
||||||
|
res.send(JSON.stringify(result));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ProcessLogsApi;
|
@ -4,6 +4,7 @@ let SolcW = require('./solcW.js');
|
|||||||
class Solidity {
|
class Solidity {
|
||||||
|
|
||||||
constructor(embark, options) {
|
constructor(embark, options) {
|
||||||
|
this.embark = embark;
|
||||||
this.logger = embark.logger;
|
this.logger = embark.logger;
|
||||||
this.events = embark.events;
|
this.events = embark.events;
|
||||||
this.ipc = options.ipc;
|
this.ipc = options.ipc;
|
||||||
@ -68,7 +69,7 @@ class Solidity {
|
|||||||
if (self.solcAlreadyLoaded) {
|
if (self.solcAlreadyLoaded) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
self.solcW = new SolcW({logger: self.logger, events: self.events, ipc: self.ipc, useDashboard: self.useDashboard});
|
self.solcW = new SolcW(self.embark, {logger: self.logger, events: self.events, ipc: self.ipc, useDashboard: self.useDashboard});
|
||||||
|
|
||||||
self.logger.info(__("loading solc compiler") + "..");
|
self.logger.info(__("loading solc compiler") + "..");
|
||||||
self.solcW.load_compiler(function (err) {
|
self.solcW.load_compiler(function (err) {
|
||||||
|
@ -6,7 +6,8 @@ const uuid = require('uuid/v1');
|
|||||||
|
|
||||||
class SolcW {
|
class SolcW {
|
||||||
|
|
||||||
constructor(options) {
|
constructor(embark, options) {
|
||||||
|
this.embark = embark;
|
||||||
this.logger = options.logger;
|
this.logger = options.logger;
|
||||||
this.events = options.events;
|
this.events = options.events;
|
||||||
this.ipc = options.ipc;
|
this.ipc = options.ipc;
|
||||||
@ -40,6 +41,7 @@ class SolcW {
|
|||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
this.solcProcess = new ProcessLauncher({
|
this.solcProcess = new ProcessLauncher({
|
||||||
|
embark: self.embark,
|
||||||
modulePath: utils.joinPath(__dirname, 'solcP.js'),
|
modulePath: utils.joinPath(__dirname, 'solcP.js'),
|
||||||
logger: self.logger,
|
logger: self.logger,
|
||||||
events: self.events,
|
events: self.events,
|
||||||
|
93
lib/utils/logHandler.js
Normal file
93
lib/utils/logHandler.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
// define max number of logs to keep in memory for this process
|
||||||
|
// to prevent runaway memory leak
|
||||||
|
const MAX_LOGS = require('../constants').logs.maxLogLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serves as a central point of log handling.
|
||||||
|
*/
|
||||||
|
class LogHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} options Options object containing:
|
||||||
|
* - {EventEmitter} events Embark events
|
||||||
|
* - {Logger} logger Embark logger
|
||||||
|
* - {String} processName Name of the process for which it's logs
|
||||||
|
* are being handled.
|
||||||
|
* - {Boolean} silent If true, does not log the message, unless
|
||||||
|
* it has a logLevel of 'error'.
|
||||||
|
*/
|
||||||
|
constructor({events, logger, processName, silent}) {
|
||||||
|
this.events = events;
|
||||||
|
this.logger = logger;
|
||||||
|
this.processName = processName;
|
||||||
|
this.silent = silent;
|
||||||
|
|
||||||
|
this.logs = [];
|
||||||
|
this.id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Servers as an interception of logs, normalises the message output, adds
|
||||||
|
* metadata (timestamp, id), stores the log in memory, then sends it to the
|
||||||
|
* logger for output. Max number of logs stored in memory is capped by MAX_LOGS.
|
||||||
|
*
|
||||||
|
* @param {Object} msg Object containing the log message (msg.message)
|
||||||
|
* @param {Boolean} alreadyLogged (optional, default = false) If true, prevents
|
||||||
|
* the logger from logging the event. Generally used when the log has already
|
||||||
|
* been logged using the Logger (which emits a "log" event), and is then sent
|
||||||
|
* to `handleLog` for normalization. If allowed to log again, another event
|
||||||
|
* would be emitted, and an infinite loop would occur. Setting to true will
|
||||||
|
* prevent infinite looping.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
handleLog(msg, alreadyLogged = false) {
|
||||||
|
if (!msg) return;
|
||||||
|
|
||||||
|
// Sometimes messages come in with line breaks, so we need to break them up accordingly.
|
||||||
|
let processedMessages = [];
|
||||||
|
|
||||||
|
// Ensure that `msg.message` is an array, so we process this consistently. Sometimes it
|
||||||
|
// is an Array, sometimes it is a string.
|
||||||
|
if (typeof msg.message === 'string') {
|
||||||
|
processedMessages = [msg.message];
|
||||||
|
} else if (Array.isArray(msg.message)) {
|
||||||
|
msg.message.forEach(message => {
|
||||||
|
if (Array.isArray(message)) message = message.join('\n');
|
||||||
|
let lines = message.split("\n");
|
||||||
|
lines.forEach(line => processedMessages.push(line));
|
||||||
|
});
|
||||||
|
} else if (typeof msg.message === 'object') {
|
||||||
|
processedMessages.push(JSON.stringify(msg.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime();
|
||||||
|
|
||||||
|
processedMessages.forEach((message) => {
|
||||||
|
const log = {
|
||||||
|
msg: message,
|
||||||
|
msg_clear: message.stripColors,
|
||||||
|
logLevel: msg.logLevel,
|
||||||
|
name: this.processName,
|
||||||
|
timestamp,
|
||||||
|
id: ++this.id
|
||||||
|
};
|
||||||
|
if (this.logs.length >= MAX_LOGS) {
|
||||||
|
this.logs.shift();
|
||||||
|
}
|
||||||
|
this.logs.push(log);
|
||||||
|
this.events.emit(`process-log-${this.processName}`, log);
|
||||||
|
if ((this.silent && msg.type !== 'error') || alreadyLogged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.logger[msg.type]) {
|
||||||
|
return this.logger[msg.type](utils.normalizeInput(message));
|
||||||
|
}
|
||||||
|
this.logger.debug(utils.normalizeInput(message));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = LogHandler;
|
Loading…
x
Reference in New Issue
Block a user