深色模式
前端可观测 RUM
摘要:后端一切正常但用户仍然抱怨"很卡",问题往往在浏览器里。RUM(真实用户监控)采集真实访客的页面加载性能、JS 错误和交互延迟,本文带你接入 Web 核心指标采集、错误上报,并把前端请求与后端 trace 串联起来。
适用环境
bash
# 确认有后端接收上报数据(OTLP Collector 或 RUM 产品)
nc -z -w2 127.0.0.1:4318 && echo "OTLP HTTP OK"
# 确认站点开启了跨域与上报域名可达
curl -sI https://your-site.example.com | grep -i 'access-control\|content-security'
# Node 环境(构建前端 SDK)
node --version; npm --version1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
操作步骤
1. 明确要采集的四类数据
- 性能指标:页面加载各阶段耗时(DNS、TCP、首字节、可交互)。
- Web 核心指标 Core Web Vitals:LCP(最大内容绘制)、INP(交互响应)、CLS(布局偏移)。
- 错误:JS 运行时异常、未处理的 Promise rejection、资源加载失败。
- 请求:XHR/Fetch 的成功率与耗时(用于串联后端)。
2. 用原生 API 采集导航性能
javascript
// 页面加载完成后读取 Navigation Timing
window.addEventListener('load', () => {
const nav = performance.getEntriesByType('navigation')[0];
const paint = performance.getEntriesByType('paint');
const metrics = {
dns: nav.domainLookupEnd - nav.domainLookupStart,
tcp: nav.connectEnd - nav.connectStart,
ttfb: nav.responseStart - nav.requestStart,
domReady: nav.domContentLoadedEventEnd - nav.startTime,
loadComplete: nav.loadEventEnd - nav.startTime,
fp: paint.find(p => p.name === 'first-paint')?.startTime,
fcp: paint.find(p => p.name === 'first-contentful-paint')?.startTime,
};
console.table(metrics);
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3. 采集 Core Web Vitals
bash
npm install web-vitals1
javascript
import { onLCP, onINP, onCLS } from 'web-vitals';
function send(name, value, rating) {
navigator.sendBeacon('/rum', JSON.stringify({
name, value, rating,
page: location.pathname,
ts: Date.now(),
}));
}
onLCP(m => send('LCP', m.value, m.rating)); // <2.5s 为 good
onINP(m => send('INP', m.value, m.rating)); // <200ms 为 good
onCLS(m => send('CLS', m.value, m.rating)); // <0.1 为 good1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
4. 捕获 JS 错误
javascript
window.addEventListener('error', (e) => {
send('js_error', 1, 'error', { message: e.message, stack: e.error?.stack, src: e.filename });
});
window.addEventListener('unhandledrejection', (e) => {
send('promise_rejection', 1, 'error', { reason: String(e.reason) });
});1
2
3
4
5
6
2
3
4
5
6
5. 用 OTel 浏览器 SDK 上报(可选,统一到 OTel 体系)
javascript
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
const provider = new WebTracerProvider({
resource: { attributes: { 'service.name': 'web-frontend' } },
});
provider.addSpanProcessor(
new BatchSpanProcessor(new OTLPTraceExporter({ url: 'https://collector.example.com/v1/traces' }))
);
provider.register();
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(),
new FetchInstrumentation({ propagateTraceHeaderCorsUrls: [/api\./] }),
],
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
6. 前后端链路串联
javascript
// FetchInstrumentation 会自动注入 traceparent 头;后端沿用即可得到一个完整 trace
// 后端需在 CORS 里放行 traceparent 头1
2
2
nginx
# Nginx:CORS 放行 traceparent
add_header Access-Control-Allow-Headers 'traceparent,content-type,authorization' always;
add_header Access-Control-Expose-Headers 'traceparent' always;1
2
3
2
3
DANGER
RUM 采集的数据直接来自用户浏览器。严禁采集页面输入内容、URL 中的敏感参数、Cookie 与 token;上报前必须对 URL 做参数白名单过滤,否则会造成用户数据泄露。
验证
bash
# 1) 浏览器 DevTools -> Network 里能看到 /rum 或 /v1/traces 的上报请求
# 2) 后端收到数据
curl -s http://127.0.0.1:8889/metrics | grep -c 'web_frontend'
# 3) 手动触发一个 JS 错误,确认被捕获上报
# 在控制台执行:throw new Error('rum test')
# 4) 后端 trace 里能看到来自 web-frontend 的根 span
curl -s 'http://127.0.0.1:3200/api/search?service=web-frontend&limit=5' | head -c 2001
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
常见坑
WARNING
上报用 sendBeacon 而非 fetch,否则页面关闭时上报请求会被取消,导致跳出率高的页面数据严重缺失。
WARNING
跨域请求默认不会携带 traceparent 头,导致前后端链路断成两截。需在后端 CORS 配置里显式放行该请求头。
DANGER
未做采样和限流的 RUM 上报,在大流量站点上会把 Collector 打满,并产生巨额存储成本。请设置采样比例(如 10%)并对高价值页面(如结算流程)单独提高采样率。