如何用node优雅地打印全链路日志

 更新时间:2022年03月07日 16:33:36   作者:lemon在努力  
这篇文章主要给大家介绍了关于如何用node优雅地打印全链路日志的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

前言

当用户报问题:线上某个功能使用报错时,如何快速准确地定位?当某个请求接口返回数据缓慢时,如何有效地追踪优化?

一、原理和实践

众所周知,当一个请求到来时,大概会有以下日志产生:

1、AceesLog:用户访问日志

2、Exception:代码异常日志

3、SQL:sql查询日志

4、ThirdParty:第三方服务日志

该如何追踪一条请求产生的所有日志?

一般做法是使用一个requestId来做唯一标识,

然后写一个中间件,把requestId注入到context上下文中,当需要打日志时,再从context中取出打印,

在第三方服务和SQL日志中,还需要把requestId传入到相应的函数里面打印,这样层层传递,实在太麻烦,代码侵入性也比较强。

我们的目标是降低代码侵入性,一次注入,自动跟踪。

经过调研,async_hooks可以追踪异步行为的生命周期,在每个异步资源(每个请求都是一个异步资源)中,它都有2个ID,

分别是asyncId(异步资源当前生命周期ID),trigerAsyncId(父级异步资源ID)。

async_hooks提供了以下生命周期钩子来监听异步资源:

asyncHook = async_hook.createHook({
  // 监听异步资源的创建
  init(asyncId,type,triggerAsyncId,resource){},
  // 异步资源回调函数开始执行之前
  before(asyncId){},
  // 异步资源回调函数开始执行后
  after(asyncId){},
  // 监听异步资源的销毁
  destroy(asyncId){}
})

那如果我们做一个映射,每个asyncId映射一个storage,storage里面再存储对应的requestId,那requestId就可以很容易获取了。

正好cls-hooked这个库已经基于async_hooks做好了封装,在同一份异步资源维护一份数据,以键值对的形式存储。(注意:async_hooked需要在高版本node>=8.2.1使用)当然社区中还有其他的实现,比如cls-session,node-continuation-local-storage等。

下面讲下我把cls-hooked运用在我项目中的实例:

/session.js 创建命名存储空间

const createNamespace = require('cls-hooked').createNamespace 
const session = createNamespace('requestId-store') 
module.exports = session

/logger.js 打印日志

const session = require('./session') 
module.exports = { 
info: (message) => 
{ 
const requestId = session.get('requestId')
console.log(`requestId:${requestId}`, message) 
}, 
error: (message) => 
{ 
const requestId = session.get('requestId') 
console.error(`requestId:${requestId}`, message) 
} 
}

/sequelize.js sql调用logger打印日志

const logger = require("./logger") 
new Sequelize( 
logging: function (sql, costtime) { 
logger.error( `sql exe : ${sql} | costtime ${costtime} ms` ); 
} )

/app.js 设置requestId、设置requestId返回响应头、打印访问日志

const session = require('./session') 
const logger = require('./logger') 
async function accessHandler(ctx, next) 
{ 
const requestId = ctx.header['x-request-id'] || uuid()
const params = ctx.request.body ? JSON.stringify(ctx.request.body) : JSON.stringify(ctx.request.query)
// 设置requestId session.run(() => { session.set('requestId', requestId)
logger.info(`url:${ctx.request.path};params:${params}`) next()
// 设置返回响应头
ctx.res.setHeader('X-Request-Id',requestId)
}) }

我们看下当一条请求路径是/home?a=1到来时的日志:

访问日志:
requestId:79f422a6-6151-4bfd-93ca-3c6f892fb9ac url:/home;params:{"a":"1"}

Sql日志:
requestId:79f422a6-6151-4bfd-93ca-3c6f892fb9ac sql exe :
Executed (default): SELECT `id` FROM t_user

可以看到同一条请求整个链路的日志requestId是一样的。如果后面有告警发到告警平台,那么我们根据requestId就可以找到这条请求执行的整个链路了。

细心的同学可能会观察到我在接口返回的响应头里面也设置了requestId,目的就是为了后续如果发现某条请求响应缓慢或者有问题,那直接从浏览器就可以知道requestId,就可以做分析了。

二、性能开销

我本地做了一下压测,

这是内存的占用对比:

比未使用async_hook多了约10%。

对于我们qps是百级别的系统还好,但是如果是高并发的服务,可能要慎重考虑下了。

总结

到此这篇关于如何用node优雅地打印全链路日志的文章就介绍到这了,更多相关node打印全链路日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • 基于Node的Axure文件在线预览的实现代码

    基于Node的Axure文件在线预览的实现代码

    这篇文章主要介绍了基于Node的Axure文件在线预览的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • Node.js+ELK日志规范的实现

    Node.js+ELK日志规范的实现

    这篇文章主要介绍了Node.js+ELK日志规范的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • 基于nodejs 的多页面爬虫实例代码

    基于nodejs 的多页面爬虫实例代码

    本篇文章主要介绍了基于nodejs 的多页面爬虫 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • node.js中的fs.createReadStream方法使用说明

    node.js中的fs.createReadStream方法使用说明

    这篇文章主要介绍了node.js中的fs.createReadStream方法使用说明,本文介绍了fs.createReadStream方法说明、语法、接收参数、使用实例和实现源码,需要的朋友可以参考下
    2014-12-12
  • 利用Node.js了解与测量HTTP所花费的时间详解

    利用Node.js了解与测量HTTP所花费的时间详解

    这篇文章主要给大家介绍了关于利用Node.js了解与测量HTTP所花费的时间的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • NodeJS学习笔记之FS文件模块

    NodeJS学习笔记之FS文件模块

    在看nodejs介绍的过程中,nodejs对自己的异步I/O是重点突出的说明的。在fs模块中,nodejs提供了异步和同步两种读写方式
    2015-01-01
  • node通过npm写一个cli命令行工具

    node通过npm写一个cli命令行工具

    本篇文章主要介绍了node通过npm写一个cli命令行工具 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • 举例讲解Node.js中的Writable对象

    举例讲解Node.js中的Writable对象

    这篇文章主要介绍了举例讲解Node.js中的Writable对象,是Node.js入门学习中的基础知识,需要的朋友可以参考下
    2015-07-07
  • Node使用koa2实现一个简单JWT鉴权的方法

    Node使用koa2实现一个简单JWT鉴权的方法

    这篇文章主要介绍了Node使用koa2实现一个简单JWT鉴权的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Node.js中ES6模块化及Promise对象

    Node.js中ES6模块化及Promise对象

    这篇文章主要介绍了Node.js中ES6模块化及Promise对象,node.js 遵循了 CommonJS 的模块化规范,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07

最新评论