winston 日志模块
winston模块介绍
levels
const levels = {
error: 0,
warn: 1,
info: 2,
http: 3,
verbose: 4,
debug: 5,
silly: 6
};
winston.createLogger参数
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'combined.log' })
]
});
如上代码可以创建一个Logger
.
参数
- level 默认
info
只记录 小于等于info
级别的日志。 - levels 默认``,日志的优先级别和颜色
- format 格式化
- transports 日志的输出目标,可以是文件,控制台,http服务,或者流
- exitOnError true ,如果设置为false,处理到的异常不会造成 退出
- silent false,如果为真,所有异常都被禁用
### winston.formats
winston.formats由这个三方库实现logform
const alignedWithColorsAndTime = format.combine(
format.colorize(), // info.level在控制台中输出会有颜色
format.timestamp(), // 添加这个后,info中会有timestamp属性
format.align(),
format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
);
关于info对象,format.printf中的info就是info
对象。
它至少有两个属性 level
,message
.
format.colorize
,format.timestamp
这些被称为format
。那format是什么呢,实际上是对info的进一层加工,返回一个更改过后的info
const { format } = require('logform');
const volume = format((info, opts) => {
if (opts.yell) {
info.message = info.message.toUpperCase();
} else if (opts.whisper) {
info.message = info.message.toLowerCase();
}
return info;
});
多个format
可以通过format.combine
合并成一个。正如上边第一个demo中使用的方法一样。
一些内置的format
- align 对齐
- colorize 着色
format.colorize({
colors: {
info: "red"
}
}),
- combine 合并多个format
- errors
- json
- label 一个label标签 [label]....
format.label({
label: ""
})
- Logstash 转成纯json对象
- Metadata 除了level和message属性,其它都放入到meta属性下边
- padLevels 格式长度相同
- PrettyPrint 这个最好不要在生产模式使用
- simple 简单的模式
const { format } = require('logform');
const MESSAGE = Symbol.for('message');
const simpleFormat = format.simple();
const info = simpleFormat.transform({
level: 'info',
message: 'my message',
number: 123
});
console.log(info[MESSAGE]);
// info: my message {number:123}
- timestamp 时间,接收一个[fecha]()库可以理解的字符串。
format.timestamp({
format: 'HH:mm:ss YY/MM/DD'
})
- Uncolorize 去除颜色
自定义levels
winston定义了两种levels,默认用的是npm levels
{
error: 0,
warn: 1,
info: 2,
http: 3,
verbose: 4,
debug: 5,
silly: 6
}
当然支持自定义levels
const myCustomLevels = {
levels: {
foo: 0,
bar: 1,
baz: 2,
foobar: 3
},
colors: {
foo: 'blue',
bar: 'green',
baz: 'yellow',
foobar: 'red'
}
};
const customLevelLogger = winston.createLogger({
levels: myCustomLevels.levels
});
customLevelLogger.foobar('some foobar level-ed message');
多个传输
如果需要把成功级别的消息传入一个log文件,错误的传入另一个log文件,那么可以这么使用
由level针对级别。
const logger = winston.createLogger({
transports: [
new winston.transports.File({
filename: 'combined.log',
level: 'info'
}),
new winston.transports.File({
filename: 'errors.log',
level: 'error'
})
]
});
常见的transport
每个不同的transport都可以使用不同的format参数。毕竟要写入的格式与内容不同。
const logger = winston.createLogger({
transports: [
new winston.transports.File({
filename: 'error.log',
level: 'error',
format: winston.format.json()
}),
new transports.Http({
level: 'warn',
format: winston.format.json()
}),
new transports.Console({
level: 'info',
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
]
});
异常处理
winston的异常处理。
Handling Uncaught Exceptions with winston
未捕获的异常,winston可以处理。
const logger = createLogger({
transports: [
new transports.File({ filename: 'combined.log' })
],
exceptionHandlers: [
new transports.File({ filename: 'exceptions.log' })
]
});
winston将异常保留在对应的log文件中后会退出
设置exitOnError: false
,可以取消退出。
未捕获的promise
使用rejectionHandlers
const { createLogger, transports } = require('winston');
// Enable rejection handling when you create your logger.
const logger = createLogger({
transports: [
new transports.File({ filename: 'combined.log' })
],
rejectionHandlers: [
new transports.File({ filename: 'rejections.log' })
]
});
nest中集成winston模块
安装nest-winston
npm install nest-winston
main.ts全局winston
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
async function bootstrap() {
// 关闭默认的logger
const app = await NestFactory.create(AppModule,{
logger: false,
});
const nestWinston = app.get(WINSTON_MODULE_NEST_PROVIDER);
//全局的logger
app.useLogger(nestWinston);
// 异常拦截写入日志
app.useGlobalFilters(new HttpExceptionFilter(nestWinston.logger))
await app.listen(3003);
}
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, Inject } from '@nestjs/common';
import { Error as MongoError } from "mongoose"
import { Request, Response } from 'express';
import * as _ from "lodash"
import {Logger} from "winston";
import {ErrorCode} from "../constant/error"
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
constructor(private readonly logger: Logger) {}
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
let status = HttpStatus.OK;
let message;
let errorCode: number;
if(exception instanceof MongoError) {
errorCode = ErrorCode.Mongoose.CODE; //Mongo Error Code
message = exception.message || ErrorCode.Mongoose.MESSAGE
}else if (exception.getStatus){
const httpException: HttpException = exception as HttpException;
if(httpException.message && typeof httpException.message.errorCode !== 'undefined' ) {
errorCode = httpException.message.errorCode;
message = httpException.message.message || ErrorCode.getMessageByCode(httpException.message.errorCode); //
}else{
errorCode = httpException.getStatus(); // 未登陆 401,
message = _.isObject(httpException.message) ? ((httpException.message as any).error) : ""
status = errorCode;
}
}else{
errorCode = ErrorCode.JSERROR.CODE;
message = exception.message;
this.logger.error( {message: [ exception.message, exception.stack ].join('\n'),})
}
response.status(status).json({
errorCode,
message,
data: ""
});
}
}
app.module.ts
import {WinstonModule} from "nest-winston";
import * as winston from 'winston';
import DailyRotateFile = require("winston-daily-rotate-file");
const format = winston.format;
@Module({
imports: [
WinstonModule.forRoot({
exitOnError: false,
format: format.combine(
format.colorize(),
format.timestamp({
format: 'HH:mm:ss YY/MM/DD'
}),
format.label({
label: "测试"
}),
format.splat(),
format.printf( info => {
return `${info.timestamp} ${info.level}: [${info.label}]${info.message}`
}),
),
transports: [
new winston.transports.Console({
level: 'info',
}),
new DailyRotateFile({
filename: 'logs/application-%DATE%.log',
datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
}),
],
}),
],
controllers: [],
providers: [AppService],
})
export class AppModule {
}