在前端开发中,性能体验是影响用户留存的核心因素,而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
启动成功后,可访问以下地址验证:
- Prometheus:http://localhost:9090(可查看拉取的指标)
- PushGateway:http://localhost:9091(可查看临时缓存的指标)
- Grafana:http://localhost:3000(初始账号密码:admin/admin,首次登录需修改密码)
三、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数据源
- 打开Grafana:http://localhost:3000,登录后(默认admin/admin,首次登录修改密码)。
- 点击左侧「Data Sources」→「Add data source」,选择「Prometheus」。
- 在配置页面,「URL」填写:http://prometheus:9091(Docker内部服务地址,与docker-compose中一致)。
- 点击「Save & Test」,提示「Data source is working」即配置成功。
2. 创建监控大盘
我们创建一个大盘,包含多系统筛选、核心指标趋势图、分位统计,步骤如下:
-
点击左侧「Dashboards」→「New dashboard」→「Add visualization」。
-
选择已配置的Prometheus数据源,进入指标配置页面。
-
配置核心指标(以LCP为例):
- 「Query」填写:frontend_lcp(Prometheus中存储的LCP指标名)。
- 「Legend」填写:{{system}} - {{page}}(显示系统+页面,方便区分)。
- 图表类型选择「Line chart」(折线图,查看趋势)。
-
重复步骤3,依次添加FCP(frontend_fcp)、CLS(frontend_cls)、INP(frontend_inp)、TTFB(frontend_ttfb)指标。
-
添加系统筛选器(关键,支持多系统切换):
- 点击大盘顶部「Dashboard settings」→「Variables」→「Add variable」。
- 「Name」填写:system,「Type」选择「Query」,「Data source」选择Prometheus。
- 「Query」填写:label_values(frontend_lcp, system)(获取所有系统标识)。
- 勾选「Multi-value」和「Include All option」,点击「Save」。
-
调整图表布局、标题,点击「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生产环境配置,可留言获取~