告别笨重的 ELK,拥抱轻量级 PLG:NestJS 日志监控实战指南

告别笨重的 ELK,拥抱轻量级 PLG:NestJS 日志监控实战指南

💡 前言 : 在微服务和容器化大行其道的今天,日志监控是必不可少的一环。传统的 ELK (Elasticsearch, Logstash, Kibana) 方案虽然强大,但对于中小型项目来说,资源占用过高(尤其是 ES)。 本文将带你实战一套轻量级的日志监控方案:Promtail + Loki + Grafana (PLG) ,并结合 NestJS 实现结构化日志的采集与可视化。


🚀 为什么选择 PLG?

  • Loki: 被称为 "云原生的 Prometheus",它不索引日志全文,只索引 Label,因此资源占用极低,写入速度极快。
  • Promtail: 专门为 Loki 设计的日志采集器,配置简单,支持多种抓取方式。
  • Grafana: 颜值即正义,统一的监控大屏,不仅能看指标(Metrics),还能看日志(Logs)。

目标效果 : 通过 Nginx 反向代理,访问 https://your-domain.com/log/ 直接查看实时日志仪表盘,且日志包含完整的 ReqRes 结构化数据。


🛠️ 第一步:NestJS 结构化日志改造

要想日志查得爽,日志格式必须标准。我们使用 winston 来生成 JSON 格式的日志。

1. 安装依赖

bash 复制代码
pnpm add nest-winston winston

2. 配置 Winston (app.module.ts)

重点是使用 winston.format.json(),这样 Promtail 才能轻松解析。

typescript 复制代码
// src/app.module.ts
WinstonModule.forRoot({
  transports: [
    new winston.transports.File({
      filename: 'logs/combined.log',
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json(), // 👈 关键点
      ),
    }),
  ],
})

3. 全局日志中间件

手写一个 Middleware,把请求参数、响应体、耗时都记录下来。

typescript 复制代码
// src/common/middleware/logging.middleware.ts
// ...省略部分代码...
this.logger.info(
  `${method} ${originalUrl} ${statusCode} ...`, 
  {
    req: { body: req.body, query: req.query }, // 👈 结构化数据
    res: { body: parsedResponseBody },
    context: 'HTTP',
  }
);

🐳 第二步:Docker Compose 编排 PLG

docker-compose.prod.yml 中一键拉起所有服务。

yaml 复制代码
services:
  # 你的应用服务
  app:
    # ...
    volumes:
      - app_logs:/app/logs  # 👈 挂载卷,把日志暴露出来

  # 日志聚合
  loki:
    image: grafana/loki:2.9.2
    command: -config.file=/etc/loki/local-config.yaml

  # 日志采集
  promtail:
    image: grafana/promtail:2.9.2
    volumes:
      - ./plg/promtail/config.yaml:/etc/promtail/config.yaml
      - app_logs:/var/log/app # 👈 读取同一个卷
    depends_on:
      - loki

  # 可视化
  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_SERVER_ROOT_URL=https://your-domain.com/log/ # 👈 子路径部署关键配置
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
    volumes:
      - ./plg/grafana/provisioning:/etc/grafana/provisioning # 👈 自动化配置

⚙️ 第三步:Promtail 采集配置

Promtail 的核心在于 pipeline_stages,它决定了如何解析你的日志。

yaml 复制代码
# plg/promtail/config.yaml
scrape_configs:
  - job_name: nest_logs
    static_configs:
      - targets: ['localhost']
        labels:
          app: nest-prisma-app
          __path__: /var/log/app/*.log
    pipeline_stages:
      - json: # 👈 解析 JSON,把 req, res 提取出来
          expressions:
            level: level
            req: req
            res: res
      - labels: # 👈 把 level 变成标签,方便筛选错误日志
          level:

🔌 第四步:Nginx 反向代理与 Grafana 子路径

为了安全和方便,我们通常把 Grafana 藏在 Nginx 后面,通过 /log/ 访问。

Nginx 配置:

nginx 复制代码
location /log/ {
    proxy_pass http://localhost:3001; # 👈 转发到 Grafana 端口
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

踩坑指南:

  1. Grafana 白屏/404 : 必须配置 GF_SERVER_SERVE_FROM_SUB_PATH=true
  2. Proxy Pass 斜杠 : Nginx 中 proxy_pass http://localhost:3001; 后面尽量别带 /,让 Grafana 自己处理路径前缀。

✨ 第五步:自动化 Dashboard (Provisioning)

不要每次部署都手动配数据源!使用 Grafana Provisioning 功能。

plg/grafana/provisioning/datasources/datasource.yml 中定义 Loki 数据源,容器启动时自动加载。这样即使容器销毁,配置也不会丢。


🎉 最终效果

部署完成后,打开 https://your-domain.com/log/

  1. 实时流 : Live 模式下看着日志一条条刷出来,极度舒适。
  2. 精准检索 : 输入 {app="nest-prisma-app", level="error"} 瞬间定位所有错误。
  3. 上下文详情 : 点击日志展开,直接看到 req.bodyres.body,再也不用猜用户传了什么参数!

总结 这套 PLG 方案占用资源极少(几十 MB 内存),但功能却非常强大。配合 NestJS 的结构化日志,是中小型项目监控的不二之选。

相关推荐
ovensi1 天前
Docker+NestJS+ELK:从零搭建全链路日志监控系统
后端·nestjs
Gogo8163 天前
nestjs 的项目启动
nestjs
没头发的卓卓19 天前
新手入门:nest基本使用规则(适合零基础小白)
nestjs
孟祥_成都20 天前
深入 Nestjs 底层概念(1):依赖注入和面向切面编程 AOP
前端·node.js·nestjs
濮水大叔1 个月前
VonaJS: 基于winston的Logger日志系统
typescript·nodejs·nestjs
濮水大叔1 个月前
VonaJS: 序列化/数据脱敏(上)
typescript·node.js·nestjs
濮水大叔1 个月前
VonaJS: 序列化/数据脱敏(下)
typescript·nodejs·nestjs