likes
comments
collection
share

log4js 在「昆仑虚」中的实践

作者站长头像
站长
· 阅读数 20

引言

log4jsNode.js 日志处理中比较常用的一个库, 之前 个人项目(昆仑虚) 是使用 winston 来处理日志的, 经过对比最后决定改用 log4js 所以才有了这篇文章, 下面将会对 log4js 先进行简单讲解, 然后再一步步演示我是如何将 log4js 在昆仑虚中进行实践的

有关昆仑虚的介绍可查阅 《仿 Mac 个人网站开发 |项目复盘》 这篇文章

一、安装依赖

首先我们先安装下依赖 log4js

npm install log4js

二、日志分级

在日常日志管理中, 日志的分级是很有必要的, 那么 log4js 中默认的 日志分级 又是怎么样的呢? 如下代码我们可以通过 log4js.levels 获取到 log4js 的所有 日志分级 信息:

import log4js from 'log4js';
console.log(log4js.levels);

代码执行结果:

log4js 在「昆仑虚」中的实践

从上图可以看到, 通过 log4js.levels 可以获取到 log4jsLevel 实例, 从打印内容可知 log4js 的日志分为 个等级, 各个级别的名字和权重如下:

下列数组顺序是按照权重 从小到大 进行排序的, 其中 level 表示权重值、levelStr 是等级名称、colour 是该等级的日志在终端的颜色

levels: [
  Level { level: 5e-324, levelStr: 'ALL', colour: 'grey' },
  Level {level: 1.7976931348623157e+308,levelStr: 'OFF', colour: 'grey' },
  Level { level: 5000, levelStr: 'TRACE', colour: 'blue' },
  Level { level: 10000, levelStr: 'DEBUG', colour: 'cyan' },
  Level { level: 20000, levelStr: 'INFO', colour: 'green' },
  Level { level: 30000, levelStr: 'WARN', colour: 'yellow' },
  Level { level: 40000, levelStr: 'ERROR', colour: 'red' },
  Level { level: 50000, levelStr: 'FATAL', colour: 'magenta' },
  Level { level: 9007199254740992, levelStr: 'MARK', colour: 'grey' ,
]

log4js日志分级 基本能够适用于所有情况了, 当然如果有需要, 我们也可以通过 Level 实例对象的 addLevels() 方法来新增分级

import log4js from 'log4js';

// 新增分级 custom
log4js.levels.addLevels({
  custom: {
    value: 30001, // 权重值
    colour: 'yellow', // 日志颜色
  },
});

console.log(log4js.levels);

三、初识 appenders 和 category

首先我们先看一个简单的 DEMO:

import log4js from 'log4js';

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: { type: 'stdout' },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'warn',
      appenders: ['console'],
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug');
logger.warn('这一个 warn');
logger.error('这一个 error');

在终端中使用 node 执行上面代码:

log4js 在「昆仑虚」中的实践

下面我们对上面代码进行简单说明:

  1. appenders: 输出源, 用于定义日志是如何输出的, 可以是终端打印、写入文件、发送电子邮件、网络发送数据等等; 所有 appender 都有一个 type 决定使用哪个 appender; log4js 内置了很多常用的 appender, 具体可以查看 官网, 上面代码中我们定义了一个输出源 console, 其中 type 等于 stdout, 表示使用内置的 appender stdoutappender 会在终端打印出日志
  2. categories: 类别, 用于对日志进行分组管理, 其中 appenders 配置项, 表示该类型的日志是如何输出的, level 则限制该类型的日志等级, 例子中 level 等于 warn 表示只处理等级权限 大于或等于 warn 的日志, 所以执行上面的例子会发现只输出了 warnerror 的日志, 需要注意的是 categories 必须要定一个 default 配置, 更多细节查看 官网
  3. log4js.getLogger() 获取指定类型的 Logger 实例, 参数为 类型名, 不传则取 default 配置

四、 初识 Layout

log4js 允许我们通过 layout 定义每一条输出日志的格式 log4js 内置了以下几种 类型、并且还支持自定义类型

4.1 内置类型: basic

该类型输出日志时格式为: 时间戳 + 级别 + 类别 + 日志内容

如下代码所示, 定义了 layout 类型为 basic:

import log4js from 'log4js';

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
      layout: { type: 'coloured' }, // 定义 layout: 输出格式
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['console'],
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug');
logger.warn('这一个 warn');
logger.error('这一个 error');

代码执行结果:

log4js 在「昆仑虚」中的实践

4.2 内置类型: coloured (or colored)

该类型输出日志时格式同 basic, 只是时间戳、级别、类别会将根据日志事件的级别进行 着色(也是目前终端默认的输出格式)

如下代码所示, 定义了 layout 类型为 coloured:

import log4js from 'log4js';

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
      layout: { type: 'coloured' }, // 定义 layout: 输出格式
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['console'],
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug');
logger.warn('这一个 warn');
logger.error('这一个 error');

代码执行结果:

log4js 在「昆仑虚」中的实践

4.3 内置类型: messagePassThrough

该类型只会输出日志内容, 不输出时间戳、级别或类别

如下代码所示, 定义了 layout 类型为 messagePassThrough:

import log4js from 'log4js';

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
      layout: { type: 'messagePassThrough' }, // 定义 layout: 输出格式
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['console'],
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

代码执行结果:

log4js 在「昆仑虚」中的实践

补充: 下面是 Logger 实例的部分 TS 类型定义, 从定义中可以看出 debug warn error 支持多个参数、并且参数类型不限制

export interface Logger {
  trace(message: any, ...args: any[]): void;

  debug(message: any, ...args: any[]): void;

  info(message: any, ...args: any[]): void;

  warn(message: any, ...args: any[]): void;

  error(message: any, ...args: any[]): void;

  fatal(message: any, ...args: any[]): void;

  mark(message: any, ...args: any[]): void;
}

4.4 内置类型: dummy

上文提到 Logger 实例中 debug warn error 等方法是支持多个参数的, 也就是如果我们传了多个参数这些参数都会被作为日志依次输出, 但是如果我们将 layout 设置为 dummy 那么将只输出第一个参数

如下代码所示, 定义了 layout 类型为 dummy:

import log4js from 'log4js';

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
      layout: { type: 'dummy' }, // 定义 layout: 输出格式
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['console'],
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

代码执行结果: debug warn error 传了 2 个参数, 但是只会输出第一个参数内容

log4js 在「昆仑虚」中的实践

4.5 内置类型: pattern

该类型允许我们通过 模式, 自定义日志格式, 如下代码所示:

  1. pattern 定义了日志输出格式
  2. %d 表示时间 %p 表示日志级别 %c 表示日志类别 %m 表示日志数据 %n 表示换行
  3. %x{<tokenname>} 将插入 tokens 中对应数据, <tokenname> 可以是个字符串, 也可以是个函数
import log4js from 'log4js';

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
      layout: { // 定义 layout: 输出格式
        type: 'pattern',
        pattern: '%d %p %c %x{user} %m%n',
        tokens: {
          user (logEvent) {
            return '用户名';
          },
        },
      },
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['console'],
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

代码执行结果:

log4js 在「昆仑虚」中的实践

关于更多 模式 可参考 官网

4.6 自定义类型

如果现有的类型不符合我们的要求, 我们也能够自定义类型, 输出各种花里胡哨的日志信息, 如下代码所示: 通过 addLayout 注册了新的类型 custom, 并在 appenders 中应用

import log4js from 'log4js';

// 添加自定义布局: custom
log4js.addLayout('custom', (config) => (event) => `${JSON.stringify(event)}${config.separator}`);

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
      layout: { // 定义 layout: 输出格式
        type: 'custom',
        separator: ',',
      },
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['console'],
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

代码执行结果:

log4js 在「昆仑虚」中的实践

更多有关 Layout 细节请参考 官网

到此有关 log4js 基本讲解已经结束, 下面我们将进入主题

五、自定义输出格式

log4js 自带的几种终端输出格式, 个人不是很喜欢所以接下来, 我们先针对终端的输出格式进行调整, 如下代码所示:

  1. categories 中配置了 enableCallStack 该配置允许使用 调用堆栈, 开启该配置我们就能够获取到 logger.debug() logger.error() 等方法的 调用栈 信息

  2. layout 中使用 pattern 类型, 自定义了输出格式, 同时配置了 tokens.callStack 拿到 调用栈 信息

import log4js from 'log4js';

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
      layout: {
        type: 'pattern',
        pattern: '%[[%p] %d{yyyy/MM/dd-hh.mm.ss}%] at %x{callStack} %n%n  %m %n%n',
        tokens: {
          callStack: (event) => `${event.fileName} ${event.lineNumber}:${event.columnNumber}`,
        },
      },
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['console'],
      enableCallStack: true,
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

执行上面代码效果如下所示: 终端将输出 [日志级别] + 时间 + 调用栈信息 + 日志内容(换行展示), 日志级别和时间高亮显示

log4js 在「昆仑虚」中的实践

六、按照「日志分级」存储日志文件

上文我们只是将日志在控制台进行了输出, 接下来我们想要将日志存储到本地文件中, 现有需求如下:

  1. 针对所有日志进行存储, 并且需要按照 日志级别, 对不同级别的日志进行单独的存储
  2. 每个日志文件最大 20M, 并且需要保留 3 个备份文件, 对于备份文件需要进行压缩存储
  3. 同时日志格式和 console 保证一致

根据需求进行如下修改: 新增 appenders.multiWithLevel 配置项

import log4js from 'log4js';

+ // 当前目录
+ const CURRENT_DIRECTORY = new URL('.', import.meta.url).pathname;

+ const LAYOUT_CONSOLE = {
+   type: 'pattern',
+   pattern: '%[[%p] %d{yyyy/MM/dd-hh.mm.ss}%] at %x{callStack} %n%n  %m %n%n',
+   tokens: {
+     callStack: (event) => `${event.fileName} ${event.lineNumber}:${event.columnNumber}`,
+   },
+ };

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    console: {
      type: 'stdout',
+     layout: LAYOUT_CONSOLE, // 日志内容格式
    },
+   multiWithLevel: {
+     base: `${CURRENT_DIRECTORY}logs/`,  // 日志文件「存储路径 + 文件名前缀」
+     type: 'multiFile', // 多文件
+     property: 'level', // 日志按照 logEvent.level 拆分
+     extension: '.log', // 日志文件的后缀名
+     backups: 3, // 备份数量
+     compress: true, // 备份文件是否压缩存储
+     maxLogSize: 20 * 1024 * 1024, // 每个日志文件最大 20 M
+     layout: {
+       ...LAYOUT_CONSOLE,
+       pattern: LAYOUT_CONSOLE.pattern.replace(/(%\[|%\])/g, ''), // 去除着色的配置 %[ %]
+     },
+   },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
+     appenders: ['console', 'multiWithLevel'],
      enableCallStack: true,
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

测试, 可将上面 maxLogSize 修改为 2 然后反复执行代码, 将会产生下列日志文件:

log4js 在「昆仑虚」中的实践

七、按照「用户」存储日志文件

上文讲解了如果将日志按照等级进行存储, 接下来我们还想要将日志「按照 用户 ID」进行存储, 现有需求如下:

  1. 针对等级大于等于 warn 的日志, 按照 用户 ID 进行分批存储
  2. 每个日志文件最大 20M, 并且保留 3 个备份文件, 对于备份文件需要进行压缩存储
  3. 日志格式和 console 保证一致

根据需求进行如下修改:

  • 新增 appenders.multiWithUser 配置项, 按照 用户 ID 对日志进行拆分存储
  • 新增 appenders.useMultiWithUser 配置项, 针对日志级别进行过滤, 然后移交给 appenders.multiWithUser 进行处理
  • 调用 addContext 方法设置上下文: 设置用户信息
log4js.configure({
  ...
  appenders: {
    ...
+   multiWithUser: {
+     base: `${CURRENT_DIRECTORY}logs/user`,  // 日志文件「存储路径 + 文件名前缀」
+     type: 'multiFile', // 多文件
+     property: 'userId', // 日志按照 logEvent.context.userId 拆分
+     extension: '.log', // 日志文件的后缀名
+     backups: 3, // 备份数量
+     compress: true, // 备份文件是否压缩存储
+     maxLogSize: 20 * 1024 * 1024, // 每个日志文件最大 20 M
+     layout: {
+       ...LAYOUT_CONSOLE,
+       pattern: LAYOUT_CONSOLE.pattern.replace(/(%\[|%\])/g, ''), // 去除着色的配置 %[ %]
+     },
+   },
+   useMultiWithUser: {
+     level: 'warn', // 允许通过过滤器的最低事件级别
+     type: 'logLevelFilter', // 使用「日志级别过滤器」
+     appender: 'multiWithUser', // 转移给 multiWithUser 处理
+   },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
+     appenders: ['console',  'multiWithLevel', 'useMultiWithUser'],
      enableCallStack: true,
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

+ // 4. 添加上下文: 用户 ID
+ logger.addContext('userId', '21321312421231');

// 5. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

测试, 可将上面 maxLogSize 修改为 2 然后反复执行代码, 将会产生下列日志文件:

log4js 在「昆仑虚」中的实践

八、接入企微机器人: 自定义 appender

接下来我们想要将一些 重要日志 通过企业微信机器人, 在群里进行广播, 便于及早发现问题

8.1 创建机器人

点击企微群右上角 ... 选择添加机器人, 并按照提示完成机器人的创建

log4js 在「昆仑虚」中的实践

拷贝 webhook 地址, 备用

log4js 在「昆仑虚」中的实践

8.2 修改 log4js 配置项

主要是自定义了一个 appender(root) 并在 appenders 中调用, 该 appender 主要做了下了三件事:

  • 日志等级判断(只处理大于等于 warn 的日志)
  • 获取格式化后的日志信息
  • 调用企微机器人推送消息
+ import axios from 'axios';
import log4js from 'log4js';

....

+ const webhook = '企微机器人 webhook';

+ const robot = {
+   configure: (config, layouts) => (event) => {
+     // 1. 如果日志等级小于 warn 则不进行处理
+     if (!event.level.isGreaterThanOrEqualTo('warn')) {
+       return false;
+     }
+ 
+     // 2. 调用 layouts 里 messagePassThroughLayout 方法获取到格式化后的「日志事件数据」
+     const message = layouts.messagePassThroughLayout(event);
+ 
+     // 3. 调用企微机器人 api, 推送消息
+     axios({
+       method: 'POST',
+       url: webhook,
+       data: {
+         msgtype: 'text',
+         text: { content: message },
+       },
+     });
+   },
+ };

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
+   robot: { type: robot },
    ...
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
+     appenders: ['robot', 'console',  'multiWithLevel', 'useMultiWithUser'],
      enableCallStack: true,
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 添加上下文: 用户 ID
logger.addContext('userId', '21321312421231');

// 5. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

测试: 执行修改后的代码, 机器人推送情况如下:

log4js 在「昆仑虚」中的实践

8.3 机器人推送消息优化

上面机器人只是简单广播了下日志消息, 接下来我们优化下推送的内容以及模板, 这里我们可以在企微群里通过点击机器人查看机器人相关文档

log4js 在「昆仑虚」中的实践

文档中, 可以看到我们可选的模式有两种一种是 文本格式、一种是 Markdown 格式, 上面 DEMO 中使用的是 文本格式, 下面我们使用 Markdown 格式来发送消息, 因为 Markdown 可玩性相对高点

log4js 在「昆仑虚」中的实践

调整企微机器人 api 参数:

+ import moment from 'moment';

const robot = {
  configure: (config, layouts) => (event) => {
    ....
    // 3. 调用企微机器人 api, 推送消息
    axios({
      method: 'POST',
      url: webhook,
+     data: {
+       msgtype: 'markdown',
+       markdown: {
+         content: [
+           '## 昆仑虚日志监控',
+           `> 级别: <font color="warning">${event.level}</font>`,
+           `> 时间: <font color="info">${moment(event.startTime).format('YYYY-MM-DD HH:mm:ss')}</font>`,
+           `> 调用栈: <font color="comment">${event.fileName} ${event.lineNumber}:${event.columnNumber}</font>`,
+           '>',
+           '>',
+           `\`${message}\``,
+         ].join('\n'),
+       },
+     },
    });
  },
};
...
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

执行修改后的代码, 机器人推送情况如下:

log4js 在「昆仑虚」中的实践

企微机器人 markdown 格式更多配置参考 官方手册

九、完整演示代码

import axios from 'axios';
import moment from 'moment';
import log4js from 'log4js';

// 当前目录
const CURRENT_DIRECTORY = new URL('.', import.meta.url).pathname;

const LAYOUT_CONSOLE = {
  type: 'pattern',
  pattern: '%[[%p] %d{yyyy/MM/dd-hh.mm.ss}%] at %x{callStack} %n%n  %m %n%n',
  tokens: {
    callStack: (event) => `${event.fileName} ${event.lineNumber}:${event.columnNumber}`,
  },
};

const webhook = '企微机器人 webhook';

const robot = {
  configure: (config, layouts) => (event) => {
    // 1. 如果日志等级小于 warn 则不进行处理
    if (!event.level.isGreaterThanOrEqualTo('warn')) {
      return false;
    }

    // 2. 调用 layouts 里 messagePassThroughLayout 方法获取到格式化后的「日志事件数据」
    const message = layouts.messagePassThroughLayout(event);

    // 3. 调用企微机器人 api, 推送消息
    axios({
      method: 'POST',
      url: webhook,
      data: {
        msgtype: 'markdown',
        markdown: {
          content: [
            '## 昆仑虚日志监控',
            `> 级别: <font color="warning">${event.level}</font>`,
            `> 时间: <font color="info">${moment(event.startTime).format('YYYY-MM-DD HH:mm:ss')}</font>`,
            `> 调用栈: <font color="comment">${event.fileName} ${event.lineNumber}:${event.columnNumber}</font>`,
            '>',
            '>',
            `\`${message}\``,
          ].join('\n'),
        },
      },
    });
  },
};

log4js.configure({
  // 1. 输出源: 用于定义日志是如何输出的, see: https://log4js-node.github.io/log4js-node/appenders.html
  appenders: {
    robot: { type: robot },
    console: {
      type: 'stdout',
      layout: LAYOUT_CONSOLE, // 日志内容格式
    },
    multiWithLevel: {
      base: `${CURRENT_DIRECTORY}logs/`,  // 日志文件「存储路径 + 文件名前缀」
      type: 'multiFile', // 多文件
      property: 'level', // 日志按照 logEvent.level 拆分
      extension: '.log', // 日志文件的后缀名
      backups: 3, // 备份数量
      compress: true, // 备份文件是否压缩存储
      maxLogSize: 20 * 1024 * 1024, // 每个日志文件最大 20 M
      layout: {
        ...LAYOUT_CONSOLE,
        pattern: LAYOUT_CONSOLE.pattern.replace(/(%\[|%\])/g, ''), // 去除着色的配置 %[ %]
      },
    },
    multiWithUser: {
      base: `${CURRENT_DIRECTORY}logs/user`,  // 日志文件「存储路径 + 文件名前缀」
      type: 'multiFile', // 多文件
      property: 'userId', // 日志按照 logEvent.context.userId 拆分
      extension: '.log', // 日志文件的后缀名
      backups: 3, // 备份数量
      compress: true, // 备份文件是否压缩存储
      maxLogSize: 20 * 1024 * 1024, // 每个日志文件最大 20 M
      layout: {
        ...LAYOUT_CONSOLE,
        pattern: LAYOUT_CONSOLE.pattern.replace(/(%\[|%\])/g, ''), // 去除着色的配置 %[ %]
      },
    },
    useMultiWithUser: {
      level: 'warn', // 允许通过过滤器的最低事件级别
      type: 'logLevelFilter', // 使用「日志级别过滤器」
      appender: 'multiWithUser', // 转移给 multiWithUser 处理
    },
  },

  // 2. 类别: 用于定义日志的类别, see: https://log4js-node.github.io/log4js-node/categories.html
  categories: {
    default: {
      level: 'all',
      appenders: ['robot', 'console',  'multiWithLevel', 'useMultiWithUser'],
      enableCallStack: true,
    },
  },
});

// 3. 获取 Logger 实例
const logger = log4js.getLogger();

// 4. 添加上下文: 用户 ID
logger.addContext('userId', '21321312421231');

// 5. 输出日志
logger.debug('这是一个 debug:', '会不会被输出');
logger.warn('这一个 warn:', '会不会被输出');
logger.error('这一个 error:', '会不会被输出');

十、收个尾

log4js个人项目(昆仑虚) 中的实践基本也就到此结束咯, 当然实际运用并没有本文演示的那么简单粗暴, 在实际用途中大概率还是要对代码进行封装、拆分…… 但是要用到的相关知识点在上文中基本都有提到。

本文正在参加「金石计划」

转载自:https://juejin.cn/post/7210756376310284349
评论
请登录