官方文檔:
注意別看錯版本,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調(diào)用的是回調(diào)函數(shù),所以每次運行這段代碼log的輸出順序不一定一樣。
info: Hello distributed log files!
info: Hello again distributed logs
debug: Now my debug messages are written to console!
當(dāng)我運行第四次輸出的順序是:
info: Hello distributed log files!
debug: Now my debug messages are written to console!
info: Hello again distributed logs
1.2 修改log的輸出目的地
默認情況下,默認的logger只將內(nèi)容輸出到Console。但肯定可以修改的。有兩種方式修改log的輸出目的地。
1.2.1 你可以通過winston.add(), winston.remove()來添加和移除log傳送的途徑。 下面代碼將詳細的log輸出到腳本同級目錄下的somefile.log里,而且還會帶上timestamp**
'use strict';
const winston = require('winston');
/*
默認情況下,僅在默認記錄器上設(shè)置控制臺傳輸。您可以通過add()和remove()方法添加或刪除傳輸:
*/
winston.add(winston.transports.File, { filename: 'somefile.log' });
winston.remove(winston.transports.Console); //關(guān)閉了控制臺輸出
/* use default logger
可以通過winston模塊直接訪問默認記錄器??梢栽谀J記錄器上使用您可以在記錄器實例上調(diào)用的任何方法:
*/
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);
/*
或者通過一次調(diào)用configure()來完成:
*/
'use strict';
const winston = require('winston');
winston.configure({
transports: [
new (winston.transports.File)({ filename: 'somefile.log' })
]
});
/* use default logger
可以通過winston模塊直接訪問默認記錄器??梢栽谀J記錄器上使用您可以在記錄器實例上調(diào)用的任何方法:
*/
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)創(chuàng)建一個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(大家熟悉的控制臺)
//并且打開了顏色控制開關(guān)
new (winston.transports.Console)({ colorize: true }),//Console可以不要參數(shù):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
當(dāng)Exception被拋出時,我們往往希望這個exception可以輸出到特定的地方讓我們可以troubleshooting.
你可以這么設(shè)置, 通過設(shè)置.handleExceptions()或者設(shè)置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版后才有的。

當(dāng)我下載了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

最后附上一份結(jié)合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);
}