Pino.js解决同时启用transport和stream的问题

  • 今天在处理项目中一个 bug 时,发现 Pino.js​​​​ 配置的 transport​​​​ 和 stream​​​​,只有 transport​​​​ 生效了,配置选项如下:

    /** pinoHttp的配置选项 */
    const devOptions: Options = {
      /** 对每个请求/响应的自动日志设置上下文的值 */
      customProps: (_req, _res) => ({ context: 'HTTP' }),
      transport: {
        target: 'pino-pretty',
        options: {
          translateTime: 'HH:MM:ss Z',
        },
      },
      stream: pino.destination(LOG_PATH + LOG_NAME),
    };
    
  • 本意是要开发环境中,使用 pino-pretty 美化日志输出,方便定位问题,同时启用 stream 将日志写入到日志文件中。 但是现在,pino-pretty 是正常输出的 ,而日志文件中没有日志记录。

  • 先问了问 ChatGPT,说两者冲突无法共存,又重复询问了几次,答案就开始冲突,陷入死循环了。

  • 尝试喂了官方文档校正一下,逻辑还是前后矛盾……得了,还是自己来吧。

  • 翻了翻官方文档,在 Transports 这个章节中,有提及

    A transport is a module that exports a default function that returns a writable stream. 传输器是一个导出默认方法的模块,该方法返回一个可写流。

    返回的是一个可写流……这样的话,将 transport 看作一个普通的流,直接使用 pino.multistream​​​​​​​ 方法配置不就行了嘛。

  • 修改如下:

    /** pinoHttp的配置选项 */
    const devOptions: Options = {
      /** 对每个请求/响应的自动日志设置上下文的值 */
      customProps: (_req, _res) => ({ context: 'HTTP' }),
      stream: pino.multistream([
        pino.transport({ target: 'pino-pretty' }),
        pino.destination(LOG_PATH + LOG_NAME),
      ]),
    };
    
  • 再测试一下,控制台输出的是经过 pino-pretty​​​ 美化后的日志,文件中存储的是原始的 JSON 日志,符合预期的效果。

  • 很好,收工……了么?

  • 然而没那么顺利,为了避免在生产服务器上留下 node_modules​​​ ​这么一大坨依赖,我是使用 ncc​​​​ 将项目打包之后再进行部署的。

  • 按照上面的方式修改日志配置之后,项目运行后会提示 Error: Cannot find module 'real-require'。​​​​

  • 经过一番定位之后,感觉问题应该出自 pino.transport({ target: 'pino-pretty' })​​​ ​这里,查看 pino.js 源码后,大致判断是因为这种使用方式使用了 ncc​​​ ​不支持的动态导入。

  • 最简单的解决方式就是回退一下方案,毕竟现有的开发环境中其实没太大的必要保留日志文件……现在的 Options 之所以这么写,是因为这是从我的另一个爬虫项目复制过来的 = = ||。 爬虫嘛,除了测试过程中看看控制台输出,后续分析的时候主要还是靠翻找日志的。 但是回退会让我感觉这样很不爽

  • 继续折腾,机缘巧合之下,发现 pino-pretty​​默认导出方法的类型定义是:

    declare function PinoPretty(options?: PrettyOptions_): PinoPretty.PrettyStream;​​

  • 啧,也是一个流,所以上面的选项我又改成了这样:

    /** pinoHttp的配置选项 */
    const devOptions: Options = {
      /** 对每个请求/响应的自动日志设置上下文的值 */
      customProps: (_req, _res) => ({ context: 'HTTP' }),
      // 输出到控制台,或者设置成如下,同时输出到控制台和文件中:
      stream: pino.multistream([
        pinoPretty({ translateTime: 'HH:MM:ss Z' }),
        pino.destination({
          dest: LOG_PATH + LOG_NAME,
          minLength: 4096,
          sync: false,
        }),
      ]),
    };
    

    ncc 再进行打包,测试后发现正常了。