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;