告别笨重的 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 的结构化日志,是中小型项目监控的不二之选。

相关推荐
XiaoYu20024 天前
第3章 Nest.js拦截器
前端·ai编程·nestjs
XiaoYu20025 天前
第2章 Nest.js入门
前端·ai编程·nestjs
实习生小黄5 天前
NestJS 调试方案
后端·nestjs
当时只道寻常9 天前
NestJS 如何配置环境变量
nestjs
濮水大叔20 天前
VonaJS是如何做到文件级别精确HMR(热更新)的?
typescript·node.js·nestjs
ovensi21 天前
Docker+NestJS+ELK:从零搭建全链路日志监控系统
后端·nestjs
Gogo81623 天前
nestjs 的项目启动
nestjs
没头发的卓卓1 个月前
新手入门:nest基本使用规则(适合零基础小白)
nestjs