nodejs 日志库 log4js

log4js 是一个功能强大且灵活的日志记录库,专为 nodejs 应用程序设计。它提供了丰富的日志记录功能和配置选项。log4js 支持多种日志输出方式(如控制台、文件、数据库等),并且可以根据日志级别进行过滤和格式化。

log4js 的官网链接: https://log4js-node.github.io/log4js-node/index.html

1. 主要特性

  1. 多种日志输出方式
    • 控制台输出:将日志输出到控制台。
    • 文件输出:将日志写入文件,支持文件轮转。
    • 数据库输出:将日志存储到数据库中。
    • HTTP 输出:将日志发送到远程服务器。
  2. 日志级别(日志级别从低到高依次为)
    • ALL:记录所有日志。
    • TRACE:记录细粒度的信息事件,比 DEBUG 级别更详细。
    • DEBUG:记录细粒度的信息事件,用于调试应用程序。
    • INFO:记录一般信息事件,突出应用程序的进展。
    • WARN:记录潜在的有害情况。
    • ERROR:记录错误事件,但应用程序仍可继续运行。
    • FATAL:记录非常严重的错误事件,通常会导致应用程序中止。
    • MARK:标记级别,通常不用于日志记录,但可以用于标记日志中的特定点。
    • OFF:关闭日志记录
  3. 日志格式化
    • 支持自定义日志格式,可以根据需要格式化日志输出。
    • 提供多种内置格式化选项,如 basiccolouredjson 等。
  4. 配置灵活
    • 支持通过代码或配置文件进行配置。
    • 可以动态更新配置,无需重启应用程序。

2. 安装方法

要在你的 Node.js 项目中使用 log4js,可以通过 npm 进行安装:

npm install log4js

3. 基本用法

以下是一个简单的示例,展示了如何在 Node.js 应用程序中使用 log4js

const log4js = require('log4js');

// 配置 log4js
log4js.configure({
  appenders: {
    console: { type: 'console' },
    file: { type: 'file', filename: 'logs/app.log' }
  },
  categories: {
    default: { appenders: ['console', 'file'], level: 'debug' }
  }
});

const logger = log4js.getLogger();

// 记录不同级别的日志
logger.trace('这是一个 TRACE 级别的日志'); // 这个日志不会被记录,因为 trace 比 debug 级别低
logger.debug('这是一个 DEBUG 级别的日志');
logger.info('这是一个 INFO 级别的日志');
logger.warn('这是一个 WARN 级别的日志');
logger.error('这是一个 ERROR 级别的日志');
logger.fatal('这是一个 FATAL 级别的日志');

4. log4js 中的术语

  • 级别(level)
  • 类别 (category)
  • 附加器(appender)
  • 日志记录器(logger)
  • 日志事件(LogEvent)

级别(level) – 日志级别是日志事件(调试、信息等)的严重性或优先级。附加器是否会看到事件取决于类别的级别。如果该级别小于或等于事件的级别,则事件将被发送到类别的附加器。

类别 (category) – 用于对日志事件进行分组的标签。这可以基于模块(例如“auth”、“payment”、“http”)或任何您喜欢的模块。具有相同类别的日志事件将发送到相同的附加器。Log4js 支持类别的层次结构,使用点来分隔层 – 例如,如果“myapp.submodule”未定义任何附加器,则“myapp.submodule”类别中的日志事件将使用“myapp”的级别,并且还将使用为“myapp”定义的任何附加器。 (可以通过在子类别上设置 inherit=false 来禁用此行为。)当您从 log4js (log4js.getLogger(‘somecategory’)) 获取 Logger 时,将定义日志事件的类别。

附加器(appender) – 附加器负责输出日志事件。它们可能会将事件写入文件、发送电子邮件、将其存储在数据库中或执行任何其他操作。大多数附加器使用布局将事件序列化为字符串以供输出。

日志记录器(logger) – 这是您的代码与 log4js 的主要接口。记录器实例可能有一个可选类别,在您创建实例时定义。记录器提供创建 LogEvents 并将其传递给附加器的信息、调试、错误等函数。

布局(layout) – 用于将 LogEvent 转换为字符串表示的函数。Log4js 提供了几种不同的实现:基本、彩色和更可配置的基于模式的布局。

日志事件(LogEvent) – 日志事件具有时间戳、级别和可选类别、数据和上下文属性。当您调用 logger.info(‘cheese value:’, edam) 时,记录器将创建一个日志事件,其中包含现在的时间戳、INFO 级别、创建记录器时选择的类别以及具有两个值(字符串“cheese value:”和对象“edam”)的数据数组,以及添加到记录器的任何上下文数据。

5. 自定义 layout

const log4js = require("log4js");

// json 形式输出
log4js.addLayout("json", function (config /* type: json 对应的layout的配置 */) {
  return function (logEvent) {
    return JSON.stringify(logEvent) + config.separator;
  };
});

log4js.configure({
  appenders: {
    out: { type: "stdout", layout: { type: "json", separator: "," } },
  },
  categories: {
    default: { appenders: ["out"], level: "info" },
  },
});

const logger = log4js.getLogger("json-test");
logger.info("this is just a test");
logger.error("of a custom appender");
logger.warn("that outputs json");

6. 日志优先级的注意点

比如使用了 ERROR 级别,那么比 ERROR 级别低的级别不会被记录,INFO, WARN 这些方法输出时,不会打到日志库中

7. log4js 适配 nestjs 的 common logger

如果要使用 log4js 作为 nestjs 的日志记录器, logger 不但可以输出到控制台,还可以记录日志到文件中,或者网络传输日志 等

一般在初始化 app.module.ts 后,使用自定义的 Logger 替换 @nestjs/common 的 Logger

import { Injectable, LoggerService } from '@nestjs/common';
import { Logger } from 'log4js';

@Injectable()
export class Log4jsLogger implements LoggerService {
  constructor(private readonly logger: Logger) {}

  updateContext(context?: string) {
    if (context && context.length > 0) {
      this.logger.addContext('name', context);
    } else {
      this.logger.addContext('name', '');
    }
  }

  verbose(message: any, context?: string) {
    this.updateContext(context);
    this.logger.trace(message);
  }

  debug(message: any, context?: string) {
    this.updateContext(context);
    this.logger.debug(message);
  }

  log(message: any, context?: string) {
    this.updateContext(context);
    this.logger.info(message);
  }

  warn(message: any, context?: string) {
    this.updateContext(context);
    this.logger.warn(message);
  }

  error(message: any, trace?: string, context?: string) {
    this.updateContext(context);
    this.logger.error(message, trace);
  }

  static getTimestamp() {
    const localeStringOptions = {
      year: 'numeric',
      hour: 'numeric',
      minute: 'numeric',
      second: 'numeric',
      day: '2-digit',
      month: '2-digit',
    } as const;
    return new Date(Date.now()).toLocaleString(undefined, localeStringOptions);
  }

  getTimestamp() {
    return Log4jsLogger.getTimestamp();
  }
}