Nodejs nestjs 日志问题 traceId

发布于 1周前 作者 ionicwang 来自 nodejs/Nestjs

nestjs(express)做了一个简单的服务
目前用 winston替换了 nestjs的默认日志及 typeorm的日志
有时候需要查看日志定位问题,所以想在日志中添加 traceId用于追踪一次 request的完整链路,最好是无代码侵入的
有查到使用 Async hooks进行 request上下文保存的
大家有没有已实现的方案?


Nodejs nestjs 日志问题 traceId

24 回复

没用 winston ,用的 pino ,自带了 genReqId 的方法


既然是你自己做的服务那你就在 logger.log()的时候把你要的 traceId 加进去呗…
没怎么看懂你有什么进一步需求

需要调用其他函数的时候,traceId 要当做参数继续传递下去吗?因为之前没有做过 traceId 这些,所以想知道一些详细的方案。traceId 的生成能和 jwt 中的 userId 做一些关联吗?

你是用了 fastify 还是 express 配合 pino 使用的?

哦所以你问的是 tracerId 的生成问题而不是怎么在日志里加 id 的问题是吧?
这个讲道理没有什么固定规则呀当然你怎么看得顺眼怎么来。要我的话函数名加个时间戳么差不多了…最多再加个 api 名

nestjs 是不太好做的,所以我自己实现了一个类似的框架 sensejs
进一步了解: https://sensejs.io 或者 https://github.com/sensejs/sensejs

有如何在日志中加 id 的疑问,刚才说的 logger.log()直接加 traceId ,那如果调用其它函数,traceId 要当做参数继续往下传递是吧?

NestJS 印象中我是这么做的,在依赖注入框架下,然后在每个类初始化的时候用,可以把 requestId 注入进来,并关联到 Logger 上,requestId 的产生需要定义一个 provider 。当然这种做法还是有一定的侵入性

这个都是你自己决定的…你想接着用这个 id 就往下传,想用个新的就再生成一个呗

感谢大佬,我去试试

感谢大佬,大概了解一些了

大概是在中间件使用 continuation-local-storage 或者 cls-hooked 记录 jwt 的 id 或者 生产的 reqId 然后供上下文使用,然后 logger 记录的时候不需要显式传参。

async hooks 不是 100%可靠的。这点比较坑,打日志可以用,但是不要做业务,比如存储 tenant id 之类的

总结的太棒了😀

nestjs-cls https://papooch.github.io/nestjs-cls/
或者自己实现一个中间件也是一样的

个人暂时还是比较笨的方法实现,把 logger 对象挂在 req 上,请求的开头自己生成一个 traceid,挂上去,后续这个 req ,在 nestjs 的生命周期里都是能拿到的,然后用类似 req.logger.debug() 这种方式打印,整个生命周期就都是用 traceid 关联的了。如果还有其他服务,也可以把这个 traceid 放到请求头或者其他地方,一路传下去,也可以实现多个服务被一个 traceid 串起来的功能


把 logger 放到 req 是個不錯的想法,反正 req 都要一直往下傳的

感谢大佬,这个 nestjs-cls 中 requestID 的 case ,比较符合我想要的效果

很实用的想法,我之前想到在请求拦截中生成 traceId ,挂在 req 对象上,controller 里解出来,后续一直在 service 中做传递。但觉得应该有更优雅的实现

当初网上确实找不到成熟方案,所以自己实现的,核心就是利用 AsyncLocalStorage
1 使用 express-request-id 作为日志的 traceid
2 基于 AsyncLocalStorage 自己写一个中间件,获取请求头里的 traceid 并赋值到 AsyncLocalStorage 的实例中,这个是最核心的,代码不多,node 版本好想要 14 以上,网上有相关教程
3 重写 console 的 log 合 error 方法,日志就用这两个,没用第三方 logger ,这样所有请求默认的日志就会带 traceid
4 关于定时任务因为不走请求所以没有 traceid ,需要自己随机生成一个然后在最外层包进去(我是机器时间+任务名)

最终效果大概这样
https://s2.loli.net/2023/08/22/9EOeUzQD5XvAqWj.png
定时任务的
https://s2.loli.net/2023/08/22/x4dHy8wcLiM5mP3.png

理论上最终可以做成想 java 那样的把模块方法名也打印出来

不错的实现方式。我目前直接引用了 nestjs-cls 这个包,全局注册时自定义了 traceid 生成规则,在 winston logger 的 printf 方法中通过 cls 取出 traceid 进行打印。

看了下原理一样的,也是基于 AsyncLocalStorage ,可惜当初做的时候这包还没出来,不然就省事了

在Node.js中使用NestJS框架时,日志管理和追踪(特别是通过traceId)是确保系统可维护性和调试效率的关键。为了实现这一点,你可以使用NestJS内置的日志系统结合中间件或拦截器来生成和传递traceId。

首先,你可以使用中间件来为每个请求生成一个唯一的traceId,并将其附加到请求对象中。这里是一个简单的中间件示例:

import { Injectable, NestMiddleware, MiddlewareConsumer } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class TraceIdMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    req.traceId = `${Date.now()}-${Math.floor(Math.random() * 100000)}`;
    console.log(`[${req.traceId}] Request received`);
    next();
  }
}

export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(TraceIdMiddleware)
      .forRoutes('*');
  }
}

然后,你可以在服务或控制器中使用这个traceId来记录日志。例如,在控制器中:

@Get()
findAll(@Req() req) {
  console.log(`[${req.traceId}] Handling GET request`);
  // 业务逻辑
}

通过这种方式,你可以确保每个请求的日志都包含一个唯一的traceId,便于追踪和调试。此外,你还可以考虑使用更高级的日志管理库(如winston)来进一步丰富日志功能。

回到顶部