4

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<Response>();
        const request = ctx.getRequest<Request>();
        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 {
      
    }
    

    最普通的一个
    301 声望41 粉丝

    永远不要做你擅长的事。