{T}

NestJS第三方日志模块Pino学习笔记

NestJS第三方日志模块Pino学习笔记

核心知识点

1. NestJS 第三方日志库推荐

NestJS 内置 Logger 仅支持 Console 输出,生产环境需要更强大的第三方日志库:

日志库说明特点
Pino本节重点性能最高,NestJS 官方推荐
Winston另一主流选择功能全面,社区活跃,传输通道丰富

Pino 以极高的性能著称,是 Node.js 生态中速度最快的日志库之一。


2. Pino 核心包介绍

2.1 包概览

bash
pnpm install pino-http pino-pretty pino-roll
包名作用使用阶段
pino-httpHTTP 请求日志中间件,记录请求/响应信息全部阶段
pino-pretty日志格式化,彩色输出,便于开发查看开发阶段
pino-roll日志文件滚动(按时间/大小自动分割)生产阶段

2.2 三包关系图

code
pino-http(核心中间件)
    │
    ├── 开发环境 → pino-pretty(美化输出到控制台)
    │
    └── 生产环境 → pino-roll(写入日志文件,自动滚动)

3. 开发环境:pino-pretty 美化日志

3.1 基础配置

typescript
// app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { LoggerModule } from 'nestjs-pino';
import pino from 'pino';
import { join } from 'path';

@Module({
  imports: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(
      pino-http({
        transport: {
          target: 'pino-pretty',
          options: {
            colorize: true,  // 彩色输出
          },
        },
      }),
    ).forRoutes('*');
  }
}

3.2 pino-pretty 输出效果

使用 pino-pretty 后,终端输出从压缩的单行 JSON 变为格式化的彩色日志

code
//  不使用 pino-pretty(丑陋的压缩 JSON)
{"level":30,"time":1711756800000,"req":{"method":"GET","url":"/api/v1/user"},"res":{"statusCode":200},"responseTime":12}

//  使用 pino-pretty(美观的格式化输出)
[1711756800000] INFO  (user): GET /api/v1/user
    req: { method: "GET", url: "/api/v1/user", cookies: "..." }
    res: { statusCode: 200 }
    responseTime: 12ms

3.3 根据环境切换输出方式

生产环境不使用 pino-pretty,因为它会降低性能。仅在开发环境使用美化输出。

typescript
// 根据环境动态决定 transport 目标
const transportTarget =
  process.env.NODE_ENV === 'development'
    ? 'pino-pretty'           // 开发:美化输出
    : 'pino-roll';            // 生产:文件滚动记录

4. 生产环境:pino-roll 文件滚动

4.1 pino-roll 简介

  • 功能:自动按规则将日志写入文件,支持按时间周期文件大小滚动分割
  • 官网描述"A pino transport that automatically rolls your log file"
  • 使用场景:生产环境日志持久化

4.2 安装

bash
pnpm install pino-roll

4.3 滚动方式:按时间周期(frequency)

typescript
pino-http({
  transport: {
    target: 'pino-roll',
    options: {
      file: join(__dirname, '..', 'logs', 'log.txt'),
      frequency: 'daily',   // 每天滚动一个文件
      mkdir: true,           // 自动创建 logs 目录
    },
  },
})

frequency 可选值

说明生成文件示例
'daily'每天一个文件log.txt.2026-03-30
'hourly'每小时一个文件log.txt.2026-03-30T08

4.4 滚动方式:按文件大小(size)

typescript
pino-http({
  transport: {
    target: 'pino-roll',
    options: {
      file: join(__dirname, '..', 'logs', 'log.txt'),
      size: '10M',    // 每个文件最大 10MB
      mkdir: true,
    },
  },
})

size 格式:以 KM 结尾,如 '0.1K'(测试用)、'10M'(生产推荐)。

4.5 两种滚动方式对比

方式参数适用场景优点缺点
按时间frequency: 'daily'日志量稳定按天查找方便文件大小不固定
按大小size: '10M'日志量波动大文件大小可控,便于打开查找需跨文件

推荐:生产环境通常设置为 10M 一个文件,10MB 的文本文件可以正常打开,便于排查问题。


5. targets 多目标输出

5.1 同时输出到控制台和文件

transport.targets 支持数组形式,同时配置多个输出目标:

typescript
pino-http({
  transport: {
    targets: [
      // 目标一:控制台美化输出(开发环境)
      {
        target: 'pino-pretty',
        options: {
          colorize: true,
        },
        level: 'info',
      },
      // 目标二:文件滚动记录
      {
        target: 'pino-roll',
        options: {
          file: join(__dirname, '..', 'logs', 'log.txt'),
          frequency: 'daily',
          mkdir: true,
        },
        level: 'info',
      },
    ],
  },
})

每个目标可以单独设置 level,实现不同目标输出不同等级的日志。


6. 全局注册中间件

6.1 放置位置选择

位置效果推荐
在某个 Module 的 configure()仅该 Module 生效不推荐
AppModuleconfigure()全局所有路由生效推荐

6.2 全局注册示例

typescript
// app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { join } from 'path';
import pinoHttp from 'pino-http';

@Module({
  imports: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    // 全局启用 pino-http 日志中间件
    consumer.apply(
      pinoHttp({
        transport: {
          targets: [
            {
              target: 'pino-pretty',
              options: { colorize: true },
              level: 'info',
            },
            {
              target: 'pino-roll',
              options: {
                file: join(__dirname, '..', 'logs', 'log.txt'),
                frequency: 'daily',
                mkdir: true,
              },
              level: 'info',
            },
          ],
        },
      }),
    ).forRoutes('*');  // 匹配所有路由
  }
}

代码实战案例

需求描述

在 NestJS 项目中集成 Pino 日志,开发环境使用 pino-pretty 美化输出,生产环境使用 pino-roll 文件滚动记录,全局生效。

完整实现

第一步:安装依赖

bash
pnpm install pino-http pino-pretty pino-roll

第二步:配置 AppModule

typescript
// app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { join } from 'path';
import pinoHttp from 'pino-http';
import { UserModule } from './user/user.module';

@Module({
  imports: [UserModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(
      pinoHttp({
        transport: {
          targets: [
            // 目标一:控制台美化输出
            {
              target: 'pino-pretty',
              options: {
                colorize: true,
              },
              level: 'info',
            },
            // 目标二:日志文件滚动记录
            {
              target: 'pino-roll',
              options: {
                file: join(__dirname, '..', 'logs', 'log.txt'),
                frequency: 'daily',
                mkdir: true,
              },
              level: 'info',
            },
          ],
        },
      }),
    ).forRoutes('*');
  }
}

第三步:启动并测试

bash
pnpm start:dev

第四步:发送请求,查看日志

bash
# GET 请求
curl http://localhost:3000/api/v1/user

# POST 请求
curl -X POST http://localhost:3000/api/v1/user -H "Content-Type: application/json" -d '{"name":"test"}'

控制台输出(pino-pretty 美化):

code
[1711756800000] INFO  GET /api/v1/user
    req: { method: "GET", url: "/api/v1/user" }
    res: { statusCode: 200 }
    responseTime: 12ms

[1711756801000] INFO  POST /api/v1/user
    req: { method: "POST", url: "/api/v1/user", body: { name: "test" } }
    res: { statusCode: 201 }
    responseTime: 25ms

日志文件(logs/log.txt,原始 JSON 格式):

code
{"level":30,"time":1711756800000,"req":{"method":"GET","url":"/api/v1/user"},"res":{"statusCode":200},"responseTime":12}
{"level":30,"time":1711756801000,"req":{"method":"POST","url":"/api/v1/user"},"res":{"statusCode":201},"responseTime":25}

文件中记录的是原始 JSON,虽然没有美化,但便于后续日志分析工具解析和回溯。


常见问题与解决方案

问题原因解决方案
生产环境日志性能下降使用了 pino-pretty生产环境使用 pino-roll,仅开发环境使用 pino-pretty
logs 目录不存在报错未设置 mkdir: truepino-roll options 中添加 mkdir: true
文件滚动未生效frequencysize 未设置设置 frequency: 'daily'size: '10M'
日志文件太大无法打开文件大小设置不合理设置 size: '10M',10MB 文件可正常打开
日志只在某个 Module 生效中间件注册在子 Module 中将中间件注册到 AppModuleconfigure()

学习要点总结

  1. Pino 三件套pino-http(核心中间件)+ pino-pretty(开发美化)+ pino-roll(生产文件滚动)
  2. 环境区分:开发环境用 pino-pretty 美化输出,生产环境用 pino-roll 文件记录
  3. 滚动方式frequency: 'daily'(按天)或 size: '10M'(按大小),推荐 10MB 一文件
  4. targets 多目标:可同时输出到控制台和文件,每个目标可设置不同日志等级
  5. 全局注册:在 AppModule.configure() 中使用 forRoutes('*') 全局生效

延伸学习资源

官方文档

后续课程预告

  • Winston 日志库:另一主流日志库,支持更多传输通道
  • ELK Stack 集成:日志收集 + 存储 + 可视化分析
  • 日志告警:基于错误日志自动触发告警通知

生产环境日志最佳实践

code
推荐配置方案:
├── 控制台:pino-pretty(仅开发环境)
├── 文件记录:pino-roll + daily(生产环境)
│   └── logs/log.txt.2026-03-30
├── 文件大小:10MB 滚动
├── 日志格式:JSON(便于日志分析工具解析)
└── 日志级别:info 及以上(error、warn、log)