log4js日志管理(分级分类、格式化、落盘等)使用详解

[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...

相关推荐
程序员_三木13 分钟前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
开心工作室_kaic2 小时前
springboot476基于vue篮球联盟管理系统(论文+源码)_kaic
前端·javascript·vue.js
川石教育2 小时前
Vue前端开发-缓存优化
前端·javascript·vue.js·缓存·前端框架·vue·数据缓存
搏博2 小时前
使用Vue创建前后端分离项目的过程(前端部分)
前端·javascript·vue.js
温轻舟2 小时前
前端开发 之 12个鼠标交互特效上【附完整源码】
开发语言·前端·javascript·css·html·交互·温轻舟
web135085886352 小时前
2024-05-18 前端模块化开发——ESModule模块化
开发语言·前端·javascript
LCG元3 小时前
javascript页面设计案例【使用HTML、CSS和JavaScript创建一个基本的互动网页】
javascript
技术程序猿华锋4 小时前
Gemini 2.0 Flash 体验版实测:日常视觉识别的最佳选择,关键在于其API Key现在是免费调用
开发语言·javascript·ecmascript·googlecloud·gemini
TttHhhYy4 小时前
uniapp+vue开发app,蓝牙连接,蓝牙接收文件保存到手机特定文件夹,从手机特定目录(可自定义),读取文件内容,这篇首先说如何读取,手机目录如何寻找
开发语言·前端·javascript·vue.js·uni-app
抓住鼹鼠不撒手4 小时前
xterm.js结合websocket实现web ssh
前端·javascript·websocket