node-framework
Version:
node-framework
369 lines (301 loc) • 9.28 kB
JavaScript
/**
* @file index.js
* @author sekiyika (px.pengxing@gmail.com)
* @description
* logger
*/
var fs = require('fs');
var util = require('util');
var path = require('path');
var winston = require('winston');
var chalk = require('chalk');
module.exports = function (app) {
var isDebug = process.env.NODE_ENV === 'development';
var levelMap = {
silly: 0,
debug: 1,
verbose: 2,
info: 3,
warn: 4,
error: 5
};
function Logger() {
/**
* @type {String}
*/
this.appname;
/**
* @type {String}
*/
this.dir;
/**
* @type {number}
*/
this.maxsize;
/**
* @type {String}
*/
this.level;
/**
* @type {Array}
*/
this.transports;
/**
* @type {Array}
*/
this.wfTransports;
/**
* @type {winston.Logger}
*/
this.logger;
/**
* @type {winston.Logger}
*/
this.wfLogger;
/**
* @type {String}
*/
this.pathPrefix;
/**
* 保存profiler的句柄
* @type {Array<String>}
*/
this.profilers = [];
}
Logger.prototype.initialize = function () {
app.logger.dProfile('component:Logger:initialize');
this.appname = app.config.global.appname;
this.dir = app.config.logger.dir;
this.maxsize = app.config.logger.maxsize;
this.level = app.config.logger.level;
this.transports = app.config.logger.transports || [];
this.wfTransports = app.config.logger.wfTransports || [];
this.pathPrefix = this.dir + '/' + this.appname;
this.create();
app.logger.info('Component Logger initialize DONE');
app.logger.dProfile('component:Logger:initialize');
};
Logger.prototype.create = function () {
var options = { exitOnError: false };
var wfOptions = { exitOnError: false };
if (!fs.existsSync(this.dir)) {
fs.mkdirSync(this.dir);
}
var logFile = this.pathPrefix + '.log';
var wfLogFile = this.pathPrefix + '.log.wf';
var transports = this.transports;
var dailyRotatePattern = app.config.logger.dailyRotatePattern;
var clazz = !!app.config.logger.dailyRotatePattern
? winston.transports.DailyRotateFile
: winston.transports.File;
if (transports.length === 0) {
// 添加正常信息的transport
transports.push(new clazz({
filename: logFile,
maxsize: this.maxsize,
level: this.level,
json: false,
timestamp: getTimeStr,
datePattern: dailyRotatePattern
}));
if (!dailyRotatePattern) {
if (!fs.existsSync(logFile)) {
fs.openSync(logFile, 'w+');
}
}
}
var wfTransports = this.wfTransports;
if (wfTransports.length === 0) {
// 添加错误信息的transport
wfTransports.push(new clazz({
filename: wfLogFile,
maxsize: this.maxsize,
level: 'warn',
json: false,
timestamp: getTimeStr,
datePattern: dailyRotatePattern
}));
if (!dailyRotatePattern) {
if (!fs.existsSync(wfLogFile)) {
fs.openSync(wfLogFile, 'w+');
}
}
}
options.transports = transports;
wfOptions.transports = wfTransports;
var oldLogger = this.logger;
var oldWfLogger = this.wfLogger;
this.logger = new winston.Logger(options);
this.wfLogger = new winston.Logger(wfOptions);
// 避免内存泄露,销毁之前的logger
if (oldLogger) {
oldLogger.close();
}
if (oldWfLogger) {
oldWfLogger.close();
}
};
Logger.prototype.silly = function () {
this.log('silly', arguments);
};
Logger.prototype.debug = function () {
this.log('debug', arguments);
};
Logger.prototype.verbose = function () {
this.log('verbose', arguments);
};
Logger.prototype.info = function () {
this.log('info', arguments);
};
Logger.prototype.warn = function () {
this.log('warn', arguments);
};
Logger.prototype.error = function () {
this.log('error', arguments, this.wfLogger);
};
Logger.prototype.fatal = function () {
this.log('error', arguments, this.wfLogger);
};
/**
* 根据key返回当前key是否已经处于profile状态下
* @param key
* @returns {boolean}
*/
Logger.prototype.isProfiled = function (key) {
return this.profilers[key] ? true : false;
};
/**
* 会采用debug输出信息
* @param {String} key
*/
Logger.prototype.dProfile = function (key) {
var now = Date.now();
var then;
if (this.profilers[key]) {
then = this.profilers[key];
delete this.profilers[key];
var args = Array.prototype.slice.call(arguments);
args.push('duration: ' + chalk.yellow((now - then) + 'ms'));
this.debug.apply(this, args);
} else {
this.profilers[key] = now;
}
};
/**
* profile
* @param {String} key
*/
Logger.prototype.profile = function (key) {
var now = Date.now();
var then;
if (this.profilers[key]) {
then = this.profilers[key];
delete this.profilers[key];
var args = Array.prototype.slice.call(arguments);
args.push('duration: ' + (now - then) + 'ms');
this.info.apply(this, args);
} else {
this.profilers[key] = now;
}
};
/**
* 所有日志的最终处理函数
*
* @param {String} level
* @param {Object} args
* @param {winston.Logger} logger
*/
Logger.prototype.log = function (level, args, logger) {
// 节省性能,level低于当前设置的level,则直接返回
if (levelMap[this.level] > levelMap[level]) {
return;
}
args = Array.prototype.slice.call(args, 0);
while (args[args.length - 1] === null) {
args.pop();
}
var msg = '';
// 如果是打印error
if (args.length === 1 && args[0] instanceof Error) {
msg = args[0].stack || args[0].toString();
} else {
msg = util.format.apply(null, args);
}
var info = getPosition();
var prefix = ''
+ process.pid
+ ' ['
+ path.relative(app.config.global.appdir, info.currentFile)
+ ':'
+ info.currentLine
+ '] ';
logger = logger || this.logger;
// 将包含\n的字符分多行打印
msg = msg.split('\n');
msg.forEach(function (message) {
logger[level](prefix + message);
// 如果是debug环境,则在console中打印日志
if (isDebug) {
consoleLogAccordingToLevel(level, prefix + message);
}
});
};
var colorMap = {
silly: 'magenta',
verbose: 'cyan',
debug: 'blue',
info: 'green',
warn: 'yellow',
error: 'red'
};
/**
* colorize level and output it
*
* @param {String} level
* @param {String} str
*/
function consoleLogAccordingToLevel(level, str) {
var msg = level;
var color = colorMap[level];
if (chalk[color]) {
msg = chalk[color](level);
}
msg = msg + ' ' + str;
console.log(msg);
}
/**
* 获取错误打印日志的文件和行号
*
* @returns {{stack: (Error.stack|*), currentLine: number, currentFile: String}}
*/
function getPosition() {
var orig = Error.prepareStackTrace;
Error.prepareStackTrace = function (_, stack) {
return stack;
};
var err = new Error();
Error.captureStackTrace(err, arguments.callee);
var stack = err.stack;
Error.prepareStackTrace = orig;
return {
stack: stack,
currentLine: stack[2].getLineNumber(),
currentFile: stack[2].getFileName()
};
}
function getTimeStr() {
var now = new Date();
var month = now.getMonth() + 1;
var date = now.getDate();
var hour = now.getHours();
var min = now.getMinutes();
var sec = now.getSeconds();
month < 10 ? month = '0' + month : null;
date < 10 ? date = '0' + date : null;
hour < 10 ? hour = '0' + hour : null;
min < 10 ? min = '0' + min : null;
sec < 10 ? sec = '0' + sec : null;
return now.getFullYear() + '-' + month + '-' + date + ' ' + hour + ':' + min + ':' + sec;
}
return new Logger();
};