slogger-repo/lib/LogPrinter.js
const path = require('path');
const fs = require('fs');
const config = require('./config');
const LogstashSender = require('./senders/LogstashSender');
const LOG_LEVEL_MAP = config.LOG_LEVEL_MAP;
const NEW_LINE_SEPARATOR = require('os').EOL;
// var LOG_CACHE = '';
const consoleStream = process.stdout;
/**
*
* @typedef LogFileItem
*
* @property {String} filename The file to save the log.
* @property {String} category The category of the log , it can be the log level, such as `debug` `info` `warn` `error`, or it can be a custom string.
*/
/**
* @typedef LogstashItem
*
* @property {Logstash} server The `Logstash` server.
* @property {String} category The category of the log , it can be the log level, such as `debug` `info` `warn` `error`, or it can be a custom string.
*/
/**
* Format the Date to a string.
*
* @private
* @param {Date} date
*/
function dateFormat(date) {
return date.getFullYear()+'-' + (date.getMonth() + 1) + '-'+date.getDate() + ' '+date.getHours()+':'+date.getMinutes() + ':'+date.getSeconds() + '.' + date.getMilliseconds();
}
function myFormat(params) {
var str = '';
if (!params) {
return str;
}
var element;
for (var i=0,len=params.length;i<len;i++) {
element = params[i];
if (element instanceof Error) {
str += ' ' + element.stack + NEW_LINE_SEPARATOR;
} else if (typeof(element) === 'object') {
str += ' ' + JSON.stringify(element);
} else {
str += ' ' + element;
}
}
return str;
}
function getRealFilename(filename,clusterNum) {
if (!clusterNum) {
return filename;
}
const dir = path.dirname(filename);
const ext = path.extname(filename);
if (ext) {
return dir + '/' + path.basename(filename,ext) + '-' + clusterNum + ext;
}
return filename + '-' + clusterNum;
}
/**
* @private
* @param {Object} options
* @param {Number} [options.flushInterval=0]
* @param {LogFileItem[]} [options.logFiles=undefined]
* @param {LogstashItem[]} [options.logstashes=undefined]
* @param {Boolean=} [options.disableTimePrefix=false] Whether disable the time perfix.
* @param {String=} [options.projectName=''] The name of project which use slogger.
*/
function LogPrinter(options) {
this._flushInteval = options.flushInterval|| 0;
this._logCache = '';
const logFiles = options.logFiles || [];
this._disableTimePrefix = options.disableTimePrefix;
this._levelFileMap = {};
this._levelFileLen = 0;
this._levelLogstashMap = {};
this._clusterNum = process.env.NODE_APP_INSTANCE;
this._projectName = options.projectName || '';
if (logFiles.length > 0) {
for(var i=0,len=logFiles.length;i<len;i++) {
const logFileConfig = logFiles[i];
const filename = logFileConfig.filename;
const category = logFileConfig.category;
if (!this._levelFileMap[category]) {
this._levelFileMap[category] = {
logCache:'',
streams:[]
};
this._levelFileLen++;
}
this._levelFileMap[category].streams.push(
fs.createWriteStream(getRealFilename(filename,this._clusterNum), {'flags': 'a'})
);
}
}
const logstashes = options.logstashes || [];
for (var i=0,len=logstashes.length;i<len;i++) {
const lsConfig = logstashes[i];
this._levelLogstashMap[lsConfig.category] = new LogstashSender({
logstash:lsConfig.server,
delayTime:this._flushInteval
}) ;
}
if (this._flushInteval > 0) {
this._flushLog();
}
}
LogPrinter.prototype._wirteToStream = function(fileConfig) {
var streams = fileConfig.streams;
var content = fileConfig.logCache;
for(var i=0,len=streams.length;i<len;i++) {
streams[i].write(content);
}
};
LogPrinter.prototype._flushFileStream = function() {
var _this = this;
setImmediate(function doFlushFileStream() {
for (var level in _this._levelFileMap) {
var fileConfig = _this._levelFileMap[level];
if (!fileConfig.logCache) {
continue;
}
_this._wirteToStream(fileConfig);
fileConfig.logCache = '';
}
});
};
LogPrinter.prototype._flushLog = function() {
var _this = this;
setTimeout(function flushTimeout() {
if (_this._logCache) {
consoleStream.write(_this._logCache);
// console.info(_this._logCache);
_this._logCache = '';
// stdout.uncork();
}
if (_this._levelFileLen > 0) {
_this._flushFileStream();
}
_this._flushLog();
},this._flushInteval);
};
LogPrinter.prototype.print = function(args,level) {
var config = LOG_LEVEL_MAP[level] || {color : ''};
var prefix = config.color;
if (!this._disableTimePrefix) {
prefix += dateFormat(new Date());
}
prefix += ' ['+(config.showName || level)+'] \x1b[0m';
var len = args.length;
var params = new Array(len+1);
params[0] = prefix;
for (var i=1;i<=len;i++) {
params[i] = args[i-1];
}
var logContent = myFormat(params) + NEW_LINE_SEPARATOR;
var fileConfig = this._levelFileMap[level];
if (this._flushInteval > 0) {
this._logCache += logContent;
if (fileConfig) {
fileConfig.logCache += logContent;
}
} else {
consoleStream.write(logContent);
if (fileConfig) {
fileConfig.logCache = logContent;
this._wirteToStream(fileConfig);
}
}
var logstashStream = this._levelLogstashMap[level];
if (logstashStream) {
logstashStream.addData({projectName:this._projectName,level,logContent});
}
};
module.exports = LogPrinter;