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

NestJS共享日志模块抽为NPM包后请求触发服务重启报错

问题背景

开发可在多个微服务间复用的NestJS共享日志模块时,日志模块代码直接内置在微服务项目中运行正常,但将代码抽离为独立NPM包引入后功能异常。

异常复现代码

共享NPM包内的日志模块代码:

// my-logger.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [
    LoggerModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        pinoHttp: {
          level: process.env.LOG_LEVEL || 'info',
          redact: configService.get<string[]>('logger.redacted.fields'),
          prettyPrint: {
            colorize: false,
            singleLine: true,
            levelFirst: false,
            translateTime: "yyyy-mm-dd'T'HH:MM:ss.l'Z'",
            messageFormat: '{req.headers.x-correlation-id} [{context}] {msg}',
            ignore: 'pid,hostname,context,req,res,responseTime',
            errorLikeObjectKeys: ['err', 'error'],
          },
        },
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [],
  providers: [],
})
export class MyLoggerModule {}

微服务侧App Module代码:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from '../config/configuration';
import { MyLoggerModule } from '@my-company/my-logger.module';
import { HttpModule } from '@nestjs/axios';

@Module({
  imports: [
    ConfigModule.forRoot({ load: [configuration] }),
    MyLoggerModule,
    HttpModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

异常现象

引入共享NPM包的微服务可正常构建、部署,但每次收到新请求时都会触发服务重启,抛出如下错误:

node[1]: ../src/tcp_wrap.cc:149:static void node::TCPWrap::New(const v8::FunctionCallbackInfo<v8::Value>&): Assertion `args[0]->IsInt32()' failed.

问题根本原因

该问题和ConfigService本身逻辑无关,核心是NPM依赖重复安装导致的Nest模块多实例隔离:

  • 抽离为独立NPM包时,如果未将@nestjs/common、@nestjs/config、nestjs-pino这类框架级依赖声明为peerDependencies,NPM会在共享包的独立node_modules目录下安装一套属于自己的依赖副本,最终整个项目中会存在两份完全独立的Nest运行时实例、两份ConfigModule定义。
  • 共享日志模块中引用的ConfigModule来自共享包自身的依赖副本,和微服务AppModule中通过ConfigModule.forRoot()初始化的ConfigModule不是同一个实例,因此异步工厂中注入的ConfigService没有加载微服务侧自定义的configuration配置,configService.get<string[]>('logger.redacted.fields')返回undefined。
  • pino-http收到undefined类型的redact配置后,初始化日志传输层时会传入类型非法的文件描述符参数,最终触发Node.js底层TCPWrap的整数类型断言失败,抛出看到的崩溃错误。
  • 代码内置在微服务项目中时,所有代码引用的都是项目根目录node_modules下的同一套依赖,ConfigModule为单例,配置可正常读取,因此不会触发该问题。

解决方案
  1. 修改共享日志NPM包的package.json,把所有Nest生态、pino相关的框架级依赖全部移到peerDependencies中,强制使用宿主微服务的依赖实例,避免重复安装:
{
  "name": "@my-company/my-logger",
  "version": "1.0.0",
  "peerDependencies": {
    "@nestjs/common": "^9.0.0 || ^10.0.0",
    "@nestjs/config": "^2.0.0 || ^3.0.0",
    "nestjs-pino": "^3.0.0",
    "pino-http": "^8.0.0",
    "pino-pretty": "^9.0.0 || ^10.0.0"
  },
  "dependencies": {}
}
  1. 清理缓存后重新安装依赖:删除共享包、所有微服务项目的node_modules目录和lock文件(package-lock.json/yarn.lock/pnpm-lock.yaml),重新执行依赖安装命令,确保整个项目依赖树中只存在一份Nest相关依赖实例。
  2. 增加配置兜底逻辑,避免配置读取失败时传入非法值:
useFactory: async (configService: ConfigService) => ({
  pinoHttp: {
    level: process.env.LOG_LEVEL || 'info',
    redact: configService.get<string[]>('logger.redacted.fields') || ['req.headers.authorization', 'req.headers.cookie'],
    // 其余原有配置保持不变
  }
})
  1. (可选优化)将MyLoggerModule标记为全局模块,避免后续微服务引入时重复注册:
import { Global, Module } from '@nestjs/common';
// 其余import保持不变

@Global()
@Module({
  // 原有模块配置保持不变
  exports: [LoggerModule]
})
export class MyLoggerModule {}

内容的提问来源于stack exchange,提问作者mh377

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 22:45:37