如何监控浏览器crash?

📌 前言

页面崩溃是影响用户体验的最严重事故 ------页面完全失效,代码停止执行,即使等待也无法自行恢复,甚至常规的错误监控也会随之失效。

崩溃 vs 卡顿:卡顿是"暂时响应慢",崩溃是"彻底无法用"。崩溃发生后,页面上报能力归零,必须依赖浏览器机制。


🎯 方案选型对比

方案 实现原理 ✅ 优势 ❌ 劣势 可靠性
本地状态存储 崩溃前标记状态,二次进入上报 实现简单 依赖用户二次进入,4数据滞后,wu bao ⭐⭐
Service Worker 心跳 SW 独立生命周期,跨页面检测 独立于页面,存活时间长 ;兼容性较reporting api高一点 误报率高(卡顿误判为崩溃) ;开发量大 ⭐⭐⭐
Reporting API 🏆 浏览器原生崩溃报告机制 页面崩溃后仍可上报,无需业务代码干预 兼容性要求高 ⭐⭐⭐⭐⭐

核心结论 :Reporting API 是当前唯一能在页面彻底崩溃后仍可靠上报的技术方案。

reporting api 详解

1. 工作原理

浏览器通过 Reporting-Endpoints 响应头 声明崩溃报告接收地址。当页面发生崩溃时,浏览器在后台独立线程完成上报,页面主线程的终止不影响报告发送。

ReportingObserver(developer.chrome.com/blog/report... 适合开发者在前端主动监听和处理报告 (只支持获取 deprecation || intervention2种报告)

Reporting-Endpoints 如果希望浏览器自动上报崩溃等异常,需要通过 Reporting-Endpoints 响应头声明上报端点。 这种方式下,前端业务代码无需改动,只需配置响应头即可。

2. Reporting-Endpoints 上报字段分析

字段 说明
age 报告的时间戳与当前时间之间的毫秒数。
body 实际报告数据 ,已序列化为 JSON 字符串。报告的 body 中包含的字段由报告的 type 决定。 ⚠️ 注意 :不同类型的报告,其 body 结构不同。下面为 type = crash 时 body 包含的字段详解: 1.body.crash_report_api 可存放额外信息,读取自 window.crashReport 中的值。 2.body.reason 崩溃原因,取值包括: - oom:页面内存溢出 - unresponsive:页面长时间无响应,被浏览器强制终止 3.body.stack 崩溃时的 JavaScript 调用堆栈。body.reason 为 unresponsive且响应头需包含:document-policy = include-js-call-stacks-in-crash-reports(Chrome 137+ 开始支持) 该字段可从崩溃的文档中恢复调用堆栈。 3.body.is_top_level 崩溃页面是否属于顶级可遍历对象(top-level traversable)。 4.body.visibility_state 崩溃时页面的可见性状态,取值 visible 或 hidden。
type 报告类型,例如 csp-violation、coep 等。 🎯 需要关注的是 :crash 类型。
url 崩溃发生的页面地址。
user_agent 生成报告时所使用的请求的 User-Agent 标头。

window.crashReport

window.crashReport的使用方法(可以携带一些额外的信息,便于后续分析),但需要注意的是,该api还在实验阶段,需要浏览器开启配置项后,才可以使用

github.com/WICG/crash-...

dart 复制代码
await window.crashReport.initialize('crash-report');   // 初始化
// 设置基础信息
window.crashReport.set('pageUrl', window.location.href) // set信息
window.crashReport.set('pageTitle', document.title)
//移除信息
window.crashReport.remove('pageTitle', document.title)

实现示例

在服务端配置Reporting-Endpoints头,default中是接受报告的服务器地址。由于window.crashReport还是实验api,那么如果一定需要传递额外信息,则可以给url上增加query来实现

less 复制代码
app.use('/', express.static(path.join(__dirname, './public'), {
  setHeaders: (res) => {
    res.setHeader('Reporting-Endpoints', 'default="https://crash-report.free.beeceptor.com"');
  }
}));
结果展示

内存暴涨导致的crash

json 复制代码
[  
    {  
        "age": 10,  
        "body": {  
            "crash_report_api": {  
                "cookieEnabled": "true",  
                "crashTime": "2025-12-30T09:05:53.703Z",  
                "crashType": "oom",  
                "language": "en",  
                "memory": "[object Object]",  
                "pageLoadTime": "2025-12-30T09:05:48.822Z",  
                "pageTitle": "页面崩溃模拟 Demo",  
                "pageUrl": "<https://localhost:5000/crash.html>",  
                "platform": "MacIntel",  
                "referrer": "direct",  
                "screenColorDepth": "24",  
                "screenHeight": "1080",  
                "screenWidth": "1920",  
                "sessionId": "1767085548824-v4mushd65ib",  
                "timestamp": "1767085548823",  
                "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",  
                "viewportHeight": "958",  
                "viewportWidth": "601"  
            },  
            "is_top_level": true,  
            "visibility_state": "visible"  
        },  
        "type": "crash",  
        "url": "https://localhost:5000/crash.html",  
        "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"  
    }  
]

报告接收服务搭建

  • 支持跨域
  • 支持解析application/reports+json类型
javascript 复制代码
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use((req, res, next) => {
    // 设置 CORS 响应头
    res.header('Access-Control-Allow-Origin', '*'); // 生产环境建议指定具体域名
    res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.header('Access-Control-Max-Age', '86400'); // 预检请求缓存 24 小时

    // 处理预检请求(OPTIONS)
    if (req.method === 'OPTIONS') {
      return res.sendStatus(200);
    }
    
    next();
  });

  // // 中间件:解析 JSON 请求体
  app.use(bodyParser.json({type: 'application/json'}));
  app.use(bodyParser.json({type: 'application/reports+json'}));

参考资料

1.window.crashReport:github.com/WICG/crash-...

2.zhuanlan.zhihu.com/p/40273861 如何监控网页崩溃

3.developer.chrome.com/docs/capabi...

4.juejin.cn/post/743899...

相关推荐
zd8451015007 分钟前
基于 HTML 与 Modbus TCP 构建设备仪器上位机:可行性分析与代码结构设计
前端·tcp/ip·html
Amumu1213840 分钟前
NodeJs介绍
前端·node.js
咧咧小侠42 分钟前
Windows Codex 连接 Chrome 失败:一次 nodeRepl.fetch 报错排查记录
前端·chrome
Eider199844 分钟前
大模型输出格式幻觉解决思路
开发语言·前端·javascript
广州智造44 分钟前
Radioss时间步长(Time Step)
前端·设计·cad·建模·cae·radioss
志尊宝1 小时前
Vue3 零基础每日笔记(085):项目上线前 Checklist——环境变量、报错监控、安全自查
前端·javascript·笔记·安全·信息可视化·echarts
朱涛的自习室1 小时前
我让 Gemini 4 当高管、DeepSeek 当员工,开了一家软件公司(Day 1)
android·前端·人工智能
志尊宝1 小时前
Vue3 零基础每日笔记(088·完结):从零到一——一个完整 Vue3 项目的成长复盘
前端·javascript·vue.js·笔记·ecmascript·#vue.js
deli0071 小时前
加一台机器,为什么只有 1/N 的 key 要搬家?一致性哈希实测
前端
兔子零10241 小时前
FastAPI 零基础入门教程:写过 Node.js,就能轻松学会 Python 后端开发(一)
前端·后端·python