node项目log4js进行日志记录

安装与了解

npm install log4js --save

目前的版本是:6.6.1

在终端打印消息:

import log4js from 'log4js'

const logger = log4js.getLogger()
logger.level = 'all'
logger.debug('some debug messages')

终端效果:

[2022-09-08T15:25:23.776] [DEBUG] default - some debug messages

log4的日志默认由:时间 级别 类型 - 日志内容 组成
这里有了第一个概念,级别,log4可配置的级别一共有九个,这里找了一个参考图:

image.png

除了 all、off 外,其他七个级别从低到高如上图所示,log4只会输出同级别或者高级别的日志,效果如下:

const logger = log4js.getLogger()
logger.level = 'error'
logger.debug('some debug messages')

此时想去输出 debug 日志,但是设置的级别是error,log4不会输出比设置低级的日志。

第二个概念是类型,log4可以对我们的日志进行分类,回到上面的代码:

const logger = log4js.getLogger()
logger.level = 'all'
logger.debug('some debug messages')

输出:
[2022-09-08T15:25:23.776] [DEBUG] default - some debug messages

此时输出的类型的 default

可以在 getLogger 传入类型名称,使打印出来的日志带上类型:

const logger = log4js.getLogger('超人鸭')
logger.level = 'all'
logger.debug('some debug messages')

输出:
[2022-09-08T15:39:48.632] [DEBUG] 超人鸭 - some debug messages

这个类型在log4中叫 Categories,可以配置,详细配置下面讲。

上面只是log4的最简单使用,跟console.log没啥区别,但记录日志是需要记录到文件里面的,而且需要配置文件内容的格式,让我们更好的定位问题。

配置

log4的配置方式如下:

log4js.configure({
  appenders: {},
  categories: {}
})

通过log4js.configure方法传入一个配置对象进行配置,配置对象里面必须传入 appenders、categories

在进行配置之前,先简单介绍一个log4三个最重要的对象之间的关系,以便更好的理解,三个对象为:appenders、categories、logger。(当然这都是我自己的理解,如有错误或者更好的解释欢迎指教,)

首先是 appenders,它的作用为定义日志输出到哪里:控制台、文件等,定义文件的格式:是普通文件还是以日期创建的文件,定义文件内容的格式:如何换行、包不包含调用栈、甚至可以通过函数插入内容等。

然后是 categories,它的作用是定义日志的类型,在输出的日志中显示。一个 categories 需要包含一个或多个 appenders,也就是定义某一种类型的时候,需要从定义好 appenders 中选择好这类日志该如何输出;同时定义类型的时候也需要定义好这类日志的级别。在配置的时候,必须配置一个 default 类型的配置。

最后是 logger ,它就是通过log4js.getLogger()返回的日志打印器,我们通过它来输出各个级别日志,通过传入配置的 categories 名称,来设置输出日志的类型,如果没有传入名称或者传入一个没有配置的类型名称,就会使用 default 类型的配置。

所以三者的关系总结就是:通过 logger 去输出,定义 logger 时传入配置好的 categories , 而每一个categories 又包含定义好的 appenders, 它规定日志如何去输出。

下面通过一个官方的例子来了解一下:

import log4js from 'log4js'

log4js.configure({
  appenders: {
    out: { type: 'console' },
    app: { type: 'file', filename: 'application.log' }
  },
  categories: {
    default: { appenders: ['out'], level: 'debug' },
    test: { appenders: ['out', 'app'], level: 'debug' }
  }
})

const logger = log4js.getLogger('test')
logger.level = 'debug'
logger.debug('some debug messages')

此时控制台输出:

[2022-09-08T16:23:18.464] [DEBUG] test - some debug messages

同时在项目的根目录自动生成一个 application.log 文件:

image.png

上面的 appenders 配置有非常多,需要先配置好 type,也就是日志输出的类型,有:console直接输出到终端、file 普通文件、dateFile 日期文件等等。

而每一种 type的其他配置又有所不同,下面我会简单讲我自己真正项目中的配置,有缘看到这篇文章的人可以作为参考。而 appenders 的其他详细配置就通过官方文档了解吧。

参考配置

在真正项目需要去定位问题时,通过日期去查看文件会比较直接,所以 appenders 配置的 type 我选择的是 dateFile,其他配置都是基于这个 type。下面是我完整的配置:

import log4js from 'log4js'

log4js.configure({
  appenders: {
    console: {
      type: 'console',
      layout: { type: 'coloured' }
    },
    reqLog: {
      type: 'dateFile',
      filename: './logs/req/req',
      alwaysIncludePattern: true,
      pattern: 'yyyy-MM-dd.log',
      numBackups: 10
    },
    resLog: {
      type: 'dateFile',
      filename: './logs/res/res',
      alwaysIncludePattern: true,
      pattern: 'yyyy-MM-dd.log',
      numBackups: 10
    },
    infoLog: {
      type: 'dateFile',
      filename: './logs/info/info',
      alwaysIncludePattern: true,
      pattern: 'yyyy-MM-dd.log',
      numBackups: 10,
      layout: { type: 'pattern', pattern: '%d{yyyy-MM-dd hh:mm:ss}  %m %n%s' }
    },
    warnLog: {
      type: 'dateFile',
      filename: './logs/warn/warn',
      alwaysIncludePattern: true,
      pattern: 'yyyy-MM-dd.log',
      numBackups: 10,
      layout: { type: 'pattern', pattern: '%d{yyyy-MM-dd hh:mm:ss}  %m %n%s' }
    },
    errLog: {
      type: 'dateFile',
      filename: './logs/err/err',
      alwaysIncludePattern: true,
      pattern: 'yyyy-MM-dd.log',
      numBackups: 10,
      layout: { type: 'pattern', pattern: '%d{yyyy-MM-dd hh:mm:ss}  %m %n%s' }
    }
  },
  categories: {
    default: {
      appenders: ['infoLog', 'console'],
      level: 'all',
      enableCallStack: true
    },
    req: {
      appenders: ['reqLog'],
      level: 'info'
    },
    res: {
      appenders: ['resLog'],
      level: 'info'
    },
    info: {
      appenders: ['infoLog', 'console'],
      level: 'info',
      enableCallStack: true
    },
    warn: {
      appenders: ['warnLog', 'console'],
      level: 'warn',
      enableCallStack: true
    },
    err: {
      appenders: ['errLog', 'console'],
      level: 'error',
      enableCallStack: true
    }
  },
  pm2: true,
  disableClustering: true
})

下面对一些配置进行解释:
appenders 中的 layout ,通过它就可以自定义日志内容,其中 layout: { type: 'coloured' } 代表输出有颜色的日志,适合在终端中查看(现在的版本不配置layout好像也有颜色区别)。
layout: { type: 'pattern', pattern: '%d{yyyy-MM-dd hh:mm:ss} %m %n%s' }代表通过占位关键词进行自定义格式定义,其中的 %s 代表输出调用栈,通常在错误日志中,我们需要知道是哪里打出来的日志,哪个文件哪个函数哪一行。如果需要输出 %s,在 categories 配置中就需要添加 enableCallStack: true 的配置。%m 代表日志内容, %n 代表换行。

配置中的 appenders 类型为 dateFile 的这几个配置:

filename: './logs/req/req.log',
alwaysIncludePattern: true,
pattern: 'yyyy-MM-dd.log

就都是配置文件名的格式,生成的文件名格式为:req.log.yyyy-MM-dd.log

配置中的 numBackups: 10 代表保留10天的日志文件,防止无限生成浪费磁盘空间。

然后是 categories ,类型的配置就比较简单,定义好每一种类型包含的 appenders 与 等级就行,对于部分日志,同时包含了文件记录与终端输出,方便开发时也可以即时查看,不需要再 console.logcategories 必须包含一个 default 配置。

最后一个是 pm2 ,对于使用 pm2 进行线上部署的项目,就需要使用,disableClustering设置为true是为了解决pm2开启多线程后子线程日志不会打印的问题。

如果更改 pm2 的多进程注入的环境变量名称,就需要再配置 pm2InstanceVar ,这里详细就看官方文档了。

上面就是我真实项目中使用的配置,之后通过 log4js.getLogger() 得到 logger 方法,进行日志记录。上面我的 categories 配置基本是以类似等级来区别,通常都是不同等级的日志输出到不同的文件,所以 logger 方法输出的等级就是 categories 配置的等级且不会变,对此,可以定义好各个类型的输出方法,供外部调用,在与配置的同一个文件里面:

export function reqLog(content: any): void {
  const logger = log4js.getLogger('req')
  logger.info(content)
}

export function resLog(content: any): void {
  const logger = log4js.getLogger('res')
  logger.info(content)
}

export function infoLog(content: any): void {
  const logger = log4js.getLogger('info')
  logger.info(content)
}

export function warnLog(content: any): void {
  const logger = log4js.getLogger('warn')
  logger.warn(content)
}

export function errLog(content: any): void {
  const logger = log4js.getLogger('err')
  logger.error(content)
}

下面简单的示例,记录接口的请求与返回,使用koa的中间件,在koa的入口文件:

import { reqLog, resLog } from './utils/log4'

app.use(async (ctx, next) => {
  reqLog(ctx.request)
  await next()
  resLog(ctx.response)
})

当引入log4配置文件后,会自动在项目根目录生成 appenders 定义日志文件:

image.png

下面随便调用一个接口,看看文件内容,req、res都没有使用 layout 定义:
req请求文件:
image.png

res响应文件:
image.png

node中打印对象

先来看看一个现象:

const obj = {
  1: {
    2: {
      3: {
        4: {
          5: {
            6: {}
          }
        }
      }
    }
  }
}
console.log(obj)

此时终端的输出:

image.png

可以看到打印到第三层往后,就会缩成 [Object] ,这时候可不像浏览器一样可以点展开。

这时候我们用log4去记录这个对象,看会得到什么:

reqLog(obj)

回到刚刚的文件:

image.png

可以看到输出的文件效果与 console.log 在终端的效果一样,它内部进行的处理与node处理是一样的,所以我们在输出日志之前需要对内容进行处理,使对象不会折叠,这里需要借助到 node 的 util 模块中的 inspect 方法。使用如下:

import util from 'util'

const obj = {
  1: {
    2: {
      3: {
        4: {
          5: {
            6: {}
          }
        }
      }
    }
  }
}
console.log(
  util.inspect(obj, {
    showHidden: false,
    depth: null,
    colors: false
  })
)

效果:


image.png

深层对象记录的问题解决,修改一下log4导出的方法:

function inspectContent(content: any) {
  return util.inspect(content, {
    showHidden: false,
    depth: null,
    colors: false
  })
}

export function reqLog(content: any): void {
  const logger = log4js.getLogger('req')
  logger.info(inspectContent(content))
}

export function resLog(content: any): void {
  const logger = log4js.getLogger('res')
  logger.info(inspectContent(content))
}

export function infoLog(content: any): void {
  const logger = log4js.getLogger('info')
  logger.info(inspectContent(content))
}

export function warnLog(content: any): void {
  const logger = log4js.getLogger('warn')
  logger.warn(inspectContent(content))
}

export function errLog(content: any): void {
  const logger = log4js.getLogger('err')
  logger.error(inspectContent(content))
}

至此,一套可以供真实项目记录日志的log4配置就配置好了,如果你有更好的见解与用法,欢迎指教。

你可能感兴趣的:(node项目log4js进行日志记录)