winston构建koa日志收集
文章简概
学习和熟悉winston.js,并通过集成为中间件,在Koa中构建为日志上报模块。
winston
winston是一个 logger js,主要用于打印日志,它提供了丰富的API,足以满足我们的需求。通过对官方文档的一些研究,总结了一下具体的使用指南,并尝试集成为中间件用于Koa.js框架中进行使用。
Installation
npm install winston
Usage
winston可以通过createLogger简单的实现一个日志API。
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
defaultMeta: {service: 'user-service'},
transports: [
//
// - Write to all logs with level `info` and below to `combined.log`
// - Write all logs error (and below) to `error.log`.
//
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
您也可以直接通过require(‘winston’)生成logger(如上述代码)进行日志记录,通过判断运行环境,在非生产mode下使用不同的格式。
Logging
winston有不同的类型的日志,分为error,warn,info,verbose,debug,silly,通过一下方法赋予这些类型各自的权重。
const levels = {
error: 0,
warn: 1,
info: 2,
verbose: 3,
debug: 4,
silly: 5
};
levels 用于对当前的日志进行排序,可以的以上5种类型,levels 越低则越严重。
Creating your own Logger
可以通过 winston.createLogger 创建一个logger,如以下例子:
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'combined.log' })
]
});
一个logger支持传入以下类型的参数:
Name | Default | Description |
---|---|---|
level | ‘info’ | Log only if info.level less than or equal to this level |
levels | winston.config.npm.levels | Levels (and colors) representing log priorities |
format | winston.format.json | Formatting for info messages (see: Formats) |
transports | [] (No transports) | Set of logging targets for info messages |
exitOnError | true | If false, handled exceptions will not cause process.exit |
silent | false | If true, all logs are suppressed |
//// Logging//
// 打印的两种方式,当然第二种更加简洁
logger.log({
level: 'info',
message: 'Hello distributed log files!'
});
logger.info('Hello again distributed logs');
在一个logger中你可以通过add or remove 去添加或者删除 transports:
const files = new winston.transports.File({ filename: 'combined.log' });
const console = new winston.transports.Console();
logger
.clear() // Remove all transports
.add(console) // Add console transport
.add(files) // Add file transport
.remove(console); // Remove console transport
通过如下方式可重新配置 logger , 包括日志等等
const logger = winston.createLogger({
level: 'info',
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'combined.log' })
]
});
//// Replaces the previous transports with those in the// new configuration wholesale.//
const DailyRotateFile = require('winston-daily-rotate-file');
logger.configure({
level: 'verbose',
transports: [
new DailyRotateFile(opts)
]
});
String interpolation
log 支持以下方式输出日志,当然可以简化为 logger.info 而简化掉 第一个参数 ‘info’
logger.log('info', 'test message %s', 'my string');
// info: test message my string
logger.log('info', 'test message %d', 123);
// info: test message 123
logger.log('info', 'test message %j', {number: 123}, {});
// info: test message {"number":123}// meta = {}
logger.log('info', 'test message %s, %s', 'first', 'second', {number: 123});
// info: test message first, second// meta = {number: 123}
logger.log('info', 'test message', 'first', 'second', {number: 123});
// info: test message first second// meta = {number: 123}
logger.log('info', 'test message %s, %s', 'first', 'second', {number: 123}, function(){});
// info: test message first, second// meta = {number: 123}// callback = function(){}
logger.log('info', 'test message', 'first', 'second', {number: 123}, function(){});
// info: test message first second// meta = {number: 123}// callback = function(){}
demo举例
// logger.js
const {
createLogger,
format,
transports
} = require('winston');
const {
combine,
timestamp,
label,
printf
} = format;
// 对logger进行基础配置
const loggerConfig = {
levels: {
error: 0,
warn: 1,
info: 2,
verbose: 3,
debug: 4,
silly: 5
},
color: {
error: 'red',
debug: 'blue',
warn: 'yellow',
data: 'grey',
log: 'green',
verbose: 'cyan',
silly: 'magenta'
},
// 自定义输出的格式
// 参数 info 指的是在 createLogger 函数的 变量中的 format 变量下的 combine所包含的变量
// 如: info.label 事实上拿到的是下方的 labelName
logFormat: printf(info => {
return `[${info.date}] [${info.label}] [${info.level}] - ${info.message}`;
}),
// 日志文件绝对路径
logFileName: logdir + '/' + date + '.log'
};
const logger = function (labelName) {
const _labelName = labelName && /^[\S]+$/.test(labelName) ? labelName : '默认';
return createLogger({
levels: loggerConfig.levels,
format: combine(
label({
label: _labelName
}),
// 时间戳
timestamp(),
// 设置颜色
format.colorize({
all: true
}),
// 开启插值功能
format.splat(),
// 输出格式的配置
loggerConfig.logFormat
),
// 日志的相关配置
transports: [
// 控制台输出日志
new transports.Console(),
// 绑定日志文件
new transports.File({
level: 'error',
filename: loggerConfig.logFileName,
colorize: false
})
]
});
};
module.exports = logger; // 暴露logger对象
// app.js
// 如何使用
const logger = require('./logger')('错误收集');
logger.error('错误: %s', err.stack);
// 扩展,制作为Koa中间件,配合使用app.use, 需异步!
// 利用try-catch 抓错误,并使用 logger 处理
// 由于Koa的洋葱模型,在走完 ' start ' 之后,next走向下个中间件,在下方的报错会向上回溯
// 从而被try-catch 抓到,如果说放置在业务代码之后,事实上是抓不到错误的
app.use(
// 中间件部分,务必写在页面业务代码之前
async (ctx, next) => {
try {
console.log('start');
await next();
} catch (err) {
logger.error('错误: %s', err.stack);
}
}
);
// Koa中的请求会依次走过被 app.use 的中间件,此处可以对相应的请求进行上报日志
// 获取行为 如 duration '请求耗时' 等等数据
app.use(
async (ctx, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
logger.info('[%s] %s %s (%s ms)', ctx.method, ctx.url, ctx.ip, duration);
}
);
参考文章
Winston Github https://github.com/winstonjs/winston
Winston docs 文档地址: https://github.com/winstonjs/winston/tree/2.x