You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无需传参在其他类中使用Fastify的req.logger

Fastify全链路获取请求级logger实现方案

核心思路:使用Node.js内置的AsyncLocalStorage异步上下文存储能力,在请求入口绑定当前请求带correlationId的logger实例,全链路任意位置可直接读取,无需逐层传参。


步骤1:创建上下文存储工具

新建logger-context.ts文件,封装存储、读取logger的工具方法:

// logger-context.ts
import { AsyncLocalStorage } from 'async_hooks';
import type { FastifyBaseLogger } from 'fastify';

const loggerStorage = new AsyncLocalStorage<FastifyBaseLogger>();

/**
 * 包裹请求处理流程,将当前请求的logger注入异步上下文
 */
export function runWithLogger<T>(logger: FastifyBaseLogger, callback: () => T): T {
  return loggerStorage.run(logger, callback);
}

/**
 * 从当前异步上下文获取绑定了correlationId的请求logger
 */
export function getLogger(): FastifyBaseLogger {
  const logger = loggerStorage.getStore();
  if (!logger) {
    throw new Error('No logger found in current async context, please call this method in request lifecycle');
  }
  return logger;
}

步骤2:Fastify全局钩子绑定上下文

修改app.ts,添加onRequest钩子,每个请求进入时自动将req.logger注入异步上下文,后续所有该请求触发的异步逻辑都能读取到对应logger:

import pino from 'pino';
import fastify from 'fastify';
import { runWithLogger } from './logger-context';

declare module 'fastify' {
  interface FastifyInstance {
    config: Config;
  }
}

function build() {
  const app = fastify({
    logger: pino({
      name: process.env.NAME,
      level: process.env.LOG_LEVEL,
    }),
    disableRequestLogging: true,
    requestIdHeader: 'correlation-id',
    requestIdLogLabel: 'correlationId',
  });

  // 新增请求钩子,绑定logger到异步上下文
  app.addHook('onRequest', (req, reply, done) => {
    runWithLogger(req.logger, done);
  });

  // 原有插件注册逻辑保持不变,UseCase、TestClass维持单例即可
  app.register(apiRoutes, () => ({
    getObjectUseCase: new GetObjectUseCase(new TestClass()),
  }));

  return app;
}
export { build };

步骤3:业务代码直接取用logger

不需要修改类构造函数,不需要给方法传logger参数,在任意业务方法中直接引入getLogger即可拿到当前请求绑定的logger实例:

GetObjectUseCase.ts 改造

import { getLogger } from './logger-context';

export class GetObjectUseCase {
  private anotherClass: TestClass;

  constructor(anotherClass: TestClass) {
    this.anotherClass = anotherClass;
  }

  async execute(id: string): Promise<StoredObject> {
    // 直接获取带correlationId的logger使用
    const logger = getLogger();
    logger.info(`Start executing getObject flow, target id: ${id}`);
    
    // 下游类方法无需传logger,内部自行获取即可
    return this.anotherClass.getById(id);
  }
}

TestClass.ts 改造

import { getLogger } from './logger-context';

export class TestClass {
  async getById(id: string): Promise<StoredObject> {
    const logger = getLogger();
    logger.debug(`Start querying record by id: ${id}`);
    // 原有业务逻辑
  }
}

路由层原有代码无需改动,req.logger和下游业务层拿到的是同一个logger实例,所有日志都会自动携带当前请求的correlationId。


注意事项

  • AsyncLocalStorage是Node.js官方原生实现,性能开销极低,生产环境可用,不会出现请求间日志串号问题。
  • 禁止将getLogger()取到的logger实例存为类的成员变量,类是单例的,存成员变量会被后续请求覆盖导致日志串号,每次使用时实时调用getLogger()获取即可。
  • 非请求场景(比如定时任务、启动初始化)调用业务类时,可以手动传入全局logger,用runWithLogger包裹执行逻辑,避免抛出找不到上下文的错误。

内容的提问来源于stack exchange,提问作者Nicolas García

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:18:19