[TOC]
log4js使用详解
技术选型
在node服务端的日志收集工具中,比较火热的有winston、log4js、bunyan、npmlog等,对于日志收集的基本要求例如分布式集中收集、多渠道输出、日志格式化等上述npm库均可满足。现在分析下上述工具库的特点:
- winston:非常全面的日志管理库,但比较重一些
- log4js:定制灵活、比较全面的日志管理库,允许api配置也允许json方式配置,相对轻便、灵活
- bunyan:日志记录方式为JSON,相对来说形式比较单一
- npmlog:基础的日志记录器,不具备更多的高级功能、定制化功能。
对于本项目来说,并不需要复杂的winston来支持,log4js在兼顾功能的情况下也可以兼顾性能,因此使用log4js
但是对于express框架的后端,我们可以考虑使用express默认中间组件morgan,但是其功能比较单一、需要建立文件流、仅限于基础使用
log4js基本使用方法
1. 安装
使用npm下载即可
npm install log4js
2. 基本功能
在log4js中使用日志记录器,主要的功能如下:
日志级别:
日志分级可以将不同级别的日志在控制台中采用不同的颜色,也可以帮助在生产时有选择地生成不同级别的日志。
这一点理解上可以对应console api的调用,我们可以console.log、console.info、console.err,这是类似的。log4js默认的日志分级有9级,并且不像winston可以自定义配置日志级别,这是不可修改的:
javascript
Level.addLevels({
ALL: { value: Number.MIN_VALUE, colour: 'grey' },
TRACE: { value: 5000, colour: 'blue' },
DEBUG: { value: 10000, colour: 'cyan' },
INFO: { value: 20000, colour: 'green' },
WARN: { value: 30000, colour: 'yellow' },
ERROR: { value: 40000, colour: 'red' },
FATAL: { value: 50000, colour: 'magenta' },
MARK: { value: 9007199254740992, colour: 'grey' }, // 2^53
OFF: { value: Number.MAX_VALUE, colour: 'grey' }
});
其优先级依次递增,有个更清晰的图示:
ALL OFF 这两个等级并不会直接在业务代码中使用。剩下的七个即分别对应 Logger 实例的七个方法,.trace .debug .info ...。也就是说,你在调用这些方法的时候,就相当于为这些日志定了级。
日志类型:
1. 日志分类过滤
对于创建的同一个logger记录器实例,我们可能会遇到需要在记录日志的时候区分info 、 debug 、error等不同级别的日志,并将其放在不同的file文件中,例如分别放在xxx-info.log、xxx-debug.log、xxx-error.log三种文件中
这个时候我们需要用到类型为logLevelFilter的过滤器,该过滤器可以对不同级别的日志、指定不同的追加器
同时我们在category类别中,定义以不同的key,使用不同的filter / appender组,让日志根据对应级别匹配其类别下可输出的日志Appender。注意:category配置时,default是必须要有的,代表其他的category没有匹配到时,默认进入default; category通过getLogger函数传参设置,不同类别下的logger,使用不同类别key对应的filter / appender
以下是实现上述需求的一个封装类:
javascript
let log4js = require('log4js');
let path = require('path')
let _fs = require('fs')
class Logger {
constructor() {
this._logger = null;
this.logPathName = 'logs'
this.config = {
type: 'dateFile',
filename: path.resolve(this.logPathName + '/info.log'),
encoding: 'utf-8',
layout: {
type: 'pattern',
pattern: '%d %p %h %m'
},
//split by day
pattern: 'yyyy-MM-dd',
// ends as '.log'
keepFileExt: true,
//insert the date at the tail of name
alwaysIncludePattern: true,
}
}
init() {
//define and configure logger
log4js.configure({
appenders: {
default: this.config,
//debug appender
debugAppender: {
...this.config,
filename: path.resolve(this.logPathName + '/debug.log'),
},
//debug filter
debugFilter: {
type: 'logLevelFilter',
appender: 'debugAppender', // assign a specific appender
level: 'debug', // lowest log level to catch
maxLevel: 'debug', //highest log level to catch
},
//info filter
infoFilter: {
type: 'logLevelFilter',
appender: 'default',
level: 'info',
maxLevel: 'info',
},
//warn filter
warnFilter: {
type: 'logLevelFilter',
appender: 'default',
level: 'warn',
maxLevel: 'warn',
},
errorAppender: {
...this.config,
filename: path.resolve(this.logPathName + '/error.log'),
},
//error filter
errorFilter: {
type: 'logLevelFilter',
appender: 'errorAppender',
level: 'error',
maxLevel: 'fatal',
},
},
categories: {
default: { //default is a must
appenders: ['infoFilter', 'warnFilter', 'errorFilter','debugFilter'],
level: 'debuug',
}
},
})
//get logger,无category传参,进入default配置中
this._logger = log4js.getLogger();
this._logger.level = "debug"
}
}
2. 定义日志Logger的不同类型来源
当获取Logger实例时,唯一可以传的参数就是日志类型loggerCategory,这个是非常灵活的、自定义字符串的参数,用于从另一个维度将日志分类。 比如:
javascript
// file: set-catetory.js
var log4js = require('log4js');
var logger = log4js.getLogger('set-catetory.js');
logger.debug("Time:", new Date());
打印的日志信息如下:
css
[2016-08-21 01:24:07.332] [DEBUG] set-catetory.js - Time: 2016-08-20T17:24:07.331Z
可以从这条日志信息看出这条日志来自于 set-catetory.js 文件。又或者针对不同的 node package 使用不同的 category,这样可以区分日志来源于哪个模块。
日志落盘:
日志落盘通俗来说就是日志在哪里生成,也可以理解为日志输出的渠道。在log4js中日志的出口通过Appender来定义。 log4js提供的Appender有
- console:输出到控制台
- file:输出到指定目录文件中
- dataFile:将日志输出到文件,可以按照特定日期滚动生成,例如今天输出到 default-2023-08-21.log,明天输出到 default-2023-08-22.log
- multiFile:动态配置不同category下或者不同变量控制下,落盘到不同文件
- smtp:将日志输出到邮件
默认Appender 下面是 log4js 内部默认的 appender 设置:
javascript
// log4js.js
defaultConfig = {
appenders: [{
type: "console"
}]
}
设置自己的Appender 我们可以通过log4js.configure来设置我们想要的 appender。
javascript
// file: custom-appender.js
var log4js = require('log4js');
log4js.configure({
appenders: [{
type: 'file',
filename: 'default.log'
}]
})
var logger = log4js.getLogger('custom-appender');
logger.debug("Time:", new Date());
在上例中,我们将日志输出到了文件中,运行代码,log4js 在当前目录创建了一个名为default.log 文件,[2016-08-21 08:43:21.272] [DEBUG] custom-appender - Time: 2016-08-21T00:43:21.272Z 输出到了该文件中。
上述Appenders也可以见出,Appenders是一个数组,可以接受多个type的输出渠道~
值得一提的multiFile
1、根据category来动态落盘
javascript
log4js.configure({
appenders: {
multi: {
type: "multiFile",
base: "logs/",
property: "categoryName",
extension: ".log",
},
},
categories: {
default: { appenders: ["multi"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug("I will be logged in logs/default.log");
const otherLogger = log4js.getLogger("cheese");
otherLogger.info("Cheese is cheddar - this will be logged in logs/cheese.log");
2、根据变量来动态落盘(使用addContext设置变量)【下述例子展现了如何例如addContext来使日志进行滚动,下例中根据userId滚动,也可以同理替换为new Date使其根据日期滚动】
javascript
log4js.configure({
appenders: {
everything: {
type: "multiFile",
base: "logs/",
property: "userID",
extension: ".log",
maxLogSize: 10485760,
backups: 3,
compress: true,
},
},
categories: {
default: { appenders: ["everything"], level: "debug" },
},
});
const userLogger = log4js.getLogger("user");
userLogger.addContext("userID", user.getID());
userLogger.info("this user just logged in");
日志过滤:
我们可以调整 appender 的配置,对日志的级别和类别进行过滤:
javascript
// file: level-and-category.js
var log4js = require('log4js');
log4js.configure({
appenders: [{
type: 'logLevelFilter',
level: 'DEBUG',
category: 'category1',
appender: {
type: 'file',
filename: 'default.log'
}
}]
})
var logger1 = log4js.getLogger('category1');
var logger2 = log4js.getLogger('category2');
logger1.debug("Time:", new Date());
logger1.trace("Time:", new Date());
logger2.debug("Time:", new Date());
运行,在 default.log 中增加了一条日志:
[2016-08-21 10:08:21.630] [DEBUG] category1 - Time: 2016-08-21T02:08:21.629Z
为什么会出现如上的结果呢?主要是是从日志级别和category分类对应日志:
-
使用 logLevelFilter 和 level 来对日志的级别进行过滤,所有权重大于或者等于DEBUG的日志将会输出。这也是之前提到的日志级别权重的意义;
-
通过 category 来选择要输出日志的类别,category2 下面的日志被过滤掉了,该配置也接受一个数组,例如 ['category1', 'category2'],这样配置两个类别的日志都将输出到文件中。
日志格式(Layout):
日志格式layout在每个Appender中的layout属性中配置,有几个默认的显示类型:
- messagePassThrough:仅仅输出日志的内容;
- basic:在日志的内容前面会加上时间、日志的级别和类别,通常日志的默认 layout;
- colored/coloured:在 basic 的基础上给日志加上颜色,appender Console 默认使用的就是这个 layout;
- pattern:这是一种特殊类型,可以通过它来定义任何你想要的格式。 一个 pattern 的例子:
javascript
// file: layout-pattern.js
var log4js = require('log4js');
log4js.configure({
appenders: [{
type: 'console',
layout: {
type: 'pattern',
pattern: '[%r] [%[%5.5p%]] - %m%n'
}
}]
})
var logger = log4js.getLogger('layout-pattern');
logger.debug("Time:", new Date());
对于pattern中指定的格式标识符,可以参考官网给出的表格: log4js的官方文档和源码地址在下方,有需要可以自己查阅:
官方文档:log4js-node.github.io/log4js-node... 源码:github.com/log4js-node...