外观
前端监控与可观测性建设
前端最被动的时刻,是用户反馈"页面白屏了",而团队没有任何数据:不知道影响多少人、从哪个版本开始、错误堆栈指向哪里。
监控不是装一个 SDK 就结束。完整的可观测性要覆盖错误、性能、行为和接口,并且能把数据关联到版本、用户和具体代码。
监控体系的四个维度
| 维度 | 关注问题 | 典型数据 |
|---|---|---|
| 错误 | 哪里坏了,影响多少人 | JS 异常、资源加载失败、接口错误、框架错误 |
| 性能 | 用户是否觉得慢 | LCP、INP、CLS、TTFB、自定义耗时 |
| 行为 | 用户做了什么 | 页面访问、点击路径、关键操作 |
| 接口 | 请求是否可靠 | 成功率、耗时分布、状态码、超时 |
四个维度要能通过同一个会话 ID 关联。否则看到错误也不知道对应哪次访问、哪个接口、哪个版本。
错误采集
前端错误来源比想象中多,需要分别处理:
全局 JavaScript 异常
ts
window.addEventListener('error', (event) => {
report({
type: 'js-error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack,
release: import.meta.env.VITE_APP_VERSION
})
})Promise 未捕获异常
ts
window.addEventListener('unhandledrejection', (event) => {
report({
type: 'unhandled-rejection',
reason: normalizeError(event.reason),
release: import.meta.env.VITE_APP_VERSION
})
})资源加载失败
资源错误不会冒泡,但会在捕获阶段触发 error,需要判断事件目标:
ts
window.addEventListener(
'error',
(event) => {
const target = event.target as HTMLElement | null
if (!(target instanceof HTMLScriptElement) &&
!(target instanceof HTMLLinkElement) &&
!(target instanceof HTMLImageElement)) {
return
}
report({
type: 'resource-error',
tagName: target.tagName,
url: target instanceof HTMLImageElement ? target.src : target.getAttribute('href') ?? target.getAttribute('src')
})
},
true
)框架错误
Vue 的组件错误默认不会抛到 window.onerror,需要注册框架级处理器:
ts
app.config.errorHandler = (error, instance, info) => {
report({
type: 'vue-error',
error: normalizeError(error),
info,
component: instance?.$options.name
})
}React 侧使用 Error Boundary 捕获渲染错误,同时保留 window.onerror 作为兜底。异步事件和 Promise 中的错误仍需全局监听。
性能指标采集
Core Web Vitals 是基础,但业务还需要自定义指标,例如编辑器首屏渲染、搜索结果返回、支付流程耗时:
ts
performance.mark('checkout:start')
await submitOrder()
performance.mark('checkout:end')
performance.measure('checkout:total', 'checkout:start', 'checkout:end')
const [measure] = performance.getEntriesByName('checkout:total')
report({
type: 'custom-metric',
name: 'checkout:total',
value: measure.duration
})注意自定义指标要带上下文:页面、设备、网络、版本、用户分群。同样的 800ms,在桌面和低端安卓上的含义完全不同。
接入 Sentry
自建采集链路成本不低。Sentry 这类平台提供了错误聚合、Source Map 还原、性能追踪和会话回放:
ts
import * as Sentry from '@sentry/vue'
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
history: createWebHistory(),
routes: []
})
Sentry.init({
app,
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
release: import.meta.env.VITE_APP_VERSION,
integrations: [Sentry.browserTracingIntegration({ router })],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.01,
replaysOnErrorSampleRate: 1
})
app.use(router)
app.mount('#app')关键配置:
tracesSampleRate:性能追踪采样率。生产环境通常 5% 到 20%,全量成本高。replaysOnErrorSampleRate: 1:错误会话完整录制,便于复现,但要注意隐私脱敏。release:每次发布使用唯一版本号,才能判断错误是否由新版本引入。
Source Map 是定位问题的前提
压缩后的堆栈 a.b.c is not a function 没有价值。构建时生成 Source Map 并上传到监控平台,同时不要公开暴露:
ts
// vite.config.ts
import { sentryVitePlugin } from '@sentry/vite-plugin'
export default defineConfig({
build: {
sourcemap: true
},
plugins: [
sentryVitePlugin({
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
sourcemaps: {
assets: './dist/**'
}
})
]
})上传后可以把构建产物中的 .map 文件删除,避免源码泄漏。Sentry 通过 sourceMappingURL 和 release 关联堆栈。
上报本身的工程问题
监控 SDK 不能成为新的故障源:
- 自动兜底:上报代码整体包在
try/catch中,失败静默。 - 采样与限流:同一错误在短时间内只上报一次,避免循环报错打爆后端。
- 离线补发:将事件暂存,页面卸载时使用
sendBeacon发送。 - 隐私保护:不采集密码、身份证、银行卡等敏感信息;会话回放要遮罩输入框。
- 体积控制:SDK 按需加载,避免阻塞首屏。
ts
function safeReport(payload: unknown) {
try {
const body = JSON.stringify(payload)
if (body.length > 32_000) return
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/telemetry', new Blob([body], { type: 'application/json' }))
return
}
fetch('/api/telemetry', {
method: 'POST',
body,
keepalive: true,
headers: { 'Content-Type': 'application/json' }
}).catch(() => {})
} catch {
// 监控失败不能影响业务
}
}从数据到告警闭环
采集只是起点,真正产生价值的是闭环:
- 按版本对比:新版本错误率是否上升,能否自动关联到发布记录。
- 按影响面排序:错误数多不等于优先级高,要看影响用户数、核心页面和转化路径。
- 告警分级:白屏、支付失败等 P0 实时告警;偶发 JS 错误进入日报。
- 接入工单:告警自动创建任务,带上错误详情、复现步骤和版本号。
- 回滚机制:错误率超过阈值时能快速回滚,而不是先定位半小时。
常见坑
- 只采集 window.onerror。漏掉 Promise、资源和框架错误,覆盖率不足。
- 没有 Source Map。错误堆栈无法定位,采集了也没法修。
- 全量上报。高流量下成本失控,也会影响页面性能。
- 忽略监控 SDK 的副作用。SDK 体积、初始化时间和请求竞争都会影响 CWV。
- 数据没有 owner。看板没人看、告警没人处理,监控形同虚设。
小结
前端可观测性的核心是让问题可关联:错误关联到版本、用户、请求和操作路径,性能关联到页面和发布,行为关联到会话。工具可以买,但采集规范、告警分级和修复闭环必须由团队自己建立。