单页应用的前端性能与稳定性数据往往分散在浏览器控制台和用户反馈中,开发团队很难在故障初期获得结构化指标。Prometheus 虽然原生面向服务端,但通过工程化设计同样可以采集 Vue 3 应用的关键运行数据。核心思路是前端采集指标、后端聚合暴露、Prometheus 拉取存储、Alertmanager 触发告警。本文从指标设计、插件封装、抓取与告警配置、实践注意事项四个层面展开。

一、前端监控指标设计:明确采集对象与数据模型
前端监控与后端监控的差异在于数据来源是真实用户的浏览器环境,而不是长期运行的进程。因此指标设计要覆盖页面加载、路由切换、接口调用、资源加载和运行时异常这五类核心场景。Prometheus 提供了 Counter、Gauge、Histogram 和 Summary 四种原生指标类型,前端工程中通常使用 Histogram 来统计耗时分布,用 Counter 来累计错误次数,用 Gauge 记录当前活跃会话数。延迟类指标如果只记录平均值会掩盖尾部延迟,使用 Histogram 配合分位数可以更真实地反映用户体验。
下面是一组推荐的前端监控指标,它们既符合 Prometheus 命名规范,也能覆盖大部分 Vue 3 应用的观测需求。
| 指标名称 | 类型 | 标签 | 采集来源 |
|---|---|---|---|
| vue_page_load_duration_seconds | Histogram | route, device_type | PerformanceObserver |
| vue_route_change_duration_seconds | Histogram | from, to | Vue Router 守卫 |
| vue_http_request_duration_seconds | Histogram | method, route, status | Axios 拦截器 |
| vue_http_request_errors_total | Counter | method, route, error_type | Axios 拦截器 |
| vue_resource_load_failures_total | Counter | resource_type | 资源加载错误监听 |
| vue_js_error_total | Counter | error_type, route | 全局错误监听 |
| vue_page_view_total | Counter | route | 路由切换 |
| vue_active_sessions | Gauge | 无 | 页面展示与卸载 |
标签设计是决定 Prometheus 存储压力与查询效率的关键。务必避免把完整 URL、用户 ID、原始 User-Agent 这类高基数值放入标签。路由标签应使用规范化后的路由名称,例如 /user/:id 而不是 /user/1024。接口标签同理,只保留方法、路由模式和状态码,不要带查询参数。这样既能控制时间序列数量,又能让聚合查询具有业务意义。
下面是一段基于 PerformanceObserver 的采集示例,它在浏览器空闲时记录核心 Web Vitals 数据,并通过自定义事件交给监控插件处理。
// 使用 PerformanceObserver 采集核心 Web Vitals
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
window.dispatchEvent(new CustomEvent('monitor:metric', {
detail: {
name: 'vue_page_load_duration_seconds',
value: entry.startTime / 1000
}
}));
}
if (entry.entryType === 'first-input') {
window.dispatchEvent(new CustomEvent('monitor:metric', {
detail: {
name: 'vue_page_input_delay_seconds',
value: (entry.processingStart - entry.startTime) / 1000
}
}));
}
}
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
observer.observe({ type: 'first-input', buffered: true });
二、Vue 3 工程化封装:监控插件与指标上报
Vue 3 的插件机制非常适合承载监控逻辑。通过 app.use 注册一个监控插件,可以在应用启动时初始化指标注册表,并利用全局属性向组件暴露轻量级 API。路由守卫是接入路由切换耗时的天然位置:在 beforeEach 记录起始时间,在 afterEach 中计算差值并写入 Histogram。对于 HTTP 请求,Axios 拦截器可以统一捕获请求耗时、状态码和网络错误,避免在每个服务函数中手动埋点。
下面是一个 Vue 3 监控插件的简化实现,它负责采集路由切换耗时、运行时错误和页面浏览计数,并通过 HTTP POST 将指标增量发送到聚合层。这里通过 options.router 显式传入路由实例,避免依赖插件安装顺序。
// monitorPlugin.js
export default {
install(app, options) {
const router = options.router;
const endpoint = options.endpoint || '/api/metrics';
let routeStart = 0;
function sendMetric(name, value, labels) {
const payload = JSON.stringify({ name, value, labels });
if (navigator.sendBeacon) {
navigator.sendBeacon(endpoint, new Blob([payload], { type: 'application/json' }));
} else {
fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: payload
});
}
}
router.beforeEach((to, from) => {
routeStart = performance.now();
});
router.afterEach((to, from) => {
if (routeStart) {
const duration = (performance.now() - routeStart) / 1000;
sendMetric('vue_route_change_duration_seconds', duration, {
from: from.name || 'unknown',
to: to.name || 'unknown'
});
routeStart = 0;
}
sendMetric('vue_page_view_total', 1, {
route: to.name || 'unknown'
});
});
window.addEventListener('error', (event) => {
sendMetric('vue_js_error_total', 1, {
error_type: event.error ? event.error.name : 'unknown',
route: router.currentRoute.value.name || 'unknown'
});
});
}
};
由于浏览器实例生命周期短且无法被 Prometheus 直接抓取,通常需要引入一个聚合层。聚合层接收前端上报的指标增量,在服务端维护 Prometheus 指标注册表,并暴露标准 /metrics 端点。这种方式比 Pushgateway 更适合高频前端指标,因为聚合层可以对重复标签进行聚合,且不需要手动清理过期数据。下面使用 Express 和 prom-client 实现一个极简聚合层。
// metricsServer.js
const express = require('express');
const promClient = require('prom-client');
const app = express();
app.use(express.json());
const registry = new promClient.Registry();
promClient.collectDefaultMetrics({ register: registry });
const httpDuration = new promClient.Histogram({
name: 'vue_http_request_duration_seconds',
help: 'Duration of Vue HTTP requests',
labelNames: ['method', 'route', 'status'],
buckets: [0.05, 0.1, 0.3, 0.5, 1, 2, 5, 10],
registers: [registry]
});
const jsErrorTotal = new promClient.Counter({
name: 'vue_js_error_total',
help: 'Total number of Vue JS errors',
labelNames: ['error_type', 'route'],
registers: [registry]
});
const pageViewTotal = new promClient.Counter({
name: 'vue_page_view_total',
help: 'Total page views',
labelNames: ['route'],
registers: [registry]
});
app.post('/api/metrics', (req, res) => {
const { name, value, labels } = req.body || {};
try {
if (name === 'vue_http_request_duration_seconds') {
httpDuration.observe(labels, value);
} else if (name === 'vue_js_error_total') {
jsErrorTotal.inc(labels, value);
} else if (name === 'vue_page_view_total') {
pageViewTotal.inc(labels, value);
}
res.status(204).end();
} catch (err) {
console.error('invalid metric payload', err);
res.status(400).json({ error: 'invalid metric payload' });
}
});
app.get('/metrics', async (req, res) => {
res.set('Content-Type', registry.contentType);
res.end(await registry.metrics());
});
app.listen(3000, () => {
console.log('metrics aggregation server listening on port 3000');
});
三、Prometheus 抓取与 Alertmanager 告警配置
聚合层暴露 /metrics 端点后,需要在 Prometheus 配置文件中添加对应的抓取任务。静态目标适用于聚合层实例不多且地址固定的场景;如果聚合层需要水平扩展,可以结合服务发现机制动态获取目标列表。scrape_interval 表示 Prometheus 多久拉取一次指标,对于前端聚合层,15 秒是一个合理的默认值,既能保证告警及时性,又不会给聚合层带来明显压力。
以下是一份最小化的 Prometheus 抓取配置,它假设聚合层运行在本机 3000 端口。
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'vue-frontend-metrics'
static_configs:
- targets: ['localhost:3000']
metrics_path: '/metrics'
告警规则需要针对前端最重要的失败模式进行设计。错误率告警通常基于 Counter 的速率计算,例如使用 rate(vue_js_error_total[5m]) 判断 5 分钟内的平均每秒错误数。页面加载耗时告警则可以使用 Histogram 的分位数函数 histogram_quantile,它能够捕捉尾部延迟,而不会因为少量极端值误报。下面给出两个典型规则。
# vue_alerts.yml
groups:
- name: vue-frontend
rules:
- alert: VueJSErrorRateHigh
expr: rate(vue_js_error_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: 'Vue JS error rate is high'
description: 'The 5-minute error rate for Vue JS is above 0.05 per second.'
- alert: VuePageLoadSlow
expr: histogram_quantile(0.95, rate(vue_page_load_duration_seconds_bucket[5m])) > 3
for: 5m
labels:
severity: warning
annotations:
summary: 'Vue page load is slow'
description: 'The 95th percentile page load time is above 3 seconds over the last 5 minutes.'
Alertmanager 负责接收 Prometheus 触发的告警并进行分组、去重和通知路由。可以根据错误严重级别将告警发送到不同的接收器,例如关键错误走即时通讯工具,性能告警走电子邮件。合理设置 group_wait 和 group_interval 可以避免同一时间段内的多个告警刷屏。对于前端监控而言,建议为路由切换失败和接口错误配置独立的分组标签,这样团队可以快速定位到具体页面或服务模块。
四、实践注意事项:标签、采样与容量规划
前端高基数标签是 Prometheus 存储的常见杀手。一旦将完整 URL、用户 ID 或未脱敏的 User-Agent 放入标签,时间序列数量可能迅速膨胀。应严格采用路由模式占位符,例如把 /user/1024 规范化为 /user/:id。对于必须区分业务身份的场景,可以把用户 ID 放在日志系统而不是 Prometheus 标签中。同时可以借助 Prometheus 的 relabel_configs 在抓取侧丢弃不合理的标签组合,形成双重防线。
前端流量通常远高于后端,采样是控制指标量级的有效手段。可以在聚合层上报前按比例丢弃一部分指标,或者只在用户触发关键行为时采集耗时数据。计数器类指标一般不建议采样,因为累计错误次数需要完整数据;而 Histogram 延迟数据可以接受适度采样,只要采样策略对全量请求是随机且一致的。聚合层还应定期清理长时间未更新的时间序列,避免内存无限增长。Prometheus 客户端库本身会根据时间序列的活跃情况自动管理生命周期,但聚合层自行维护的中间状态仍需定期回收。
告警阈值需要结合真实用户体验来设置。过低的阈值会导致告警疲劳,过高的阈值则可能错过早期故障信号。建议先运行一段时间,观察指标的正常波动范围,再确定 P95 延迟、错误率等阈值。此外,前端监控通常会产生大量短生命周期的时间序列,可以通过 --storage.tsdb.retention.time 控制 Prometheus 保留周期,并结合记录规则预先计算常用的聚合查询,降低瞬时查询压力。安全方面,聚合层应限制来源域并校验上报数据格式,防止恶意构造指标污染存储。
通过上述四层设计,可以在现有 Vue 3 应用中快速建立 Prometheus 监控与告警能力。前端采集借助 Vue 插件和路由守卫完成,后端聚合层屏蔽浏览器短生命周期与 Prometheus 抓取模型之间的差异,Prometheus 负责时序存储与规则评估,Alertmanager 完成通知分发。实践中重点在于标签治理、采样策略和阈值调优,只有把这三项做扎实,前端监控体系才能真正在故障初期给出有价值的结构化信号。
Vue_3Prometheus前端监控修改时间:2026-08-13 05:04:57