luoway/nodejs-roadmap

日志实践

Opened this issue · 0 comments

luoway commented

虽然 console 支持 log、info、warn、error、debug 等方法,但它们只能输出日志到标准输出、标准错误。相当于实际只有 log、error 两个日志等级,需要开发以进一步细分。

使用日志框架如 winston、log4js,可以满足日志实践中的多数需求。

winston

winston 旨在成为支持多种传输的简单通用日志记录库,但它的API用法一点都不简单。

  • 自定义日志实例,将日志打印到控制台

    const { createLogger, transports } = require("winston")
    
    const logger = createLogger({
      transports: [new transports.Console()], // 将日志打印到控制台
    })
    
    logger.info('logger info')
    logger.log({
      level: 'info',
      message: 'logger log info'
    })

    控制台输出:

    {"level":"info","message":"logger info"}
    {"level":"info","message":"logger log info"}
  • 自定义日志实例,将日志输出到文件

    const { createLogger, transports, format } = require("winston")
    const { combine, label, timestamp, json } = format
    
    const logger = createLogger({
      format: combine(
        label({label: 'this is label'}), // 附加内容
        timestamp(), // 附加时间戳
        json() // 按json输出
      ),
      // 输出到文件
      transports: [new transports.File({ filename: 'test.log' })],
    })
    
    logger.info('logger info')
    logger.log({
      level: 'info',
      message: 'logger log info'
    })

    执行后 test.log 文件内容

    {"label":"this is label","level":"info","message":"logger info","timestamp":"2023-10-01T12:10:25.416Z"}
    {"label":"this is label","level":"info","message":"logger log info","timestamp":"2023-10-01T12:10:25.417Z"}
  • 日志格式化输出

    const { createLogger, format, transports } = require('winston')
    const { combine, timestamp, label, printf } = format
    
    const logger = createLogger({
      format: combine(
        label({ label: 'right meow!' }),
        timestamp(),
        printf(({ level, message, label, timestamp }) => {
          return `${timestamp} [${label}] ${level}: ${message}`
        })
      ),
      transports: [new transports.Console()],
    })
    
    logger.info('This is formatted message')

    控制台输出:

    2023-10-01T12:27:40.887Z [right meow!] info: This is formatted message
    
  • 自动记录未捕获的错误

    createLoggler({
      exceptionHandlers: [new transports.File({ filename: "exceptions.log" })],
      rejectionHandlers: [new transports.File({ filename: "rejections.log" })],
    })

    exceptions.log 将记录 uncaughtException 事件捕获的错误,rejections.log 将记录 unhandledRejection 事件捕获的错误,
    它们都以 createLoggler 配置的格式输出。

log4js

log4js 是一个开箱即用的日志记录器。

  • 开箱即用

    const log4js = require('log4js')
    const logger = log4js.getLogger()
    logger.level = 'debug' // 指定 logger 可记录的最高日志级别,默认为最低 off 不记录。
    logger.info('logger info')

    控制台输出:

    [2023-10-01T21:34:45.864] [INFO] default - logger info
    
  • 将日志输出到控制台和文件

    const log4js = require('log4js')
    log4js.configure({
      appenders: {
        stdout: { type: 'stdout' },
        file: { type: 'file', filename: 'test.log' },
      },
      categories: {
        default: {
          appenders: ['stdout', 'file'],
          level: 'trace',
        },
      },
    })
    const logger = log4js.getLogger()
    logger.info('logger info')

    控制台和文件有相同的输出

    [2023-10-01T21:35:38.844] [INFO] default - logger info
    
  • 日志格式化输出
    将日志通过自定义布局,格式化为 json 输出

    const log4js = require('log4js')
    
    log4js.addLayout('json', function (config) {
      return function (logEvent) {
        return JSON.stringify({
          timestamp: logEvent.startTime,
          level: logEvent.level.levelStr,
          message: logEvent.data.join(', '),
        })
      }
    })
    
    log4js.configure({
      appenders: {
        stdout: { type: 'stdout', layout: { type: 'json' } },
      },
      categories: {
        default: {
          appenders: ['stdout'],
          level: 'trace',
        },
      },
    })
    const logger = log4js.getLogger()
    logger.info('logger info')

    控制台输出:

    {"timestamp":"2023-10-01T13:46:57.804Z","level":"INFO","message":"logger info"}

log4js 相比 winston 有着更适合 JS 开发者的 API,相对轻量(Unpacked Size:160 kB < 268 kB),推荐使用。

参考资料