From 728713a183e0cd802fbb166629a7f56ebf66db78 Mon Sep 17 00:00:00 2001 From: emizzle Date: Mon, 22 Oct 2018 23:56:14 +1100 Subject: [PATCH 1/7] Embark blockchain logs when running standalone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running `embark blockchain` followed by `embark run` previously, logs generated in the standalone `embark blockchain` process were black boxed and not accessible to the main Embark process. This is fixed by creating a client IPC connection in the `embark blockchain` process that connects to the IPC server connection running in `embark run`. The connection is made by way of polling `ipc.connect` and continues polling even after a connection is made in case `embark run` is killed and restarted without restarting `embark blockchain`. `LogHandler` was introduced to extrapolate functionality used in `ProcessLauncher` that needed to also be used in the standalone blockchain process. It also caps the number of logs that are stored in memory per process by a constant value defined in `constants.json`. A `blockchain_listener` was module was created (and run inside of `embark run`) that listens for logs emitted by the `embark blockchain` client IPC and runs them through the `LogHandler`. Additionally, this module registers the API endpoints needed to handle requests for blockchain process logs in the cockpit (which were 404’ing before). # Conflicts: # lib/modules/blockchain_process/blockchain.js --- cmd/cmd_controller.js | 76 ++++------- lib/constants.json | 3 +- lib/core/engine.js | 9 +- lib/core/processes/processLauncher.js | 42 +----- lib/modules/blockchain_listener/index.js | 54 ++++++++ lib/modules/blockchain_process/blockchain.js | 129 ++++++++++++------- lib/utils/logHandler.js | 71 ++++++++++ 7 files changed, 248 insertions(+), 136 deletions(-) create mode 100644 lib/modules/blockchain_listener/index.js create mode 100644 lib/utils/logHandler.js diff --git a/cmd/cmd_controller.js b/cmd/cmd_controller.js index eafc108d0..cb43769bd 100644 --- a/cmd/cmd_controller.js +++ b/cmd/cmd_controller.js @@ -31,8 +31,8 @@ class EmbarkController { } blockchain(env, client) { - this.context = [constants.contexts.blockchain]; - return require('../lib/modules/blockchain_process/blockchain.js')(this.config.blockchainConfig, client, env).run(); + this.context = [constants.contexts.blockchain]; + return require('../lib/modules/blockchain_process/blockchain.js')(this.config.blockchainConfig, client, env, null, null, this.logger, this.events, true).run(); } simulator(options) { @@ -132,6 +132,7 @@ class EmbarkController { engine.startService("processManager"); engine.startService("coreProcess"); engine.startService("loggerApi"); + engine.startService("blockchainListener"); engine.startService("serviceMonitor"); engine.startService("libraryManager"); engine.startService("codeRunner"); @@ -437,73 +438,50 @@ class EmbarkController { } scaffold(options) { + this.context = options.context || [constants.contexts.scaffold]; + options.onlyCompile = true; const Engine = require('../lib/core/engine.js'); const engine = new Engine({ env: options.env, - client: options.client, - locale: options.locale, version: this.version, - embarkConfig: 'embark.json', - interceptLogs: false, + embarkConfig: options.embarkConfig || 'embark.json', logFile: options.logFile, - logLevel: options.logLevel, - events: options.events, - logger: options.logger, - config: options.config, - plugins: options.plugins, - context: this.context, - webpackConfigName: options.webpackConfigName + context: this.context }); + async.waterfall([ - function initEngine(callback) { + function (callback) { engine.init({}, callback); }, - function startServices(callback) { - engine.startService("scaffolding"); - callback(); - }, - function generateContract(callback) { - engine.events.request('scaffolding:generate:contract', options, function(err, file) { - // Add contract file to the manager - engine.events.request('config:contractsFiles:add', file); - callback(); - }); - }, - function initEngineServices(callback) { + function (callback) { let pluginList = engine.plugins.listPlugins(); if (pluginList.length > 0) { engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); } - engine.startService("processManager"); - engine.startService("libraryManager"); - engine.startService("codeRunner"); - engine.startService("web3"); - engine.startService("deployment", {onlyCompile: true}); - callback(); - }, - function deploy(callback) { - engine.events.request('deploy:contracts', function(err) { - callback(err); - }); - }, - function generateUI(callback) { - engine.events.request("scaffolding:generate:ui", options, () => { - callback(); - }); + engine.startService("processManager"); + engine.startService("serviceMonitor"); + engine.startService("libraryManager"); + engine.startService("pipeline"); + engine.startService("deployment", {onlyCompile: true}); + engine.startService("web3"); + engine.startService("scaffolding"); + + engine.events.request('deploy:contracts', callback); } - ], function(err) { + ], (err) => { if (err) { - engine.logger.error(__("Error generating the UI: ")); - engine.logger.error(err.message || err); - process.exit(1); + engine.logger.error(err.message); + engine.logger.info(err.stack); + } else { + engine.events.request("scaffolding:generate", options, () => { + engine.logger.info(__("finished generating the UI").underline); + process.exit(); + }); } - engine.logger.info(__("finished generating the UI").underline); - engine.logger.info(__("To see the result, execute {{cmd}} and go to /{{contract}}.html", {cmd: 'embark run'.underline, contract: options.contract})); - process.exit(); }); } diff --git a/lib/constants.json b/lib/constants.json index 26b7491e9..8971600ec 100644 --- a/lib/constants.json +++ b/lib/constants.json @@ -54,6 +54,7 @@ "gasLimit": 6000000 }, "logs": { - "logPath": ".embark/logs/" + "logPath": ".embark/logs/", + "maxLogLength": 1500 } } diff --git a/lib/core/engine.js b/lib/core/engine.js index 22d1f050a..a47051111 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -78,7 +78,8 @@ class Engine { "codeCoverage": this.codeCoverageService, "scaffolding": this.scaffoldingService, "coreProcess": this.coreProcessService, - "loggerApi": this.loggerApiService + "loggerApi": this.loggerApiService, + "blockchainListener": this.blockchainListenerService }; let service = services[serviceName]; @@ -92,6 +93,12 @@ class Engine { return service.apply(this, [options]); } + blockchainListenerService(_options){ + this.registerModule('blockchain_listener', { + ipc: this.ipc + }); + } + coreProcessService(_options){ this.registerModule('core_process', { events: this.events diff --git a/lib/core/processes/processLauncher.js b/lib/core/processes/processLauncher.js index e9ec7caf8..3ceca6821 100644 --- a/lib/core/processes/processLauncher.js +++ b/lib/core/processes/processLauncher.js @@ -2,6 +2,7 @@ const child_process = require('child_process'); const constants = require('../../constants'); const path = require('path'); const utils = require('../../utils/utils'); +const LogHandler = require('../../utils/logHandler'); let processCount = 1; class ProcessLauncher { @@ -30,6 +31,7 @@ class ProcessLauncher { this.exitCallback = options.exitCallback; this.embark = options.embark; this.logs = []; + this.logHandler = new LogHandler({events: this.events, logger: this.logger, processName: this.name, silent: this.silent}); this.subscriptions = {}; this._subscribeToMessages(); @@ -51,7 +53,7 @@ class ProcessLauncher { self.logger.error(msg.error); } if (msg.result === constants.process.log) { - return self._handleLog(msg); + return self.logHandler.handleLog(msg); } if (msg.event) { return self._handleEvent(msg); @@ -93,44 +95,6 @@ class ProcessLauncher { ); } - // 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 _handleEvent(msg) { const self = this; diff --git a/lib/modules/blockchain_listener/index.js b/lib/modules/blockchain_listener/index.js new file mode 100644 index 000000000..47c228deb --- /dev/null +++ b/lib/modules/blockchain_listener/index.js @@ -0,0 +1,54 @@ +const LogHandler = require('../../utils/logHandler'); + +const PROCESS_NAME = 'blockchain'; + +class BlockchainListener { + constructor(embark, options) { + this.embark = embark; + this.events = embark.events; + this.logger = embark.logger; + this.ipc = options.ipc; + this.logHandler = new LogHandler({events: this.events, logger: this.logger, processName: PROCESS_NAME, silent: true}); + + if (this.ipc.isServer()) { + this._listenToLogs(); + } + + this._registerApi(); + } + + _listenToLogs() { + this.ipc.on('blockchain:log', ({logLevel, message}) => { + this.logHandler.handleLog({logLevel, message}); + }); + } + + _registerApi() { + // This route is overriden by `processLauncher` when the blockchain + // process is launched (ie when not in blockchain standalone mode). + // The order is determined by the order in which the engine starts + // it's services, with `blockchain_process` coming before `web3`. + const apiRoute = '/embark-api/process-logs/' + PROCESS_NAME; + this.embark.registerAPICall( + 'ws', + apiRoute, + (ws, _req) => { + this.events.on('process-log-' + PROCESS_NAME, function (id, log) { + ws.send(JSON.stringify(Object.assign(log, {id})), () => {}); + }); + } + ); + this.embark.registerAPICall( + 'get', + apiRoute, + (req, res) => { + let limit = parseInt(req.query.limit, 10); + if (!Number.isInteger(limit)) limit = 0; + const result = this.logHandler.logs.map((log, id) => Object.assign(log, {id})).slice(limit); + res.send(JSON.stringify(result)); + } + ); + } +} + +module.exports = BlockchainListener; diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index d74f8ff69..b92471a3f 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -1,27 +1,31 @@ const async = require('async'); const {spawn, exec} = require('child_process'); - const fs = require('../../core/fs.js'); const constants = require('../../constants.json'); const utils = require('../../utils/utils.js'); - const GethClient = require('./gethClient.js'); const ParityClient = require('./parityClient.js'); const DevFunds = require('./dev_funds.js'); - const proxy = require('./proxy'); const Ipc = require('../../core/ipc'); 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) { this.userConfig = userConfig; this.env = userConfig.env || 'development'; this.isDev = userConfig.isDev; this.onReadyCallback = userConfig.onReadyCallback || (() => {}); this.onExitCallback = userConfig.onExitCallback; + this.logger = userConfig.logger || new Logger({logLevel: 'debug'}); + this.events = userConfig.events; this.proxyIpc = null; + this.isStandalone = userConfig.isStandalone; let defaultWsApi = clientClass.DEFAULTS.WS_API; if (this.isDev) defaultWsApi = clientClass.DEFAULTS.DEV_WS_API; @@ -75,29 +79,62 @@ var Blockchain = function(userConfig, clientClass) { const spaceMessage = 'The path for %s in blockchain config contains spaces, please remove them'; if (this.config.datadir && this.config.datadir.indexOf(' ') > 0) { - console.error(__(spaceMessage, 'datadir')); + this.logger.error(__(spaceMessage, 'datadir')); process.exit(); } if (this.config.account.password && this.config.account.password.indexOf(' ') > 0) { - console.error(__(spaceMessage, 'account.password')); + this.logger.error(__(spaceMessage, 'account.password')); process.exit(); } if (this.config.genesisBlock && this.config.genesisBlock.indexOf(' ') > 0) { - console.error(__(spaceMessage, 'genesisBlock')); + this.logger.error(__(spaceMessage, 'genesisBlock')); process.exit(); } this.initProxy(); this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev}); + + this.initStandaloneProcess(); }; -Blockchain.prototype.initProxy = function() { +/** + * 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 () { if (this.config.proxy) { this.config.rpcPort += constants.blockchain.servicePortOnProxy; this.config.wsPort += constants.blockchain.servicePortOnProxy; } }; -Blockchain.prototype.setupProxy = async function() { +Blockchain.prototype.setupProxy = async function () { if (!this.proxyIpc) this.proxyIpc = new Ipc({ipcRole: 'client'}); let wsProxy; @@ -108,7 +145,7 @@ Blockchain.prototype.setupProxy = async function() { [this.rpcProxy, this.wsProxy] = await Promise.all([proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false), wsProxy]); }; -Blockchain.prototype.shutdownProxy = function() { +Blockchain.prototype.shutdownProxy = function () { if (!this.config.proxy) { return; } @@ -117,21 +154,21 @@ Blockchain.prototype.shutdownProxy = function() { if (this.wsProxy) this.wsProxy.close(); }; -Blockchain.prototype.runCommand = function(cmd, options, callback) { - console.log(__("running: %s", cmd.underline).green); +Blockchain.prototype.runCommand = function (cmd, options, callback) { + this.logger.info(__("running: %s", cmd.underline).green); if (this.config.silent) { options.silent = true; } return exec(cmd, options, callback); }; -Blockchain.prototype.run = function() { +Blockchain.prototype.run = function () { var self = this; - console.log("===============================================================================".magenta); - console.log("===============================================================================".magenta); - console.log(__("Embark Blockchain using %s", self.client.prettyName.underline).magenta); - console.log("===============================================================================".magenta); - console.log("===============================================================================".magenta); + this.logger.info("===============================================================================".magenta); + this.logger.info("===============================================================================".magenta); + this.logger.info(__("Embark Blockchain using %s", self.client.prettyName.underline).magenta); + this.logger.info("===============================================================================".magenta); + this.logger.info("===============================================================================".magenta); if (self.client.name === 'geth') this.checkPathLength(); @@ -157,35 +194,35 @@ Blockchain.prototype.run = function() { }); }, function getMainCommand(next) { - self.client.mainCommand(address, function(cmd, args) { + self.client.mainCommand(address, function (cmd, args) { next(null, cmd, args); }, true); } ], function(err, cmd, args) { if (err) { - console.error(err.message); + self.logger.error(err.message); return; } args = utils.compact(args); 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.on('error', (err) => { err = err.toString(); - console.error('Blockchain error: ', err); + self.logger.error('Blockchain error: ', err); if (self.env === 'development' && err.indexOf('Failed to unlock') > 0) { - console.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); - console.error(__('Otherwise, you can change your data directory in blockchain.json (datadir)').yellow); + self.logger.error('\n' + __('Development blockchain has changed to use the --dev option.').yellow); + self.logger.error(__('You can reset your workspace to fix the problem with').yellow + ' embark reset'.cyan); + 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. // This happens with Geth and Parity, so it does not seems a client problem self.child.stdout.on('data', (data) => { - console.error(`${self.client.name} error: ${data}`); + this.logger.info(`${self.client.name} error: ${data}`); }); self.child.stderr.on('data', async (data) => { @@ -194,7 +231,7 @@ Blockchain.prototype.run = function() { self.readyCalled = true; if (self.isDev) { self.fundAccounts((err) => { - if (err) console.error('Error funding accounts', err); + if (err) this.logger.error('Error funding accounts', err); }); } if (self.config.proxy) { @@ -202,7 +239,7 @@ Blockchain.prototype.run = function() { } self.readyCallback(); } - console.log(`${self.client.name}: ${data}`); + this.logger.info(`${self.client.name}: ${data}`); }); self.child.on('exit', (code) => { @@ -212,14 +249,14 @@ Blockchain.prototype.run = function() { } else { 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) { self.onExitCallback(); } }); 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) { self.onExitCallback(); } @@ -235,7 +272,7 @@ Blockchain.prototype.fundAccounts = function(cb) { }); }; -Blockchain.prototype.readyCallback = function() { +Blockchain.prototype.readyCallback = function () { if (this.onReadyCallback) { this.onReadyCallback(); } @@ -244,25 +281,25 @@ Blockchain.prototype.readyCallback = function() { } }; -Blockchain.prototype.kill = function() { +Blockchain.prototype.kill = function () { this.shutdownProxy(); if (this.child) { this.child.kill(); } }; -Blockchain.prototype.checkPathLength = function() { +Blockchain.prototype.checkPathLength = function () { let dappPath = fs.dappPath(''); if (dappPath.length > 66) { - // console.error is captured and sent to the console output regardless of silent setting - console.error("===============================================================================".yellow); - console.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); - console.error("===============================================================================".yellow); + // this.logger.error is captured and sent to the console output regardless of silent setting + this.logger.error("===============================================================================".yellow); + this.logger.error("===========> ".yellow + __('WARNING! ÐApp path length is too long: ').yellow + dappPath.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); + this.logger.error("===============================================================================".yellow); } }; -Blockchain.prototype.isClientInstalled = function(callback) { +Blockchain.prototype.isClientInstalled = function (callback) { let versionCmd = this.client.determineVersionCommand(); this.runCommand(versionCmd, {}, (err, stdout, stderr) => { if (err || !stdout || stderr.indexOf("not found") >= 0 || stdout.indexOf("not found") >= 0) { @@ -341,7 +378,7 @@ Blockchain.prototype.initDevChain = function(callback) { }); }; -Blockchain.prototype.initChainAndGetAddress = function(callback) { +Blockchain.prototype.initChainAndGetAddress = function (callback) { const self = this; let address = null; const ALREADY_INITIALIZED = 'already'; @@ -358,7 +395,7 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) { function listAccounts(next) { self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => { if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) { - console.log(__("no accounts found").green); + this.logger.info(__("no accounts found").green); return next(); } let firstAccountFound = self.client.parseListAccountsCommandResultToAddress(stdout); @@ -366,7 +403,7 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) { console.log(__("no accounts found").green); return next(); } - console.log(__("already initialized").green); + this.logger.info(__("already initialized").green); address = firstAccountFound; next(ALREADY_INITIALIZED); }); @@ -376,7 +413,7 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) { if (!self.config.genesisBlock || self.client.name === 'parity') { return next(); } - console.log(__("initializing genesis block").green); + this.logger.info(__("initializing genesis block").green); self.runCommand(self.client.initGenesisCommmand(), {}, (err, _stdout, _stderr) => { 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') { - 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 (!userConfig.ethereumClientName) userConfig.ethereumClientName = 'geth'; @@ -416,7 +453,7 @@ var BlockchainClient = function(userConfig, clientName, env, onReadyCallback, on case 'parity': clientClass = ParityClient; break; - + return new Blockchain({blockchainConfig, client: GethCommands, env, isDev, onReadyCallback, onExitCallback, logger, events, isStandalone}); default: console.error(__('Unknow client "%s". Please use one of the following: %s', userConfig.ethereumClientName, 'geth, parity')); process.exit(); diff --git a/lib/utils/logHandler.js b/lib/utils/logHandler.js new file mode 100644 index 000000000..63ad8a74b --- /dev/null +++ b/lib/utils/logHandler.js @@ -0,0 +1,71 @@ +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; + +class LogHandler { + constructor({events, logger, processName, silent}) { + this.events = events; + this.logger = logger; + this.processName = processName; + this.silent = silent; + + this.logs = []; + this.removedCount = 0; + } + + /** + * Servers as an interception of logs, normalises the message output and + * adds metadata (timestamp, id) the data, + * 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) + * + * @returns {void} + */ + handleLog(msg) { + 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 { + 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.processName, + timestamp + }; + if(this.logs.length >= MAX_LOGS){ + this.logs.shift(); + this.removedCount++; + } + const id = this.logs.push(log) - 1 + this.removedCount; + this.events.emit(`process-log-${this.processName}`, 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)); + }); + } +} + +module.exports = LogHandler; From 3246b62151f69b1ec7f4e2e9035c3b2a42902d31 Mon Sep 17 00:00:00 2001 From: emizzle Date: Tue, 23 Oct 2018 17:43:11 +1100 Subject: [PATCH 2/7] Revert changes to `scaffold` function in `cmd_controller.js` --- cmd/cmd_controller.js | 69 ++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/cmd/cmd_controller.js b/cmd/cmd_controller.js index cb43769bd..80ff90ebe 100644 --- a/cmd/cmd_controller.js +++ b/cmd/cmd_controller.js @@ -31,7 +31,7 @@ class EmbarkController { } 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, null, null, this.logger, this.events, true).run(); } @@ -438,50 +438,73 @@ class EmbarkController { } scaffold(options) { - this.context = options.context || [constants.contexts.scaffold]; - options.onlyCompile = true; const Engine = require('../lib/core/engine.js'); const engine = new Engine({ env: options.env, + client: options.client, + locale: options.locale, version: this.version, - embarkConfig: options.embarkConfig || 'embark.json', + embarkConfig: 'embark.json', + interceptLogs: false, logFile: options.logFile, - context: this.context + logLevel: options.logLevel, + events: options.events, + logger: options.logger, + config: options.config, + plugins: options.plugins, + context: this.context, + webpackConfigName: options.webpackConfigName }); - async.waterfall([ - function (callback) { + function initEngine(callback) { engine.init({}, callback); }, - function (callback) { + function startServices(callback) { + engine.startService("scaffolding"); + callback(); + }, + function generateContract(callback) { + engine.events.request('scaffolding:generate:contract', options, function(err, file) { + // Add contract file to the manager + engine.events.request('config:contractsFiles:add', file); + callback(); + }); + }, + function initEngineServices(callback) { let pluginList = engine.plugins.listPlugins(); if (pluginList.length > 0) { engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); } - engine.startService("processManager"); - engine.startService("serviceMonitor"); engine.startService("libraryManager"); - engine.startService("pipeline"); - engine.startService("deployment", {onlyCompile: true}); + engine.startService("codeRunner"); engine.startService("web3"); - engine.startService("scaffolding"); + engine.startService("deployment", {onlyCompile: true}); - engine.events.request('deploy:contracts', callback); - } - ], (err) => { - if (err) { - engine.logger.error(err.message); - engine.logger.info(err.stack); - } else { - engine.events.request("scaffolding:generate", options, () => { - engine.logger.info(__("finished generating the UI").underline); - process.exit(); + callback(); + }, + function deploy(callback) { + engine.events.request('deploy:contracts', function(err) { + callback(err); + }); + }, + function generateUI(callback) { + engine.events.request("scaffolding:generate:ui", options, () => { + callback(); }); } + ], function(err) { + if (err) { + engine.logger.error(__("Error generating the UI: ")); + engine.logger.error(err.message || err); + process.exit(1); + } + engine.logger.info(__("finished generating the UI").underline); + engine.logger.info(__("To see the result, execute {{cmd}} and go to /{{contract}}.html", {cmd: 'embark run'.underline, contract: options.contract})); + process.exit(); }); } From f5c77b14167cafd0529346967569bdb87c1ff4fb Mon Sep 17 00:00:00 2001 From: emizzle Date: Wed, 24 Oct 2018 08:54:09 +0300 Subject: [PATCH 3/7] Process logs API refactor There are three separate instances of process log APIs: embark logs, blockchain logs (when in standalone mode), and child process logs (storage, communication, blockchain, etc). Each one was repeating the implementation of creating a process log API endpoint. This commit centralises the API declaration by using the class `ProcessLogsApi`. `ProcessLogsApi` is started for all three components mentioned above: blockchain (in standalone) in the `BlockchainListener` module, embark in the `EmbarkListener` module, and for all child processes in the `ProcessLauncher`. These listeners have two functions: 1. Create the process logs API endpoints for `get` and `ws`, and 2. Ensure that all logs are logged through the `LogHandler`, which normalises the output of the log and ensures each log has a timestamp and id (used in the cockpit for log ordering). Also, this commit moved the pipeline in to a module, so that the `embark` object could be passed to the `ProcessLogsApi` (to be used for registering API endpoints). --- cmd/cmd_controller.js | 2 +- lib/core/engine.js | 28 +++++------ lib/core/processes/processLauncher.js | 34 ++----------- lib/modules/blockchain_listener/index.js | 63 ++++++++++-------------- lib/modules/embark_listener/index.js | 37 ++++++++++++++ lib/modules/logger_api/index.js | 22 --------- lib/modules/pipeline/index.js | 3 ++ lib/modules/process_logs_api/index.js | 38 ++++++++++++++ lib/modules/solidity/index.js | 3 +- lib/modules/solidity/solcW.js | 4 +- lib/utils/logHandler.js | 52 +++++++++++++------ 11 files changed, 164 insertions(+), 122 deletions(-) create mode 100644 lib/modules/embark_listener/index.js delete mode 100644 lib/modules/logger_api/index.js create mode 100644 lib/modules/process_logs_api/index.js diff --git a/cmd/cmd_controller.js b/cmd/cmd_controller.js index 80ff90ebe..4d1bb209d 100644 --- a/cmd/cmd_controller.js +++ b/cmd/cmd_controller.js @@ -131,7 +131,7 @@ class EmbarkController { engine.startService("processManager"); engine.startService("coreProcess"); - engine.startService("loggerApi"); + engine.startService("embarkListener"); engine.startService("blockchainListener"); engine.startService("serviceMonitor"); engine.startService("libraryManager"); diff --git a/lib/core/engine.js b/lib/core/engine.js index a47051111..debc1bef8 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -78,8 +78,9 @@ class Engine { "codeCoverage": this.codeCoverageService, "scaffolding": this.scaffoldingService, "coreProcess": this.coreProcessService, - "loggerApi": this.loggerApiService, - "blockchainListener": this.blockchainListenerService + "processApi": this.processApiService, + "blockchainListener": this.blockchainListenerService, + "embarkListener": this.embarkListenerService }; let service = services[serviceName]; @@ -93,6 +94,10 @@ class Engine { return service.apply(this, [options]); } + embarkListenerService(_options){ + this.registerModule('embark_listener'); + } + blockchainListenerService(_options){ this.registerModule('blockchain_listener', { ipc: this.ipc @@ -105,8 +110,8 @@ class Engine { }); } - loggerApiService(_options){ - this.registerModule('logger_api', { + processApiService(_options){ + this.registerModule('process_api', { logger: this.logger }); } @@ -129,17 +134,10 @@ class Engine { } pipelineService(_options) { - const self = this; - this.registerModule('pipeline', { - webpackConfigName: this.webpackConfigName - }); - this.events.on('code-generator-ready', function (modifiedAssets) { - self.events.request('code', function (abi, contractsJSON) { - self.events.request('pipeline:build', {abi, contractsJSON, modifiedAssets}, () => { - self.events.emit('outputDone'); - }); - }); - }); + this.registerModule('pipeline', {plugins: this.plugins, env: this.env, webpackConfigName: this.webpackConfigName}); + const pipeline = new Pipeline({ + this.events.on('code-generator-ready', function () { + pipeline.build(abi, contractsJSON, null, () => { } serviceMonitor() { diff --git a/lib/core/processes/processLauncher.js b/lib/core/processes/processLauncher.js index 3ceca6821..b99fb272e 100644 --- a/lib/core/processes/processLauncher.js +++ b/lib/core/processes/processLauncher.js @@ -1,8 +1,7 @@ const child_process = require('child_process'); const constants = require('../../constants'); const path = require('path'); -const utils = require('../../utils/utils'); -const LogHandler = require('../../utils/logHandler'); +const ProcessLogsApi = require('../../modules/process_logs_api'); let processCount = 1; class ProcessLauncher { @@ -31,13 +30,10 @@ class ProcessLauncher { this.exitCallback = options.exitCallback; this.embark = options.embark; this.logs = []; - this.logHandler = new LogHandler({events: this.events, logger: this.logger, processName: this.name, silent: this.silent}); + this.processLogsApi = new ProcessLogsApi({embark: this.embark, processName: this.name, silent: this.silent}); this.subscriptions = {}; this._subscribeToMessages(); - if (this.embark) { - this._registerAsPlugin(); - } } _isDebug() { @@ -53,7 +49,7 @@ class ProcessLauncher { self.logger.error(msg.error); } if (msg.result === constants.process.log) { - return self.logHandler.handleLog(msg); + return self.processLogsApi.logHandler.handleLog(msg); } if (msg.event) { return self._handleEvent(msg); @@ -71,30 +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)); - } - ); - } - // Handle event calls from the child process _handleEvent(msg) { const self = this; diff --git a/lib/modules/blockchain_listener/index.js b/lib/modules/blockchain_listener/index.js index 47c228deb..4a436db6d 100644 --- a/lib/modules/blockchain_listener/index.js +++ b/lib/modules/blockchain_listener/index.js @@ -1,54 +1,45 @@ -const LogHandler = require('../../utils/logHandler'); +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 { - constructor(embark, options) { + + /** + * @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 = options.ipc; - this.logHandler = new LogHandler({events: this.events, logger: this.logger, processName: PROCESS_NAME, silent: true}); + this.ipc = ipc; + this.processLogsApi = new ProcessLogsApi({embark: this.embark, processName: PROCESS_NAME, silent: true}); if (this.ipc.isServer()) { - this._listenToLogs(); + this._listenToBlockchainLogs(); } - - this._registerApi(); } - _listenToLogs() { + /** + * 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.logHandler.handleLog({logLevel, message}); + this.processLogsApi.logHandler.handleLog({logLevel, message}); }); } - - _registerApi() { - // This route is overriden by `processLauncher` when the blockchain - // process is launched (ie when not in blockchain standalone mode). - // The order is determined by the order in which the engine starts - // it's services, with `blockchain_process` coming before `web3`. - const apiRoute = '/embark-api/process-logs/' + PROCESS_NAME; - this.embark.registerAPICall( - 'ws', - apiRoute, - (ws, _req) => { - this.events.on('process-log-' + PROCESS_NAME, function (id, log) { - ws.send(JSON.stringify(Object.assign(log, {id})), () => {}); - }); - } - ); - this.embark.registerAPICall( - 'get', - apiRoute, - (req, res) => { - let limit = parseInt(req.query.limit, 10); - if (!Number.isInteger(limit)) limit = 0; - const result = this.logHandler.logs.map((log, id) => Object.assign(log, {id})).slice(limit); - res.send(JSON.stringify(result)); - } - ); - } } module.exports = BlockchainListener; diff --git a/lib/modules/embark_listener/index.js b/lib/modules/embark_listener/index.js new file mode 100644 index 000000000..be43e5631 --- /dev/null +++ b/lib/modules/embark_listener/index.js @@ -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; diff --git a/lib/modules/logger_api/index.js b/lib/modules/logger_api/index.js deleted file mode 100644 index 703883d5e..000000000 --- a/lib/modules/logger_api/index.js +++ /dev/null @@ -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; diff --git a/lib/modules/pipeline/index.js b/lib/modules/pipeline/index.js index 4899e0e6e..c6d97381d 100644 --- a/lib/modules/pipeline/index.js +++ b/lib/modules/pipeline/index.js @@ -8,6 +8,7 @@ const WebpackConfigReader = require('../pipeline/webpackConfigReader'); class Pipeline { constructor(embark, options) { + this.embark = embark; this.env = embark.config.env; this.buildDir = embark.config.buildDir; this.contractsFiles = embark.config.contractsFiles; @@ -194,6 +195,8 @@ class Pipeline { }); let built = false; const webpackProcess = new ProcessLauncher({ + embark: self.embark, + plugins: self.plugins, modulePath: utils.joinPath(__dirname, 'webpackProcess.js'), logger: self.logger, events: self.events, diff --git a/lib/modules/process_logs_api/index.js b/lib/modules/process_logs_api/index.js new file mode 100644 index 000000000..9ee789623 --- /dev/null +++ b/lib/modules/process_logs_api/index.js @@ -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.map((log, id) => Object.assign(log, {id})).slice(limit); + res.send(JSON.stringify(result)); + } + ); + } +} + +module.exports = ProcessLogsApi; diff --git a/lib/modules/solidity/index.js b/lib/modules/solidity/index.js index 83d78e470..e2dc4ba31 100644 --- a/lib/modules/solidity/index.js +++ b/lib/modules/solidity/index.js @@ -4,6 +4,7 @@ let SolcW = require('./solcW.js'); class Solidity { constructor(embark, options) { + this.embark = embark; this.logger = embark.logger; this.events = embark.events; this.ipc = options.ipc; @@ -68,7 +69,7 @@ class Solidity { if (self.solcAlreadyLoaded) { 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.solcW.load_compiler(function (err) { diff --git a/lib/modules/solidity/solcW.js b/lib/modules/solidity/solcW.js index b471e25c6..524f4db08 100644 --- a/lib/modules/solidity/solcW.js +++ b/lib/modules/solidity/solcW.js @@ -6,7 +6,8 @@ const uuid = require('uuid/v1'); class SolcW { - constructor(options) { + constructor(embark, options) { + this.embark = embark; this.logger = options.logger; this.events = options.events; this.ipc = options.ipc; @@ -40,6 +41,7 @@ class SolcW { return done(); } this.solcProcess = new ProcessLauncher({ + embark: self.embark, modulePath: utils.joinPath(__dirname, 'solcP.js'), logger: self.logger, events: self.events, diff --git a/lib/utils/logHandler.js b/lib/utils/logHandler.js index 63ad8a74b..18e43afcc 100644 --- a/lib/utils/logHandler.js +++ b/lib/utils/logHandler.js @@ -4,7 +4,20 @@ const utils = require('./utils'); // 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; @@ -12,33 +25,42 @@ class LogHandler { this.silent = silent; this.logs = []; - this.removedCount = 0; + this.id = 0; } /** - * Servers as an interception of logs, normalises the message output and - * adds metadata (timestamp, id) the data, - * 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 + * 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) { - if(!msg) return; + 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') { + if (typeof msg.message === 'string') { processedMessages = [msg.message]; - } else { + } 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(); @@ -49,15 +71,15 @@ class LogHandler { msg_clear: message.stripColors, logLevel: msg.logLevel, name: this.processName, - timestamp + timestamp, + id: ++this.id }; - if(this.logs.length >= MAX_LOGS){ + if (this.logs.length >= MAX_LOGS) { this.logs.shift(); - this.removedCount++; } - const id = this.logs.push(log) - 1 + this.removedCount; - this.events.emit(`process-log-${this.processName}`, id, log); - if (this.silent && msg.type !== 'error') { + 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]) { From 9a830c342362be059e28730b5a047b1749bd1fd3 Mon Sep 17 00:00:00 2001 From: emizzle Date: Wed, 24 Oct 2018 09:28:27 +0300 Subject: [PATCH 4/7] Remove default behavior of logging to file Default behavior of logging to file is no longer needed now that Embark log history can be properly served using the `ProcessLogsApi` and `LogHandler` classes. # Conflicts: # lib/core/logger.js # lib/modules/blockchain_process/blockchain.js --- lib/core/logger.js | 10 +++------- lib/modules/blockchain_process/blockchain.js | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/core/logger.js b/lib/core/logger.js index 0f6a1eba3..50030ff5d 100644 --- a/lib/core/logger.js +++ b/lib/core/logger.js @@ -14,13 +14,6 @@ class Logger { this.logFunction = options.logFunction || console.log; this.logFile = options.logFile; this.context = options.context; - // Use a default logFile if none is specified in the cli, - // in the format .embark/logs/embark_.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) { + if (!this.logFile) { + return; + } 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(' ')); }; diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index b92471a3f..7ffe7f058 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -22,7 +22,7 @@ var Blockchain = function(userConfig, clientClass) { this.isDev = userConfig.isDev; this.onReadyCallback = userConfig.onReadyCallback || (() => {}); this.onExitCallback = userConfig.onExitCallback; - this.logger = userConfig.logger || new Logger({logLevel: 'debug'}); + 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.isStandalone = userConfig.isStandalone; @@ -222,7 +222,7 @@ Blockchain.prototype.run = function () { // 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 self.child.stdout.on('data', (data) => { - this.logger.info(`${self.client.name} error: ${data}`); + self.logger.info(`${self.client.name} error: ${data}`); }); self.child.stderr.on('data', async (data) => { From dcdcfb5b3273f19844b212332156e54b609f6c20 Mon Sep 17 00:00:00 2001 From: emizzle Date: Thu, 25 Oct 2018 07:21:08 +0200 Subject: [PATCH 5/7] Minor fixes for rebase --- lib/core/engine.js | 15 +++++++++++---- lib/modules/blockchain_process/blockchain.js | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/core/engine.js b/lib/core/engine.js index debc1bef8..40a13c4ee 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -134,10 +134,17 @@ class Engine { } pipelineService(_options) { - this.registerModule('pipeline', {plugins: this.plugins, env: this.env, webpackConfigName: this.webpackConfigName}); - const pipeline = new Pipeline({ - this.events.on('code-generator-ready', function () { - pipeline.build(abi, contractsJSON, null, () => { + const self = this; + this.registerModule('pipeline', { + webpackConfigName: this.webpackConfigName + }); + this.events.on('code-generator-ready', function (modifiedAssets) { + self.events.request('code', function (abi, contractsJSON) { + self.events.request('pipeline:build', {abi, contractsJSON, modifiedAssets}, () => { + self.events.emit('outputDone'); + }); + }); + }); } serviceMonitor() { diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index 7ffe7f058..05538341e 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -239,7 +239,7 @@ Blockchain.prototype.run = function () { } self.readyCallback(); } - this.logger.info(`${self.client.name}: ${data}`); + self.logger.info(`${self.client.name}: ${data}`); }); self.child.on('exit', (code) => { From efa21a1915119cdda53a31a84ee9a130325d1d24 Mon Sep 17 00:00:00 2001 From: emizzle Date: Thu, 25 Oct 2018 08:01:48 +0200 Subject: [PATCH 6/7] Fix process logs not returning The API endpoint listening for a dump of process logs was not returning logs properly for two reasons: 1. The `id` field was being appended to each log. This had been moved to the `handleLog` function of the `LogHandler`. 2. The slice needed to grab logs from the end, so the `limit` was made negative on the `.slice()`. --- lib/modules/process_logs_api/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/process_logs_api/index.js b/lib/modules/process_logs_api/index.js index 9ee789623..95d38d508 100644 --- a/lib/modules/process_logs_api/index.js +++ b/lib/modules/process_logs_api/index.js @@ -28,7 +28,7 @@ class ProcessLogsApi { (req, res) => { let limit = parseInt(req.query.limit, 10); if (!Number.isInteger(limit)) limit = 0; - const result = this.logHandler.logs.map((log, id) => Object.assign(log, {id})).slice(limit); + const result = this.logHandler.logs.slice(limit * -1); res.send(JSON.stringify(result)); } ); From 6dccbd89029fb0fc40fb5b52fc1215e9cc1b9f46 Mon Sep 17 00:00:00 2001 From: Pascal Precht Date: Thu, 25 Oct 2018 12:37:16 +0200 Subject: [PATCH 7/7] fix: Ensure Embark process live updates are being retrieved by Cockpit --- embark-ui/src/containers/HomeContainer.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/embark-ui/src/containers/HomeContainer.js b/embark-ui/src/containers/HomeContainer.js index 43f786324..29f501514 100644 --- a/embark-ui/src/containers/HomeContainer.js +++ b/embark-ui/src/containers/HomeContainer.js @@ -39,18 +39,10 @@ class HomeContainer extends Component { } 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.listenToProcessLogs(processName); - } + this.props.fetchProcessLogs(processName, LOG_LIMIT); + this.props.listenToProcessLogs(processName); this.props.fetchContracts(); this.setState({activeProcess: processName});