基于NestJS+Prometheus+Grafana实现多系统前端性能监控(LCP/CLS等指标全覆盖)

0 阅读10分钟

在前端开发中,性能体验是影响用户留存的核心因素,而Web Vitals(LCP、FCP、CLS等)作为前端性能的核心衡量标准,需要一套稳定、可扩展的监控方案来实时追踪。本文将分享如何基于NestJS、Prometheus、Grafana搭建一套支持多系统上报、数据持久化的前端性能监控平台,全程可落地、零付费,适合企业级项目或多端项目复用。

先放一张最终监控大盘效果预览(Grafana可视化):可实时查看多系统、多页面的LCP、FCP、CLS等指标趋势,支持筛选、分位统计和告警,完全满足前端性能优化和监控需求。

一、方案选型与整体架构

为什么选择这一套组合?核心优势在于「企业级可扩展性+零成本+全开源」,尤其适合多系统场景:

  • NestJS:替代原生Node.js,提供企业级后端架构,支持多模块、中间件、鉴权,方便后续扩展(如批量上报、数据清洗),同时TypeScript加持,类型安全更易维护。
  • web-vitals:Google官方出品,轻量无侵入,精准采集LCP(最大内容绘制)、FCP(首次内容绘制)、CLS(布局偏移)、INP(交互延迟)、TTFB(首字节时间)等核心指标。
  • PushGateway:作为数据中转站,接收NestJS转发的前端指标,解决前端浏览器无法直接向Prometheus上报数据的问题(Prometheus默认采用拉取模式)。
  • Prometheus:开源时序数据库,专门存储时间序列数据(如每一次前端性能上报),支持持久化、多维度查询,是监控系统的核心数据存储组件。
  • Grafana:开源可视化平台,可快速对接Prometheus,生成美观的监控大盘,支持筛选、分位统计、告警配置,无需自己手写图表。

整体架构流程图

前端(多系统) → web-vitals采集指标 → NestJS接口接收 → PushGateway缓存 → Prometheus拉取并持久化 → Grafana可视化监控

二、环境准备(Docker一键部署后端三件套)

首先部署Prometheus、PushGateway、Grafana,推荐使用Docker Compose,一键启动,无需手动配置依赖,适合快速落地。

1. 创建Docker Compose配置文件

在本地新建目录(如frontend-monitor),创建docker-compose.yml文件,内容如下(重点配置Prometheus数据持久化,避免重启丢失数据):

version: '3'
services:
  # Prometheus 时序数据库(核心存储)
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - ./prometheus_data:/prometheus  # 数据持久化挂载,重启不丢失
    restart: always

  # PushGateway 数据中转站
  pushgateway:
    image: prom/pushgateway
    ports:
      - "9091:9091"
    restart: always

  # Grafana 可视化平台
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana  # Grafana配置持久化
    restart: always

volumes:
  prometheus_data:  # Prometheus数据卷
  grafana_data:     # Grafana数据卷

2. 配置Prometheus拉取规则

在同一目录下创建prometheus.yml文件,配置Prometheus定时从PushGateway拉取数据,同时设置数据保存时间(默认15天,这里改为90天,满足长期监控需求):

global:
  scrape_interval: 5s  # 每5秒拉取一次数据
  evaluation_interval: 15s  # 每15秒评估一次告警规则

# 数据存储配置(保存90天)
storage:
  tsdb:
    retention: 90d

# 拉取配置:从PushGateway拉取前端性能指标
scrape_configs:
  - job_name: 'frontend-metrics'  # 任务名称,自定义
    static_configs:
      - targets: ['pushgateway:9091']  # PushGateway服务地址(Docker内部服务名)

3. 启动后端三件套

在终端进入当前目录,执行以下命令,一键启动Prometheus、PushGateway、Grafana:

docker-compose up -d

启动成功后,可访问以下地址验证:

三、NestJS服务开发(接收前端上报,转发至PushGateway)

NestJS作为后端服务,核心功能是接收前端上报的性能指标,进行简单参数校验后,转发至PushGateway,同时支持多系统区分(如PC端、H5端、管理系统)。

1. 创建NestJS项目并安装依赖

# 安装Nest CLI(若未安装)
npm install -g @nestjs/cli

# 创建项目
nest new frontend-metrics-service
cd frontend-metrics-service

# 安装依赖(axios用于请求PushGateway,@nestjs/axios提供HTTP模块)
npm install axios @nestjs/axios

2. 开发核心模块与控制器

NestJS采用模块化架构,我们创建一个metrics模块,专门处理前端性能指标的上报逻辑。

(1)创建Metrics控制器(metrics.controller.ts)

路径:src/metrics/metrics.controller.ts,核心功能:接收前端POST请求,校验参数,转发指标到PushGateway。

import { Controller, Post, Body, HttpException, HttpStatus } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { catchError } from 'rxjs/operators';

@Controller('metrics')
export class MetricsController {
  // 注入HttpService,用于请求PushGateway
  constructor(private readonly httpService: HttpService) {}

  /**
   * 前端性能指标上报接口
   * @param body 上报参数:system(系统标识)、page(页面路径)、name(指标名称)、value(指标值)
   */
  @Post('report')
  async reportMetrics(@Body() body: any) {
    try {
      // 1. 参数校验(必传参数:system、name、value)
      const { system, page, name, value } = body;
      if (!system || !name || value === undefined) {
        throw new HttpException(
          '参数错误:system、name、value为必传项',
          HttpStatus.BAD_REQUEST,
        );
      }

      // 2. 组装Prometheus格式的指标(时序数据格式)
      // 格式说明:frontend_${name}为指标名,{system, page}为标签(用于多系统、多页面区分)
      const metricStr = `
# TYPE frontend_${name} gauge
frontend_${name}{system="${system}",page="${page}"} ${value}
`;

      // 3. 转发到PushGateway(Docker内部服务地址:http://pushgateway:9091)
      await this.httpService.axiosRef.put(
        'http://pushgateway:9091/metrics/job/frontend-metrics',
        metricStr,
        { headers: { 'Content-Type': 'text/plain' } },
      );

      // 4. 返回成功响应
      return {
        code: 200,
        message: '指标上报成功',
        data: { system, page, name, value },
      };
    } catch (error) {
      // 异常处理
      console.error('指标上报失败:', error.message);
      throw new HttpException(
        '指标上报失败,请稍后重试',
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }
}

(2)创建Metrics模块(metrics.module.ts)

路径:src/metrics/metrics.module.ts,导入HttpModule,注册控制器。

import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { MetricsController } from './metrics.controller';

@Module({
  // 导入HttpModule,用于发送HTTP请求到PushGateway
  imports: [HttpModule],
  // 注册控制器,暴露接口
  controllers: [MetricsController],
})
export class MetricsModule {}

(3)修改主模块(app.module.ts)

路径:src/app.module.ts,导入MetricsModule,使模块生效。

import { Module } from '@nestjs/common';
import { MetricsModule } from './metrics/metrics.module';

@Module({
  imports: [MetricsModule],
})
export class AppModule {}

(4)修改主入口(main.ts)

路径:src/main.ts,启动服务,开启跨域(解决前端跨域问题)。

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 开启跨域,允许前端项目上报指标(生产环境可配置具体域名,更安全)
  app.enableCors();
  // 启动端口(与前端上报地址一致)
  await app.listen(4000);
  console.log(`NestJS服务已启动:http://localhost:4000`);
}

3. 启动NestJS服务

# 开发环境启动(热更新)
npm run start:dev

启动成功后,上报接口可正常访问:POST http://localhost:4000/metrics/report

四、前端改造(多系统指标采集与上报)

前端只需引入web-vitals库,采集核心指标,带上系统标识(如pc-web、h5-shop、admin-system),调用NestJS接口上报即可,支持多系统、多页面同时上报,互不干扰。

1. 前端上报代码(通用版,适配所有前端项目)

在前端项目的入口文件(如index.html、main.js)中引入以下代码,无需修改太多,只需修改SYSTEM_NAME区分不同系统。

<!-- 引入web-vitals库(CDN方式,无需npm安装) -->
<script src="https://unpkg.com/web-vitals"></script>
<script>
  // 关键:设置当前系统标识,不同系统修改此处即可
  // 示例:pc-web(PC主站)、h5-shop(H5商城)、admin-system(管理系统)
  const SYSTEM_NAME = "pc-web";

  /**
   * 上报指标到NestJS接口
   * @param metric web-vitals采集到的指标对象
   */
  function sendPerformanceMetric(metric) {
    // 过滤无效指标(避免异常上报)
    if (!metric || !metric.name || metric.value === undefined) return;

    // 调用NestJS上报接口
    fetch("http://localhost:4000/metrics/report", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        system: SYSTEM_NAME, // 系统标识
        page: location.pathname, // 当前页面路径(用于区分不同页面的性能)
        name: metric.name, // 指标名称(lcp、fcp、cls等)
        value: metric.value, // 指标值(单位:ms,cls为无单位)
      }),
    })
    .then(res => res.json())
    .then(data => {
      console.log("指标上报成功:", data);
    })
    .catch(err => {
      console.error("指标上报失败:", err);
    });
  }

  // 注册需要采集的指标(全覆盖Web Vitals核心指标)
  webVitals.onLCP(sendPerformanceMetric);  // 最大内容绘制
  webVitals.onFCP(sendPerformanceMetric);  // 首次内容绘制
  webVitals.onCLS(sendPerformanceMetric);  // 累积布局偏移
  webVitals.onINP(sendPerformanceMetric);  // 交互到下一次绘制
  webVitals.onTTFB(sendPerformanceMetric); // 首字节时间
</script>

2. 多系统适配说明

不同系统只需修改SYSTEM_NAME即可,例如:

  • PC主站:const SYSTEM_NAME = "pc-web";
  • H5商城:const SYSTEM_NAME = "h5-shop";
  • 管理系统:const SYSTEM_NAME = "admin-system";
  • APP内嵌H5:const SYSTEM_NAME = "app-webview";

上报后,Prometheus会自动通过system标签区分不同系统的指标,后续在Grafana中可通过筛选器切换查看。

五、Grafana配置(可视化监控大盘)

Grafana本身不存储数据,而是通过对接Prometheus,将时序数据以图表形式展示,这里我们配置多系统可筛选的监控大盘,一步到位。

1. 配置Prometheus数据源

  1. 打开Grafana:http://localhost:3000,登录后(默认admin/admin,首次登录修改密码)。
  2. 点击左侧「Data Sources」→「Add data source」,选择「Prometheus」。
  3. 在配置页面,「URL」填写:http://prometheus:9091(Docker内部服务地址,与docker-compose中一致)。
  4. 点击「Save & Test」,提示「Data source is working」即配置成功。

2. 创建监控大盘

我们创建一个大盘,包含多系统筛选、核心指标趋势图、分位统计,步骤如下:

  1. 点击左侧「Dashboards」→「New dashboard」→「Add visualization」。

  2. 选择已配置的Prometheus数据源,进入指标配置页面。

  3. 配置核心指标(以LCP为例):

    1. 「Query」填写:frontend_lcp(Prometheus中存储的LCP指标名)。
    2. 「Legend」填写:{{system}} - {{page}}(显示系统+页面,方便区分)。
    3. 图表类型选择「Line chart」(折线图,查看趋势)。
  4. 重复步骤3,依次添加FCP(frontend_fcp)、CLS(frontend_cls)、INP(frontend_inp)、TTFB(frontend_ttfb)指标。

  5. 添加系统筛选器(关键,支持多系统切换):

    1. 点击大盘顶部「Dashboard settings」→「Variables」→「Add variable」。
    2. 「Name」填写:system,「Type」选择「Query」,「Data source」选择Prometheus。
    3. 「Query」填写:label_values(frontend_lcp, system)(获取所有系统标识)。
    4. 勾选「Multi-value」和「Include All option」,点击「Save」。
  6. 调整图表布局、标题,点击「Save dashboard」,命名为「前端性能监控大盘」。

3. 大盘优化(可选)

为了让监控更实用,可添加以下优化:

  • 分位统计:添加P50、P75、P95分位指标(如:quantile(0.95, frontend_lcp),查看95%用户的LCP表现)。
  • 告警配置:设置指标阈值(如LCP>2500ms告警),支持邮件、钉钉、企业微信告警。
  • 页面筛选:添加page变量,支持筛选具体页面的性能指标。

六、关键问题说明(避坑指南)

1. 数据存在哪里?会不会丢失?

  • 前端上报的指标,先临时存在PushGateway(重启丢失)。
  • Prometheus每5秒从PushGateway拉取数据,持久化到本地磁盘(我们配置了./prometheus_data挂载,重启Docker不丢失)。
  • 数据默认保存90天,可在prometheus.yml中修改retention参数调整保存时间。

2. 多系统上报会不会冲突?

不会。我们通过system标签区分不同系统,page标签区分不同页面,Prometheus会按标签分类存储指标,Grafana可通过筛选器切换查看,互不干扰。

3. 生产环境注意事项

  • 跨域:生产环境中,NestJS的enableCors()需配置具体前端域名,避免任意域名上报。
  • 鉴权:可给NestJS接口添加API密钥,防止恶意上报。
  • 性能:PushGateway和Prometheus可根据上报量调整配置,支持横向扩展。
  • 日志:NestJS可添加日志模块,记录上报异常,方便排查问题。

七、总结

本文实现的基于NestJS+Prometheus+Grafana的前端性能监控方案,具备以下优势:

  • 零付费:全开源组件,无需购买任何服务。
  • 多系统支持:通过system标签轻松区分多端、多项目,扩展性强。
  • 企业级架构:NestJS替代原生Node.js,易维护、易扩展,支持后续功能迭代。
  • 数据持久化:Prometheus挂载数据卷,重启不丢失,支持长期监控。
  • 可视化友好:Grafana大盘可自定义,支持筛选、告警、分位统计,直观查看性能趋势。

这套方案可直接应用于生产环境,无论是个人项目、创业项目还是企业级项目,都能满足前端性能监控的需求。后续可根据实际需求,扩展批量上报、数据清洗、异常分析等功能。

如果需要完整的Grafana大盘JSON(导入即用)、NestJS生产环境配置,可留言获取~