前端异常监控方案

一、前言

前端作为直面用户的一层,即使经过多轮测试,仍可能存在隐蔽 Bug(如环境差异、用户特殊操作触发)。完善的异常捕获与上报机制能快速定位问题、减少损失,是前端质量保障的核心环节。

目前主流的异常监控方案分为两类:第三方托管工具(如阿里云 ARMS、Sentry 云服务)和 自建工具(如基于 Sentry 开源版本部署)。本文将结合核心捕获原理,详解两类工具的实战落地流程。

二、前端异常捕获核心方法

1. 基础捕获方式

(1)try catch:局部代码异常捕获

适用于明确可能出错的代码块(如异步请求、复杂计算),缺点是无法全局捕获,需手动包裹代码。

1
2
3
4
5
6
7
8
9
10
11
12
try {
// 可能抛出异常的代码(如 DOM 操作、数据解析)
const data = JSON.parse(unreliableString);
} catch (e) {
console.log('局部异常捕获:', {
message: e.message,
stack: e.stack,
type: e.name
});
// 手动上报异常
reportError(e);
}

(2)window.onerror:全局同步异常捕获

覆盖范围广,能捕获脚本执行、资源加载等同步异常,返回异常详情(信息、文件、行列号)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
window.onerror = function(errorMessage, scriptURI, lineNo, columnNo, error) {
// 过滤 Script error.(跨域导致)
if (errorMessage === 'Script error.') return true;

const errorInfo = {
errorMessage: errorMessage, // 异常信息(如 "a is not defined")
scriptURI: scriptURI, // 异常文件路径(如 "https://cdn.xxx.com/bundle.js")
lineNo: lineNo, // 异常行号
columnNo: columnNo, // 异常列号
stack: error?.stack || '',// 异常堆栈(定位原始代码)
timestamp: Date.now(), // 异常发生时间
userAgent: navigator.userAgent // 环境信息
};

// 上报异常(后续结合工具实现)
reportToMonitor(errorInfo);
return true; // 阻止浏览器默认报错打印
};

(3)unhandledrejection:Promise 异常捕获

window.onerror 无法捕获 Promise 未捕获的异常(如 fetch 失败、async/await 未 catch),需单独监听:

1
2
3
4
5
6
7
8
9
10
window.addEventListener('unhandledrejection', (event) => {
const errorInfo = {
type: 'PromiseError',
message: event.reason?.message || 'Promise 执行失败',
stack: event.reason?.stack || '',
timestamp: Date.now()
};
reportToMonitor(errorInfo);
event.preventDefault(); // 阻止控制台警告
});

2. 框架专属捕获(Vue/React)

MVVM 框架会捕获组件内部异常,需通过框架提供的 API 监听,避免 window.onerror 失效:

(1)Vue 2.x/3.x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Vue 2.x
Vue.config.errorHandler = function(err, vm, info) {
const errorInfo = {
message: err.message,
stack: err.stack,
vueInfo: info, // Vue 特定信息(如 "render" "mounted" 生命周期)
component: vm.$options.name || 'anonymous', // 异常组件名
props: vm.$props // 组件入参(辅助定位)
};
reportToMonitor(errorInfo);
};

// Vue 3.x
app.config.errorHandler = (err, instance, info) => {
// 逻辑同上,instance 对应 Vue 实例
};

(2)React 16+

通过 Error Boundary 组件捕获子组件异常(类组件专属,函数组件需借助 HOC):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

componentDidCatch(error, info) {
// 捕获子组件异常
const errorInfo = {
message: error.message,
stack: error.stack,
reactInfo: info.componentStack // React 组件堆栈
};
reportToMonitor(errorInfo);
this.setState({ hasError: true });
}

render() {
if (this.state.hasError) {
return <div>页面加载出错,请刷新重试</div>; // 降级 UI
}
return this.props.children;
}
}

// 使用:包裹可能出错的组件
<ErrorBoundary>
<MyBusinessComponent />
</ErrorBoundary>

3. 常见捕获问题解决方案

(1)跨域 Script error.

静态资源(CDN)跨域时,window.onerror 仅返回 Script error.,无法获取详细信息:

  • 前端:脚本标签添加 crossorigin="anonymous"
    1
    <script src="https://cdn.xxx.com/bundle.js" crossorigin="anonymous"></script>
  • 服务器:CDN 配置响应头 Access-Control-Allow-Origin: *(允许所有域访问)。

(2)压缩代码定位问题(sourceMap)

生产环境代码压缩后,异常行列号对应压缩后的代码,需通过 sourceMap 还原原始代码位置:

  1. 构建工具配置(Webpack 示例):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // webpack.config.js
    module.exports = {
    mode: 'production',
    devtool: 'hidden-source-map', // 生成 map 文件但不嵌入代码(避免暴露源码)
    output: {
    filename: 'bundle.[hash].js',
    path: path.resolve(__dirname, 'dist')
    }
    };
  2. 部署时:将 bundle.[hash].js.map 上传至监控工具(或服务器私有路径),用于解析异常。

三、异常上报工具实战

1. 方案一:阿里云 ARMS(第三方托管,快速落地)

阿里云 ARMS 是一站式 APM 工具,支持前端异常监控、性能监控、用户行为分析,无需自建服务,集成成本低。

(1)集成步骤

  1. 登录阿里云 ARMS 控制台,创建「前端监控应用」,获取 appkey
  2. 项目中引入 ARMS SDK(推荐 CDN 方式,无需打包):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 在 index.html 头部引入 -->
    <script>
    !function(e,a,t,n,g){var i={};function o(){var e=a.createElement("script");e.src=t,e.async=!0,e.onload=e.onerror=function(){i[t]=!0},a.head.appendChild(e)}i[a]={init:function(t){i.config=t,o()}},e[n]=i[e]}(window,document,"https://retcode.alicdn.com/retcode/bl.js","__bl");

    // 初始化配置
    __bl.init({
    appkey: "你的 ARMS appkey",
    scene: 1, // 1=web 场景
    sendResourceError: true, // 捕获资源加载异常(如图片、脚本加载失败)
    sendApiError: true, // 捕获接口请求异常(如 fetch、XMLHttpRequest 失败)
    enableSPA: true // 支持 SPA 路由切换监控
    });
    </script>

(2)核心优势

  • 自动捕获:无需手动编写 window.onerror,SDK 自动捕获同步/异步/资源/接口异常;
  • sourceMap 自动解析:ARMS 控制台上传 sourceMap 文件后,自动还原压缩代码的原始报错位置;
  • 可视化面板:支持异常趋势、TOP 异常、用户环境分布(浏览器、系统、地区)分析;
  • 告警配置:支持钉钉、短信、邮件告警,实时响应严重异常。

(3)sourceMap 上传

  1. 构建项目生成 bundle.[hash].jsbundle.[hash].js.map
  2. 登录 ARMS 控制台 → 前端监控 → 应用配置 → sourceMap 管理 → 上传文件(关联对应 JS 文件名)。

2. 方案二:自建 Sentry(开源灵活,可控性高)

Sentry 是开源的异常监控工具,支持多语言/框架,可部署在私有服务器,适合对数据隐私有要求的场景。

(1)部署 Sentry 服务(Docker 方式)

  1. 安装 Docker 和 Docker Compose;
  2. 克隆 Sentry 部署仓库:
    1
    2
    git clone https://github.com/getsentry/onpremise.git
    cd onpremise
  3. 执行部署脚本:
    1
    2
    ./install.sh # 初始化配置(设置管理员账号、数据库等)
    docker-compose up -d # 启动服务
  4. 访问 http://服务器IP:9000,登录后创建「前端项目」,获取 DSN(上报地址)。

(2)前端集成 Sentry SDK

  1. 安装依赖:
    1
    npm install @sentry/browser @sentry/tracing --save
  2. 初始化 SDK(入口文件 main.js):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import * as Sentry from '@sentry/browser';
    import { Integrations } from '@sentry/tracing';

    Sentry.init({
    dsn: "你的 Sentry DSN(如 http://xxx@服务器IP:9000/1)",
    integrations: [
    new Integrations.BrowserTracing({
    tracingOrigins: ["localhost", "https://你的域名"],
    routingInstrumentation: Sentry.vueRouterInstrumentation(router) // Vue Router 监控(可选)
    })
    ],
    tracesSampleRate: 1.0, // 性能监控采样率(1.0=全量)
    environment: process.env.NODE_ENV, // 环境标识(dev/prod)
    release: `你的项目名@${process.env.npm_package_version}` // 版本号(用于关联 sourceMap)
    });

(3)sourceMap 上传(自动解析)

  1. 安装 Sentry CLI:
    1
    npm install @sentry/cli --save-dev
  2. 配置 sentry.properties 文件:
    1
    2
    3
    4
    defaults.url=http://服务器IP:9000
    defaults.org=sentry(你的组织名)
    defaults.project=前端项目名
    auth.token=你的 Sentry 授权令牌(控制台 → Settings → API Keys)
  3. 构建时自动上传 sourceMap(package.json 脚本):
    1
    2
    3
    4
    5
    "scripts": {
    "build": "webpack --mode production",
    "sentry:upload": "sentry-cli releases files 你的项目名@1.0.0 upload-sourcemaps ./dist --url-prefix '~/static/js'",
    "deploy": "npm run build && npm run sentry:upload"
    }
  • 执行 npm run deploy,构建后自动上传 sourceMap 到 Sentry 服务。

(4)核心优势

  • 开源免费:无第三方依赖,数据存储在私有服务器;
  • 高度定制:支持自定义上报字段(如用户 ID、业务场景)、异常过滤;
  • 生态完善:支持 Vue/React/Angular 等框架,提供丰富的插件(如用户行为追踪、接口监控);
  • 详细堆栈:自动解析 sourceMap,展示原始代码行列号、组件上下文。

四、最佳实践

1. 上报字段优化

除基础异常信息外,补充以下字段可提升排查效率:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const errorInfo = {
// 基础信息
message: e.message,
stack: e.stack,
type: e.name,
// 环境信息
userAgent: navigator.userAgent,
url: window.location.href,
referrer: document.referrer,
// 业务信息(按需添加)
userId: localStorage.getItem('userId') || 'anonymous', // 用户 ID
scene: 'homepage', // 业务场景(首页/详情页)
appVersion: '1.0.0', // 项目版本
timestamp: Date.now()
};

2. 避免服务器压力

  • 采样上报:高访问量项目可设置采样率(如 10%),避免重复异常淹没服务器;
    1
    2
    3
    4
    // 简单采样逻辑(10% 采样)
    if (Math.random() < 0.1) {
    reportToMonitor(errorInfo);
    }
  • 去重处理:基于异常信息 + 文件名 + 行列号生成唯一标识,同一异常短时间内仅上报一次;
  • 批量上报:前端缓存异常,定期批量发送(如每 30 秒发送一次)。

3. 监控面板关注重点

  • 异常趋势:关注异常量突增(可能是新版本 Bug 导致);
  • TOP 异常:优先修复高频异常(如接口报错、公共组件异常);
  • 环境分布:重点排查特定浏览器/系统的异常(如 IE 兼容性问题);
  • 用户轨迹:结合用户行为日志(如点击、输入),还原异常触发场景。

参考资料


前端异常监控方案
https://cszy.top/20210127-前端异常捕获与上报/
作者
csorz
发布于
2021年1月27日
许可协议