| const cluster = require('cluster'); |
| const debug = require('debug')('log4js:clustering'); |
| const LoggingEvent = require('./LoggingEvent'); |
| const configuration = require('./configuration'); |
| |
| const listeners = []; |
| |
| let disabled = false; |
| let pm2 = false; |
| let pm2InstanceVar = 'NODE_APP_INSTANCE'; |
| |
| const isPM2Master = () => pm2 && process.env[pm2InstanceVar] === '0'; |
| const isMaster = () => disabled || cluster.isMaster || isPM2Master(); |
| |
| const sendToListeners = (logEvent) => { |
| listeners.forEach(l => l(logEvent)); |
| }; |
| |
| // in a multi-process node environment, worker loggers will use |
| // process.send |
| const receiver = (worker, message) => { |
| // prior to node v6, the worker parameter was not passed (args were message, handle) |
| debug('cluster message received from worker ', worker, ': ', message); |
| if (worker.topic && worker.data) { |
| message = worker; |
| worker = undefined; |
| } |
| if (message && message.topic && message.topic === 'log4js:message') { |
| debug('received message: ', message.data); |
| const logEvent = LoggingEvent.deserialise(message.data); |
| sendToListeners(logEvent); |
| } |
| }; |
| |
| configuration.addListener((config) => { |
| // clear out the listeners, because configure has been called. |
| listeners.length = 0; |
| |
| disabled = config.disableClustering; |
| pm2 = config.pm2; |
| pm2InstanceVar = config.pm2InstanceVar || 'NODE_APP_INSTANCE'; |
| |
| debug(`clustering disabled ? ${disabled}`); |
| debug(`cluster.isMaster ? ${cluster.isMaster}`); |
| debug(`pm2 enabled ? ${pm2}`); |
| debug(`pm2InstanceVar = ${pm2InstanceVar}`); |
| debug(`process.env[${pm2InstanceVar}] = ${process.env[pm2InstanceVar]}`); |
| |
| // just in case configure is called after shutdown |
| if (pm2) { |
| process.removeListener('message', receiver); |
| } |
| if (cluster.removeListener) { |
| cluster.removeListener('message', receiver); |
| } |
| |
| if (config.disableClustering) { |
| debug('Not listening for cluster messages, because clustering disabled.'); |
| } else if (isPM2Master()) { |
| // PM2 cluster support |
| // PM2 runs everything as workers - install pm2-intercom for this to work. |
| // we only want one of the app instances to write logs |
| debug('listening for PM2 broadcast messages'); |
| process.on('message', receiver); |
| } else if (cluster.isMaster) { |
| debug('listening for cluster messages'); |
| cluster.on('message', receiver); |
| } else { |
| debug('not listening for messages, because we are not a master process'); |
| } |
| }); |
| |
| module.exports = { |
| onlyOnMaster: (fn, notMaster) => (isMaster() ? fn() : notMaster), |
| isMaster: isMaster, |
| send: (msg) => { |
| if (isMaster()) { |
| sendToListeners(msg); |
| } else { |
| if (!pm2) { |
| msg.cluster = { |
| workerId: cluster.worker.id, |
| worker: process.pid |
| }; |
| } |
| process.send({ topic: 'log4js:message', data: msg.serialise() }); |
| } |
| }, |
| onMessage: (listener) => { |
| listeners.push(listener); |
| } |
| }; |