All files / lib/appenders multiprocess.js

39.39% Statements 26/66
25.81% Branches 8/31
41.18% Functions 7/17
39.39% Lines 26/66
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159    1x 1x   1x 1x                                                                   1x     1x                                                   1x 1x   1x     1x                                                                                                     1x 1x               1x 1x 1x 1x   1x       1x 1x 1x 1x 1x 1x           1x 1x 1x  
'use strict';
 
const log4js = require('../log4js');
const net = require('net');
 
const END_MSG = '__LOG4JS__';
const servers = [];
 
/**
 * Creates a server, listening on config.loggerPort, config.loggerHost.
 * Output goes to config.actualAppender (config.appender is used to
 * set up that appender).
 */
function logServer(config) {
  /**
   * Takes a utf-8 string, returns an object with
   * the correct log properties.
   */
  function deserializeLoggingEvent(clientSocket, msg) {
    let loggingEvent;
    try {
      loggingEvent = JSON.parse(msg);
      loggingEvent.startTime = new Date(loggingEvent.startTime);
      loggingEvent.level = log4js.levels.toLevel(loggingEvent.level.levelStr);
    } catch (e) {
      // JSON.parse failed, just log the contents probably a naughty.
      loggingEvent = {
        startTime: new Date(),
        categoryName: 'log4js',
        level: log4js.levels.ERROR,
        data: ['Unable to parse log:', msg]
      };
    }
 
    loggingEvent.remoteAddress = clientSocket.remoteAddress;
    loggingEvent.remotePort = clientSocket.remotePort;
 
    return loggingEvent;
  }
 
  const actualAppender = config.actualAppender;
 
  /* eslint prefer-arrow-callback:0 */
  const server = net.createServer(function serverCreated(clientSocket) {
    clientSocket.setEncoding('utf8');
    let logMessage = '';
 
    function logTheMessage(msg) {
      if (logMessage.length > 0) {
        actualAppender(deserializeLoggingEvent(clientSocket, msg));
      }
    }
 
    function chunkReceived(chunk) {
      let event;
      logMessage += chunk || '';
      if (logMessage.indexOf(END_MSG) > -1) {
        event = logMessage.substring(0, logMessage.indexOf(END_MSG));
        logTheMessage(event);
        logMessage = logMessage.substring(event.length + END_MSG.length) || '';
        // check for more, maybe it was a big chunk
        chunkReceived();
      }
    }
 
    clientSocket.on('data', chunkReceived);
    clientSocket.on('end', chunkReceived);
  });
 
  server.listen(config.loggerPort || 5000, config.loggerHost || 'localhost', function () {
    servers.push(server);
    // allow the process to exit, if this is the only socket active
    server.unref();
  });
 
  return actualAppender;
}
 
function workerAppender(config) {
  let canWrite = false;
  const buffer = [];
  let socket;
 
  function write(loggingEvent) {
    // JSON.stringify(new Error('test')) returns {}, which is not really useful for us.
    // The following allows us to serialize errors correctly.
    // Validate that we really are in this case
    if (loggingEvent && loggingEvent.stack && JSON.stringify(loggingEvent) === '{}') {
      loggingEvent = { stack: loggingEvent.stack };
    }
    socket.write(JSON.stringify(loggingEvent), 'utf8');
    socket.write(END_MSG, 'utf8');
  }
 
  function emptyBuffer() {
    let evt;
 
    /* eslint no-cond-assign:0 */
    while ((evt = buffer.shift())) {
      write(evt);
    }
  }
 
  function createSocket() {
    socket = net.createConnection(config.loggerPort || 5000, config.loggerHost || 'localhost');
    socket.on('connect', () => {
      emptyBuffer();
      canWrite = true;
    });
    socket.on('timeout', socket.end.bind(socket));
    // don't bother listening for 'error', 'close' gets called after that anyway
    socket.on('close', createSocket);
  }
 
  createSocket();
 
  return function log(loggingEvent) {
    if (canWrite) {
      write(loggingEvent);
    } else {
      buffer.push(loggingEvent);
    }
  };
}
 
function createAppender(config) {
  Eif (config.mode === 'master') {
    return logServer(config);
  }
 
  return workerAppender(config);
}
 
function configure(config, options) {
  let actualAppender;
  Eif (config.appender && config.mode === 'master') {
    log4js.loadAppender(config.appender.type);
    actualAppender = log4js.appenderMakers[config.appender.type](config.appender, options);
    config.actualAppender = actualAppender;
  }
  return createAppender(config);
}
 
function shutdown(done) {
  let toBeClosed = servers.length;
  servers.forEach(function (server) {
    server.close(function () {
      toBeClosed -= 1;
      Eif (toBeClosed < 1) {
        done();
      }
    });
  });
}
 
module.exports.appender = createAppender;
module.exports.configure = configure;
module.exports.shutdown = shutdown;