From 81ddd053e27f2b9c6f1bbca7bf7465ba347e995b Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Fri, 21 Jul 2017 08:35:36 +1000 Subject: [PATCH] feat(pm2): got tests for pm2 working --- lib/appenders/recording.js | 6 +-- lib/log4js.js | 7 +-- test/tap/cluster-test.js | 11 ++--- test/tap/pm2-support-test.js | 89 +++++++++++++++++++++++++++++++++--- 4 files changed, 94 insertions(+), 19 deletions(-) diff --git a/lib/appenders/recording.js b/lib/appenders/recording.js index 78992a4..f0fa2f2 100644 --- a/lib/appenders/recording.js +++ b/lib/appenders/recording.js @@ -2,7 +2,7 @@ const debug = require('debug')('log4js:recording'); -let recordedEvents = []; +const recordedEvents = []; function configure() { return function (logEvent) { @@ -12,11 +12,11 @@ function configure() { } function replay() { - return recordedEvents; + return recordedEvents.slice(); } function reset() { - recordedEvents = []; + recordedEvents.length = 0; } module.exports = { diff --git a/lib/log4js.js b/lib/log4js.js index 2184863..8677c7d 100644 --- a/lib/log4js.js +++ b/lib/log4js.js @@ -196,7 +196,11 @@ function configure(configurationFileOrObject) { // we only want one of the app instances to write logs if (config.pm2 && process.env[config.pm2InstanceVar] === '0') { debug('listening for PM2 broadcast messages'); + process.removeListener('message', receiver); process.on('message', receiver); + } else if (cluster.isMaster) { + cluster.removeListener('message', receiver); + cluster.on('message', receiver); } enabled = true; @@ -259,8 +263,5 @@ const log4js = { }; module.exports = log4js; - -cluster.on('message', receiver); - // set ourselves up configure(process.env.LOG4JS_CONFIG || defaultConfig); diff --git a/test/tap/cluster-test.js b/test/tap/cluster-test.js index 001ff17..f3e1dc6 100644 --- a/test/tap/cluster-test.js +++ b/test/tap/cluster-test.js @@ -20,14 +20,12 @@ if (cluster.isMaster) { masterLogger.info('this is master'); let workerLevel; - let workerId; cluster.on('message', (worker, message) => { - if (worker.type) { + if (worker.type || worker.topic) { message = worker; } - if (message.type === '::testing') { + if (message.type && message.type === '::testing') { workerLevel = message.level; - workerId = message.id; } }); @@ -67,10 +65,9 @@ if (cluster.isMaster) { // can't run the test in the worker, things get weird process.send({ type: '::testing', - level: workerLogger.level.toString(), - id: cluster.worker.id + level: workerLogger.level.toString() }); // test sending a badly-formed log message - process.send({ type: '::log4js-message', event: { cheese: 'gouda' } }); + process.send({ topic: 'log4js:message', data: { cheese: 'gouda' } }); cluster.worker.disconnect(); } diff --git a/test/tap/pm2-support-test.js b/test/tap/pm2-support-test.js index 863fd74..350726a 100644 --- a/test/tap/pm2-support-test.js +++ b/test/tap/pm2-support-test.js @@ -1,10 +1,87 @@ 'use strict'; const test = require('tap').test; +const cluster = require('cluster'); -test('PM2 Cluster support', (batch) => { - batch.test('should listen for messages if pm2 support enabled'); - batch.test('should write messages on NODE_APP_INSTANCE - 0'); - batch.test('should send messages with the correct format'); - batch.end(); -}); +// PM2 runs everything as workers +// - no master in the cluster (PM2 acts as master itself) +// - we will simulate that here (avoid having to include PM2 as a dev dep) +if (cluster.isMaster) { + // create two worker forks + // PASS IN NODE_APP_INSTANCE HERE + const appEvents = {}; + ['0', '1'].forEach((i) => { + cluster.fork({ NODE_APP_INSTANCE: i }); + }); + + cluster.on('message', (worker, msg) => { + if (worker.type || worker.topic) { + msg = worker; + } + if (msg.type === 'testing') { + appEvents[msg.instance] = msg.events; + } + + // we have to do the re-broadcasting that the pm2-intercom module would do. + if (msg.topic === 'log4js:message') { + for (const id in cluster.workers) { + cluster.workers[id].send(msg); + } + } + }); + + let count = 0; + cluster.on('exit', () => { + count += 1; + if (count === 2) { + test('PM2 Support', (batch) => { + batch.test('should not get any events when turned off', (t) => { + t.notOk(appEvents['0'].filter(e => e && e.data[0].indexOf('will not be logged') > -1).length); + t.notOk(appEvents['1'].filter(e => e && e.data[0].indexOf('will not be logged') > -1).length); + t.end(); + }); + + batch.test('should get events on app instance 0', (t) => { + t.equal(appEvents['0'].length, 2); + t.equal(appEvents['0'][0].data[0], 'this should now get logged'); + t.equal(appEvents['0'][1].data[0], 'this should now get logged'); + t.end(); + }); + + batch.test('should not get events on app instance 1', (t) => { + t.equal(appEvents['1'].length, 0); + t.end(); + }); + batch.end(); + }); + } + }); +} else { + const recorder = require('../../lib/appenders/recording'); + const log4js = require('../../lib/log4js'); + log4js.configure({ + appenders: { out: { type: 'recording' } }, + categories: { default: { appenders: ['out'], level: 'info' } } + }); + + const logger = log4js.getLogger('test'); + logger.info('this is a test, but without enabling PM2 support it will not be logged'); + + // we have to wait a bit, so that the process.send messages get a chance to propagate + setTimeout(() => { + log4js.configure({ + appenders: { out: { type: 'recording' } }, + categories: { default: { appenders: ['out'], level: 'info' } }, + pm2: true + }); + const anotherLogger = log4js.getLogger('test'); + anotherLogger.info('this should now get logged'); + }, 500); + + // we have to wait a bit, so that the process.send messages get a chance to propagate + setTimeout(() => { + const events = recorder.replay(); + process.send({ type: 'testing', instance: process.env.NODE_APP_INSTANCE, events: events }); + cluster.worker.disconnect(); + }, 1000); +}