# Log4js
# node 日志工具
log4js
是一种 node 日志管理工具,可以将自定义格式的日志输出到各种渠道。
对于控制台的日志输出可以呈现彩色日志,对于文件方式的日志输出,可以根据文件大小或者日期进行日志切割。
# 基础用法
const log4js = require("log4js"); // 引入依赖
const logger = log4js.getLogger(); // 获取 logger 实例
logger.level = "info"; // 定义日志级别,默认是 OFF - 关闭所有的日志
logger.info("I am a info msg"); // 调用对应日志级别方法输出日志
# 默认值说明
- 通过打印
logger
可以查看默认配置如下:
Logger {
category: 'default', // 注意这里
context: {},
callStackSkipIndex: 0,
parseCallStack: [Function: defaultParseCallStack]
}
- 查看
logger.level
配置如下:
Level {
level: 1.7976931348623157e+308,
levelStr: 'OFF', // 默认是 OFF 关闭日志,所以如果不设置 level 的话,是不会打印日志的
colour: 'grey'
}
# 基础用法日志信息如下
# 几个概念
# Level
日志级别 划分日志不同等级的类型,侧重于一条日志的重要性。
例如 console 打印日志,有普通的 console.log
, 也有错误类型的 console.error
,也有警告类型 console.warn
。
日志分级能更好的管理日志的输出形式,比如 error 类型的就是 红色展示。
另外,日志的等级设置可以快速的过滤掉一些在生产环境不需要记录的日志信息。比如在开发时,使用 logger.debug
打印 debug 级别的日志,上线后使用 info
级别的日志后。debug 的就不再输出,因为日志只输出和当前设置级别相同或更高的日志。
默认级别数,重置 level 时可以作为参考。如果担心有更新,可以通过 log4js.levels
查看。
{
ALL: new Level(Number.MIN_VALUE, "ALL"),
TRACE: new Level(5000, "TRACE"),
DEBUG: new Level(10000, "DEBUG"),
INFO: new Level(20000, "INFO"),
WARN: new Level(30000, "WARN"),
ERROR: new Level(40000, "ERROR"),
FATAL: new Level(50000, "FATAL"),
MARK: new Level(9007199254740992, "MARK"), // 2^53
OFF: new Level(Number.MAX_VALUE, "OFF")
}
# Category
日志分类 和日志级别 level
的作用都是用来区分日志的类型的。category 可以理解成更大维度的区分信息。
# 应用
const log4js = require("log4js");
// getLogger 函数接受唯一一个参数就是 category 分类名称
const logger = log4js.getLogger("res-model");
logger.level = "info"; // 默认是 OFF - 关闭所有的日志
logger.info("I am a res-model category log");
# Appenders
level
和 category
解决了日志级别和分类的问题,日志最基础的模样已经有了。接下来就是日志输出到哪里的问题了。
// appender 的默认配置是将所有日志事件写入到标准输出流(standard output stream)中
defaultConfigs = {
appenders: [{ type: "stdout" }],
};
appenders 中的常见 type 有哪些?
- conosle、stdout、stderr 这三个都是输出到控制台的,个人理解是开发时使用的模式
- file 输出到指定的文件中,type 指定为 file 时还有更多配置,见下文
- fileSync 同步文件 appender,和标准文件 appender 相比,区别在于所以写入都是同步的。而且不像异步版本,不支持对备份文件进行压缩。
- dateFile 按日期滚动的文件。它的行为类似于一开始会创建一个你定义的 filename 文件名,比如是 test.log。过了一天之后,备份出一个 test.log.xxxx-xx-xx 形式的文件,然后重新创建出一个新的 test.log 文件。
appenders
一直都是 log4js 的配置中设置的,示例后面一起看。
# 重点 API
# configure
应用方式:log4js.configure(string || object)
参数说明
string 如果参数为 string 类型,则被当做为配置文件路径,则该配置文件必须为 JSON 文件。
object 如果参数一个配置对象,则至少要定义一个 appender 和默认的 category。
configure 如果要设置一个 object,具体参数如下:
1. levels(object)
- 非必填
- 该属性用来自定义日志的 level,或者重新定义已存在的 level。
json
形式,key 作为 level 名称;value 是 object,有两个属性:value(integer)
用来确认顺序 和colour
用来设置颜色- 该参数可以配置额外的 level。但是如果配置的名称恰好与默认 level 名称一致,则直接覆盖掉默认的 value 值。
- level 名称必须以字母开头,只能包含字母、数字、下划线
2. appenders(object)
object.key
是自定义的 appender 名称,对应的 value 值是一个对象- value 对象中必须包含
type
属性,它就是指明用那种方式输出日志,上面有记录常用的 console、file、dateFile... - 搭配着对应的 type 属性,还有更多细节的属性,比如
type: file
时,后面需要设置 filename,即文件输出的路径或着文件名。
log4js.configure({
appenders: {
stdout: { type: "stdout" },
stderr: { type: "stderr" },
console: { type: "console" },
file: { type: "file", filename: "test-file.log" },
},
});
3. categories(object)
- 配置好了
appenders
,确认了日志往哪里输出后,就可以确认哪种分类的日志,使用哪种输出方式。所以,需要在 categories 中配置 appenders 的 key。 - object.key 时自定义的 category 名称,对应的 value 值是一个对象。
- value 对象参数 1:
appenders
, 它是一个数组,里面是 appenders 中配置的 key 名。 - value 对象参数 2:
level
, 指定发送到 appender 的最小 level。例如,如果设置为'error',则 appender 只能接收到‘error’, ‘fatal’, ‘mark’级别的消息,‘info’, ‘warn’, ‘debug’, or ‘trace’ 会被忽略掉。 - 重点的是:必须要有一个 key 是 default 的配置,在所有日志事件没有匹配到指定的 category 时使用该默认值
const log4js = require("log4js");
log4js.configure({
appenders: {
stdout: { type: "stdout" },
stderr: { type: "stderr" },
console: { type: "console" },
file: { type: "file", filename: "logs/test-file.log" }, // 将在同级目录下创建 logs 文件夹同时创建 test-file.log 文件
},
categories: {
default: { appenders: ["stdout"], level: "debug" },
test: { appenders: ["stdout"], level: "debug" },
file: { appenders: ["file"], level: "info" },
},
});
const logger = log4js.getLogger("test");
logger.level = "info"; // 这里可以重新调整 level 级别
logger.info("日志输出");
疑惑点
这里发现的一个让我自己很困惑的点,如上面的代码配置后。
虽然在使用 getLogger 方法获取 logger 实例时我已经设置了参数是 test
。
当然此时的本意是我只使用 test 的类别,别的类别可能处于被我放弃的情况。
但是当执行程序时,会同时为 categories 下的 file 创建一个在 appenders 中设置的文件名称 logs/test-file.log
但是如果 categories 下没有 file 这个配置,就不会发生这种问题。
所以,个人理解不要在 categories 中设置多余的配置,最好是精准配置。避免多处额外不必要的文件影响项目架构。
4. pm2(boolean)
- 非必填
- 当使用 pm2 时候,需要设置该值为 true,否则无法正常运行日志
- 您还需要将 pm2-intercom 安装为 pm2 模块:pm2 install pm2-intercom
5. pm2InstanceVar(string)
- 非必填
- 默认值是 NODE_APP_INSTANCE
- 当使用 pm2 且更改了 NODE_APP_INSTANCE 默认值时,需要对应设置该属性。
6. disableClustering(boolean)
- 非必填
- 如果您喜欢 log4js 过去忽略集群环境的方式,或者您在 PM2 日志记录方面遇到问题,请将此设置为 true。 每个工作进程都将执行自己的日志记录。 如果您正在记录文件,请小心这一点,可能会发生奇怪的事情。
# Loggers
获取方式:log4js.getLogger([category])
返回一个 Logger 对象,该对象使用指定的 category,如果未指定 category 参数,则使用默认的 category。
Logger 对象实现了以下方法:
1. .level(args…)
可以使任何小写的 level 名称(包含自定义的 level)。例如:logger.info('some info')会发送一个 info 级别的 log event。
2. is[level]Enabled()
- 返回布尔值,检测是否会发送指定的 level 的日志事件。
- 例如,当前 category 指定的 level 为 INFO 或者更低,则
logger.isInfoEnabled()
返回 true。
3. addContext(key, value)
- key 为 string 类型,value 可以为任何类型。
- 该键值对会被添加到所有日志事件中,例如可以添加相关 id,用来追踪应用中一个用户的所有日志。
- 目前只在 logFaceappender 中使用了 context 值。
4. removeContext(key)
- 删除指定的 context。
5. clearContext()
- 清除所有 context 键值对。
6. level 当前 level 值,可以通过更改该值从而重写原本的 level 值。
# Shutdown
获取方式:log4js.shutdown(cb)
当 log4js 关闭所有 appender 以及所有日志事件都完成写入时,会调用传入的回调函数。 在程序退出时使用该方法,可以保证所有文件写入,socket 都已关闭,等等。
# Custom Layouts
获取方式:log4js.addLayout(type, fn)
添加自定义的 layout 函数。详细查看:layouts (opens new window)
# Appender 配置示例
# File Appender
将日志事件写入到文件。当使用文件 appender 时,需要在应用终止时,调用 log4js.shutdown 以保证完成剩余的异步操作。
文件 appender 使用的是 streamroller 库。
配置参数
- type - "file"
- filename - string - 文件路径。
- maxLogSize - integer (optional) - 限制文件的大小,单位为 byte。如果超出该值则会备份原文件创建新文件。
- backups - integer (optional, default value = 5) - 备份文件的最大数量。
- layout - (optional, defaults to basic layout) - see layouts
剩余其他额外的参数,会被传递给底层的 streamroller (opens new window) (参数含义也可查看 node.js core file streams):
- encoding - string (default “utf-8”)
- mode- integer (default 0644)
- flags - string (default ‘a’)
- compress - boolean (default false) - 在滚动期间压缩备份文件(备份文件将具有 .gz 扩展名)
- keepFileExt - boolean (default false) - 更新日志文件时保留文件扩展名(file.log 变为 file.1.log 而不是 file.log.1)
log4js.configure({
appenders: {
everything: { type: "file", filename: "all-the-logs.log" },
},
categories: {
default: { appenders: ["everything"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug("I will be logged in all-the-logs.log");
配置上文件滚动和压缩以及备份文件个数时:
log4js.configure({
appenders: {
everything: {
type: "file",
filename: "all-the-logs.log",
maxLogSize: 10485760,
backups: 3,
compress: true,
},
},
categories: {
default: { appenders: ["everything"], level: "debug" },
},
});
上述代码会产生一个当前的日志文件(all-the-logs.log)。当该文件达到 10Mb 时,会压缩并命名为:all-the-logs.log.1.gz ,并创建一个新文件命名为 all-the-logs.log 。
当 all-the-logs.log 再次达到 10Mb 时,原 all-the-logs.log.1.gz 会被改名为 all-the-logs.log.2.gz,以此类推。
# dateFile Appender
配置参数
- filename string 文件路径及文件名,./为项目根目录
- pattern? string 滚动日志的时间类型,默认为 .yyyy-MM-dd
- encoding? string 编码格式,默认为 utf-8
- compress? boolean 是否压缩滚动日志文件
- daysToKeep? number 如果此值大于零,则日志滚动期间将删除早于该天数的文件,默认为 0
- alwaysIncludePattern? boolean 在当前日志文件名中和滚动日志一样包括 pattern
- layout? Layout 输出的样式
pattern
pattern 是保存滚动日志的时间分割,规则如下:
- yyyy : 年
- MM : 月
- dd : 日
- hh : 时
- mm : 分
- ss : 秒
- SSS : 毫秒
// 配置成这样,会每秒都保存滚动日志文件,若当前秒没有日志,则不保存。
// 文件名格式基本统一为:文件名.年-月-日 时:分:秒
pattern: ".yyyy-MM-dd hh:mm:ss";
const log4js = require("log4js");
log4js.configure({
appenders: {
dateFileOut: {
type: "dateFile",
filename: "datefile.log",
pattern: ".yyyy-MM-dd hh:mm:ss",
maxLogSize: 200,
},
},
categories: {
default: { appenders: [], level: "debug" },
dataFileLog: { appenders: ["dateFileOut"], level: "debug" },
},
});
var logger = log4js.getLogger("dataFileLog");
logger.debug("日志输出");