官方文档:
注意别看错版本,2.x版和3.x版的是有差别的
3.x版: https://github.com/winstonjs/winston
2.x版: https://github.com/winstonjs/winston/tree/2.x
这里我用的是2.4.1版.
1.从最简单的开始,引入winston后使用默认的logger
1.1 利用默认的logger打印出一些log信息
var winston = require('winston');
winston.log('info', 'Hello distributed log files!');
winston.info('Hello again distributed logs');
winston.level = 'debug';
winston.log('debug', 'Now my debug messages are written to console!');
控制台输出:
注意因为winston写log调用的是回调函数,所以每次运行这段代码log的输出顺序不一定一样。
info: Hello distributed log files!
info: Hello again distributed logs
debug: Now my debug messages are written to console!
当我运行第四次输出的顺序是:
info: Hello distributed log files!
debug: Now my debug messages are written to console!
info: Hello again distributed logs
1.2 修改log的输出目的地
默认情况下,默认的logger只将内容输出到Console。但肯定可以修改的。有两种方式修改log的输出目的地。
1.2.1 你可以通过winston.add(), winston.remove()来添加和移除log传送的途径。 下面代码将详细的log输出到脚本同级目录下的somefile.log里,而且还会带上timestamp**
'use strict';
const winston = require('winston');
/*
默认情况下,仅在默认记录器上设置控制台传输。您可以通过add()和remove()方法添加或删除传输:
*/
winston.add(winston.transports.File, { filename: 'somefile.log' });
winston.remove(winston.transports.Console); //关闭了控制台输出
/* use default logger
可以通过winston模块直接访问默认记录器。可以在默认记录器上使用您可以在记录器实例上调用的任何方法:
*/
winston.log('info', 'Hello distributed log files!');
winston.info('Hello again distributed logs');
winston.level = 'debug';
winston.log('debug', 'Now my debug messages are written to console!');
1.2.2 通过configure() 一次性修改配置, 为什么说一次性呢?因为transports是winston的一个属性,是一组定义log的传出方式,第一种方法的add, remove操作的其实就是这个transports列表
下面这种方式,达到跟1.2.1 的例子一样的效果,而不用winston.remove(winston.transports.Console);
/*
或者通过一次调用configure()来完成:
*/
'use strict';
const winston = require('winston');
winston.configure({
transports: [
new (winston.transports.File)({ filename: 'somefile.log' })
]
});
/* use default logger
可以通过winston模块直接访问默认记录器。可以在默认记录器上使用您可以在记录器实例上调用的任何方法:
*/
winston.log('info', 'Hello distributed log files!');
winston.info('Hello again distributed logs');
winston.level = 'debug';
winston.log('debug', 'Now my debug messages are written to console!');
log输出到file中是以追加的方式输入,这里我运行了2次就有两次一样的输出
2. 接下来说下如何自定义和使用logger, 用default logger总会受限。
2.1 自己实例化并使用logger,一般由3个步骤要走。
a. 用new (wiston.Logger)创建一个logger对象
b. 指定transports列表,说明log要输出到哪里,这里你依旧可以用操作default logger那样用add(), remove()来添加删除transports.
c. 指定输出的log信息的等级,有2种方式, 如下,这两种写法是等同的 .
这里可以看到你可以用哪些log level :
https://github.com/winstonjs/winston/tree/2.x#using-logging-levels
//You can pass a string representing the logging level to the log() method or use the level specified methods defined on every winston Logger.
//logger is an instance of 'new (wiston.Logger)'
logger.error("I am an error");
logger.log('error', "I am an error");
来一段代码:
'use strict';
const winston = require('winston');
const logger = new (winston.Logger)({ //这个圆括号可有可无
level: 'info',
transports: [
//transports用于指定log要输出到哪里,这里输出到Console(大家熟悉的控制台)
//并且打开了颜色控制开关
new (winston.transports.Console)({ colorize: true }),//Console可以不要参数:new (winston.transports.Console)(),
new (winston.transports.File)({ filename: 'somefile.log' })
]
});
logger.level = 'debug';
logger.info('Hello world');
logger.debug('Debugging info');
logger.error("I am an error"); //这行代码和下面那行的效果一模一样
logger.log('error', "I am an error");
这里你依旧可以用操作default logger那样用add(), remove()来添加删除transports.
logger
.add(winston.transports.File)
.remove(winston.transports.Console);
2.2接下来说说exceptionHandlers
当Exception被抛出时,我们往往希望这个exception可以输出到特定的地方让我们可以troubleshooting.
你可以这么设置, 通过设置.handleExceptions()或者设置exceptionHandlers的属性
winston.handleExceptions(new winston.transports.File({ filename: 'path/to/exceptions.log' }));
var logger = new (winston.Logger)({
transports: [
new winston.transports.File({ filename: 'path/to/all-logs.log' })
],
exceptionHandlers: [
new winston.transports.File({ filename: 'path/to/exceptions.log' })
]
});
今天先到这里,困了。
有些人会遇到下面的问题,那是因为版本错了。比如我这里package.json里指定的是2.4.1版,但是winston.createLogger()是3.x版后才有的。
当我下载了3.0.0版,这段代码就正常工作了:
参考:
https://github.com/winstonjs/winston/tree/2.x
https://github.com/winstonjs/winston
http://thisdavej.com/using-winston-a-versatile-logging-library-for-node-js/
https://github.com/winstonjs/winston/issues/1101
最后附上一份结合KOA框架的logger.js
const path = require('path'),
winston = require('winston');
module.exports = (app) => {
let transports = [];
transports.push(new (winston.transports.Console)({
colorize: true,
level: 'info',
prettyPrint: _jsonPrettyPrint
}));
let workerIdSuffix = process.env.workerId ? ("." + process.env.workerId) : "";
transports.push(new (winston.transports.File)({
filename: path.join(app.conf.get('log.path'), 'server.log' + workerIdSuffix),
json: false,
level: 'info',
prettyPrint: _jsonPrettyPrint
}));
app.logger = new (winston.Logger)({
transports: transports,
exceptionHandlers: [
new (winston.transports.File)({
filename: path.join(app.conf.get('log.path'), 'error.log' + workerIdSuffix),
json: false,
prettyPrint: _jsonPrettyPrint
}),
new (winston.transports.Console)({
colorize: true,
prettyPrint: _jsonPrettyPrint
})
]
});
let accessLogger = new (winston.Logger)({
transports: [new (winston.transports.File)({
filename: path.join(app.conf.get('log.path'), 'access.log'),
json: false,
level: 'info',
formatter: (options) => options.message
})]
});
app.accessLogger = {
log: function () {
this.request._endTime = this.request._endTime || new Date;
let request = this.request,
response = this.response,
elapsed = request._endTime - request._startTime,
startTime = moment(request._startTime).format('DD/MMM/YYYY HH:mm:ss ZZ'),
instanceId = process.env.workerId ? `instance.${process.env.workerId}` : '';
accessLogger.info(`"${request.ip}" [${startTime}] "${request.method} ${request.url}" "${request.header['user-agent']}" ${response.status} ${elapsed}ms "${instanceId}"`);
}
};
app.use(async (ctx, next) => {
ctx.request._startTime = new Date();
await next();
ctx.request._endTime = new Date();
app.accessLogger.log.call(ctx);
});
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
if (err instanceof BadRequestError) {
_handleBadRequestError(ctx, err)
} else {
_handleError(ctx, err);
}
ctx.app.emit('error', err, ctx);
}
});
};
function _handleBadRequestError(context, err) {
context.status = 400;
context.body = {
error: err.message
}
}
function _handleError(context, err) {
context.status = err.status || 500;
if (err.response && err.response.body && err.response.body.error) {
context.body = err.response.body;
} else {
let cause = err.response && err.response.error ? err.response.error : err;
context.body = {
error: cause.message,
stack: cause.stack ? cause.stack : cause
};
}
}
function _jsonPrettyPrint(obj) {
return JSON.stringify(obj);
}