EggJS实现一个简易的链路日志,集成到kibana中

项目背景

公司部署的kibana目前是从容器中的标准输入输出中采集日志,目前还不支持指定文件或者目录,egg本身的日志是基于文件的,所以是没发在kibana中查看egg本身打印的日志

标准输入输出

linux shell下常用输入输出操作符是:

1.  标准输入   (stdin) :代码为 0 ,使用 < 或 << ; /dev/stdin -> /proc/self/fd/0   0代表:/dev/stdin 
2.  标准输出   (stdout):代码为 1 ,使用 > 或 >> ; /dev/stdout -> /proc/self/fd/1  1代表:/dev/stdout
3.  标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ; /dev/stderr -> /proc/self/fd/2 2代表:/dev/stderr
复制代码

node中console.log会输出到stdout、console.error输出到stderr,所以要想收集到日志,那就要用console方式输出,

但是node中console是一个同步操作,所以频繁的执行console输出到终端,会阻塞进程,影响性能,所以日志输出需要有一个合并操作,所以我需要自己写一个个性化日志输出插件,我最先想到的就是利用egg提供的拓展去做这件事

Egg 拓展

Egg 框架提供了多种扩展点扩展自身的功能:

Application
Context
Request
Response
Helper
在开发中,我们既可以使用已有的扩展 API 来方便开发,也可以对以上对象进行自定义扩展,进一步加强框架的功能。
复制代码

我选择基于Context进行拓展,给他增加一个自定义Log方法,代码如下,可以根据自己的业务需求自行修改,文件路径为app/extend/context.js

module.exports = {
  logs: [],
  LogStart (text) {
    const time = new Date()
    this.logs = [{
      time,
      text: 'start print log'
    }]
  },
  Log (text) {
    const time = new Date()
    this.logs.push({
      time,
      text
    })
  },
  LogEnd (type) {
    const tranceId = new Date().getTime()
    const url = this.request.url
    const userId = this.request.header['user-id']
    let logObj = {
      tranceId,
      userId,
      url,
      steps: {}
    }
    this.logs.map((item, index) => {
      let timeStr = item.time.toLocaleString() + ' ' + item.time.getMilliseconds() + 'ms'
      logObj.steps[index] = `< ${timeStr} > - ${item.text}`
    })
    const useTime = new Date().getTime() - new Date(this.logs[0].time).getTime()
    logObj.useTime = `${useTime} ms`
    if (type === 'error') {
      console.error('System Error:' + JSON.stringify(logObj))
    } else {
      console.log(JSON.stringify(logObj))
    }
  }
} 
复制代码

egg日志打印策略

通常 Web 访问是高频访问,每次打印日志都写磁盘会造成频繁磁盘 IO,为了提高性能,我们采用的文件日志写入策略是: 日志同步写入内存,异步每隔一段时间(默认 1 秒)刷盘

所以我在日志调用结束会将日志的完整链路一次全部打出,防止频繁调用,只输出一条记录,在kibana中方便查看

这样我们在中间件中就可以这样调用

module.exports = function (options) {
    return async function proxy(ctx, next) {
        await next()
        ctx.LogStart()
        ctx.Log('你要打印的日志!')
        ctx.Log('执行A操作!')
        ctx.Log('执行B操作!')
        ctx.LogEnd()
    };
};
复制代码

最后在kibana可以看到下面这样

kibana
整个链路的日志都能统一输出
kibana

以上是个人在项目中的一次小的尝试,如有问题,还请指出,谢谢

猜你喜欢

转载自juejin.im/post/5c74fac16fb9a049dd80f256