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

Nest.js中如何在全局日志上下文注入JWT验证后的用户数据?

Nest.js 全局日志自动注入JWT用户数据的通用方案

核心思路

利用Node.js的AsyncLocalStorage实现请求级别的上下文共享,把JWT验证后的用户数据存入上下文,让nestjs-pino在打印任何日志时都能自动从上下文取到用户信息,不用在控制器和服务之间手动传参。

具体实现步骤

1. 封装请求上下文存储

新建request.context.ts文件,用AsyncLocalStorage存储请求相关的用户数据:

import { AsyncLocalStorage } from 'async_hooks';

// 可根据你的JWT用户结构定义更具体的类型
export interface RequestContext {
  user?: {
    id: string;
    username: string;
    // 其他用户字段
  };
}

export const requestContext = new AsyncLocalStorage<RequestContext>();

2. 拦截请求并注入用户上下文

编写全局拦截器,在JWT验证完成后将用户数据存入上下文:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { requestContext } from './request.context';

@Injectable()
export class RequestContextInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    // 假设JWT守卫已将用户信息存入request.user
    const ctxData = { user: request.user };
    // 启动上下文,后续整个请求链路均可访问该数据
    return requestContext.run(ctxData, () => next.handle());
  }
}

在AppModule中全局注册这个拦截器:

import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { RequestContextInterceptor } from './request.context.interceptor';

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: RequestContextInterceptor,
    },
  ],
})
export class AppModule {}

3. 配置nestjs-pino从上下文取用户数据

修改nestjs-pino的配置,让customProps从我们的上下文获取用户数据:

import { Module } from '@nestjs/common';
import { LoggerModule } from 'nestjs-pino';
import { requestContext } from './request.context';

@Module({
  imports: [
    LoggerModule.forRoot({
      pinoHttp: {
        customProps: () => {
          // 每次生成日志时从上下文读取用户数据
          const currentContext = requestContext.getStore();
          return {
            user: currentContext?.user ?? null,
          };
        },
        // 你的其他pino配置(如日志格式、级别、输出方式等)
        transport: {
          target: 'pino-pretty',
        },
      },
    }),
  ],
})
export class AppModule {}

4. 服务中使用pino日志

在服务里注入PinoLogger(而非Nest默认的Logger),直接打印日志即可自动带上用户信息:

import { Injectable } from '@nestjs/common';
import { PinoLogger } from 'nestjs-pino';

@Injectable()
export class UserService {
  constructor(private readonly logger: PinoLogger) {
    this.logger.setContext(UserService.name);
  }

  async getUserInfo(userId: string) {
    this.logger.info(`查询用户信息,ID: ${userId}`);
    // 该日志会自动包含user字段,无需手动传递
    // 业务逻辑...
  }
}

原理说明

AsyncLocalStorage会在整个请求调用链(控制器→服务→子服务→工具函数)中维护独立的上下文,无论在哪一层打印日志,pino的customProps都能从上下文拿到当前请求的用户数据,彻底解决服务日志中用户字段为空的问题。

内容的提问来源于stack exchange,提问作者Yaroslav-BV

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:53:22