当前位置: 首页 > 工具软件 > log4js > 使用案例 >

log4js使用笔记

江棋
2023-12-01

1. 前言

本文将介绍笔者log4j时的一些笔记,以期协助读者规避一些问题。

2. 使用步骤

2.1 配置logjs

// src/config/log4js.config.js

// 配置应用名(英文)数组,根据配置的应用名,生成 appenders 、categories
const appNames = ['nginx', 'tomcat']

/**
 * 内置两种布局:layout:console(针对控制台、终端)、layout:basic(针对文件)
 *
 * 代码:
    require('@/config/log4js.config')
 */
const path = require('path')
const log4js = require('log4js')
const Logger = require('@/logger')

/**
 * 添加控制台的简单布局
 */
log4js.addLayout('layout:console', function (config) {
  return function (event) {
    const { type, message } = event.data[0]
    return `${type}: ${message}`
  }
})

/**
 * 添加基础布局
 */
log4js.addLayout('layout:basic', function (config) {
  return function (event) {
    return JSON.stringify(event.data[0])
  }
})

const log4jsConfig = {
  appenders: {
    'appender:console': {
      type: 'console',
      layout: { type: 'layout:console', separator: ',' },
    },
  },
  categories: {
    default: {
      appenders: ['appender:console'],
      level: 'DEBUG',
      enableCallStack: false,
    },
  },
}

for (const appName of appNames) {
  const appenderName = `appender:${appName}`
  log4jsConfig.appenders[appenderName] = {
    type: 'dateFile',
    filename: path.join(Logger.getDefaultDirPath(appName), appName),
    maxLogSize: Math.pow(1024, 3),
    pattern: 'yyyy-MM-dd.txt',
    keepFileExt: true,
    fileNameSep: '.',
    alwaysIncludePattern: true,
    backups: 3,
    compress: false,
    layout: {
      type: 'layout:basic',
    },
  }
  log4jsConfig.categories[appName] = {
    appenders: [appenderName, 'appender:console'],
    level: 'DEBUG',
    enableCallStack: false,
  }
}

log4js.configure(log4jsConfig)

2.2 log4js基础类

// src/logger/index.js

// https://github.com/log4js-node/log4js-example/blob/master/config/log4js.json
// https://log4js-node.github.io/log4js-node/file.html
const log4js = require('log4js')
const moment = require('moment')
const path = require('path')
const { OSUtils, CryptoUtils } = require('s-utils')

/**
 * @class Logger
 * @classdesc 日志基础类
 */
module.exports = class Logger {
  static INFO = 'info'
  static ERROR = 'error'
  static WARN = 'warn'
  static DEBUG = 'debug'

  /**
   * Creates an instance of Logger.
   * @param {string} [appName='log4js'] 应用名称(英文)
   * @memberof Logger
   */
  constructor (appName = 'log4js') {
    // 日志信息数组
    this.logs = []
    // 当前日志的ID
    this.id = 0
    // log4js实例
    this.logger4js = null
    // 应用的名称(英文)
    this.appName = appName
    this.logger4js = this.getLog4jsLogger()
  }
  /**
   * 获取默认日志目录路径
   *
   * @param {string} appName 应用的名称(英文名)
   * @returns {string}
   * @memberof Logger
   */
  static getDefaultDirPath (appName) {
    return path.join(OSUtils.getHomeDirPath(), `logs/${appName}`)
  }
  /**
   * 获取log4js日志实例
   *
   * @returns {object}
   * @memberof Logger
   */
  getLog4jsLogger () {
    return log4js.getLogger(this.appName)
  }
  /**
   * 获取当前日志数组
   *
   * @returns {object[]} 当前日志数组
   * @memberof Logger
   */
  getLogs () {
    return this.logs
  }
  /**
   * 清空当前日志数组
   *
   * @memberof Logger
   */
  emptyLogs () {
    this.logs.length = 0
  }
  /**
   * 生成日志
   *
   * @param {string} type 日志类型
   * @param {string} message 日志内容
   * @param {object} [extraData={}] 额外的数据,json对象
   * @returns {object} 日志信息对象
   * @memberof Logger
   */
  generateLogInfo (type, message, extraData = {}) {
    const time = moment().format('YYYY-MM-DD HH:mm:ss')
    const logInfo = {
      // 取md5值
      id: CryptoUtils.md5(`${this.id}-${time}`),
      type,
      message,
      time,
      extraData,
    }
    this.id++
    return logInfo
  }
  /**
   * 记录日志
   *
   * @param {string} type 日志类型
   * @param {string} message 日志内容
   * @param {object} [extraData={}] 额外的数据,json对象
   * @memberof Logger
   */
  log (type, message, extraData = {}) {
    var _a
    // 内容为空时,不展示日志
    if (
      !((_a =
        message === null || message === void 0 ? void 0 : message.trim) ===
        null || _a === void 0
        ? void 0
        : _a.call(message))
    ) {
      return
    }
    const log = this.generateLogInfo(type, message, extraData)
    // 类型对应的日志函数为空时,使用info函数
    const logFn = this.logger4js[type]
      ? this.logger4js[type]
      : this.logger4js.info
    logFn.call(this.logger4js, log)
    this.logs.push(log)
  }
  /**
   * 记录常规日志
   *
   * @param {string} message 日志内容
   * @param {object} [extraData={}] 额外的数据,json对象
   * @memberof Logger
   */
  info (message, extraData = {}) {
    this.log(Logger.INFO, message, extraData)
  }
  /**
   * 记录错误日志
   *
   * @param {string} message 日志内容
   * @param {object} [extraData={}] 额外的数据,json对象
   * @memberof Logger
   */
  error (message, extraData = {}) {
    this.log(Logger.ERROR, message, extraData)
  }
  /**
   * 记录警告日志
   *
   * @param {string} message 日志内容
   * @param {object} [extraData={}] 额外的数据,json对象
   * @memberof Logger
   */
  warn (message, extraData = {}) {
    this.log(Logger.WARN, message, extraData)
  }
  /**
   * 记录调试日志
   *
   * @param {string} message 日志内容
   * @param {object} [extraData={}] 额外的数据,json对象
   * @memberof Logger
   */
  debug (message, extraData = {}) {
    this.log(Logger.DEBUG, message, extraData)
  }
}

2.3 创建logger实例

2.3.1 配置log4js

在业务项目的入口文件,配置log4js。必须要先配置后使用。

// src/main.js

require('@/config/log4js.config')

2.3.2 创建实例

const logger = new Logger('nginx')
logger.info('This is test info.', { addtional: 'test' })

特别说明:
OSUtils, CryptoUtils是笔者自己工具库里的工具函数,可根据需要使用其他方式来替换。

2.4 在pm2中使用log4js

步骤:

  • 执行:pm2 install pm2-intercom
  • log4js中配置:
log4js.configure({
  appenders: { out: { type: "stdout" } },
  categories: { default: { appenders: ["out"], level: "info" } },
  pm2: true,
  pm2InstanceVar: "INSTANCE_ID",
});
  • pm2中配置:
// 当使用log4js时,需要配置instance_var
 instance_var: 'INSTANCE_ID',

具体请查看这里

3. 常见问题

3.1 按照如上步骤配置log4js之后,并未如期记录日志

请检查是否使用了类似webpack之类的打包工具。如果使用了,再检查Logger是否适合业务项目一起构建的,如果Logger类是单独构建的,就会导致所做的配置不会生效。因为按照log4js的规则,若要对它正确配置,必须要确保配置时和使用时,在运行的时候是同一个log4js库,如果使用了webpack并且Logger类是单独构建的,单独构建的Logger类里的log4js和业务项目里的log4js不是同一个实例,在业务项目里做的配置,就不会生效。

 类似资料: