/ lib / utils / logHandler.js
logHandler.js
 1  const utils = require('./utils');
 2  
 3  // define max number of logs to keep in memory for this process
 4  // to prevent runaway memory leak
 5  const MAX_LOGS = require('../constants').logs.maxLogLength;
 6  
 7  /**
 8   * Serves as a central point of log handling.
 9   */
10  class LogHandler {
11    
12    /**
13     * @param {Object} options Options object containing:
14     * - {EventEmitter} events Embark events
15     * - {Logger} logger Embark logger
16     * - {String} processName Name of the process for which it's logs 
17     *            are being handled.
18     * - {Boolean} silent If true, does not log the message, unless
19     *             it has a logLevel of 'error'. 
20     */
21    constructor({events, logger, processName, silent}) {
22      this.events = events;
23      this.logger = logger;
24      this.processName = processName;
25      this.silent = silent;
26  
27      this.logs = [];
28      this.id = 0;
29    }
30  
31    /**
32     * Servers as an interception of logs, normalises the message output, adds
33     * metadata (timestamp, id), stores the log in memory, then sends it to the 
34     * logger for output. Max number of logs stored in memory is capped by MAX_LOGS.
35     * 
36     * @param {Object} msg Object containing the log message (msg.message)
37     * @param {Boolean} alreadyLogged (optional, default = false) If true, prevents 
38     * the logger from logging the event. Generally used when the log has already 
39     * been logged using the Logger (which emits a "log" event), and is then sent 
40     * to `handleLog` for normalization. If allowed to log again, another event 
41     * would be emitted, and an infinite loop would occur. Setting to true will 
42     * prevent infinite looping.
43     * 
44     * @returns {void} 
45     */
46    handleLog(msg, alreadyLogged = false) {
47      if (!msg) return;
48  
49      // Sometimes messages come in with line breaks, so we need to break them up accordingly.
50      let processedMessages = [];
51  
52      // Ensure that `msg.message` is an array, so we process this consistently. Sometimes it
53      // is an Array, sometimes it is a string.
54      if (typeof msg.message === 'string') {
55        processedMessages = [msg.message];
56      } else if (Array.isArray(msg.message)) {
57        msg.message.forEach(message => {
58          if (Array.isArray(message)) message = message.join('\n');
59          let lines = message.split("\n");
60          lines.forEach(line => processedMessages.push(line));
61        });
62      } else if (typeof msg.message === 'object') {
63        processedMessages.push(JSON.stringify(msg.message));
64      }
65  
66      const timestamp = new Date().getTime();
67  
68      processedMessages.forEach((message) => {
69        const log = {
70          msg: message,
71          msg_clear: message.stripColors,
72          logLevel: msg.logLevel,
73          name: this.processName,
74          timestamp,
75          id: ++this.id
76        };
77        if (this.logs.length >= MAX_LOGS) {
78          this.logs.shift();
79        }
80        this.logs.push(log);
81        this.events.emit(`process-log-${this.processName}`, log);
82        if ((this.silent && msg.type !== 'error') || alreadyLogged) {
83          return;
84        }
85        if (this.logger[msg.type]) {
86          return this.logger[msg.type](utils.normalizeInput(message));
87        }
88        this.logger.debug(utils.normalizeInput(message));
89      });
90    }
91  }
92  
93  module.exports = LogHandler;