使用 rrweb 还原用户的操作,监听线上 BUG

哥们最害怕的时刻莫过于:测试环境一切正常,一上线用户就报错。

更糟糕的是,用户反馈往往只有一句:“页面打不开了”或者“点击没反应”。当我们试图复现时,却发现自己无论怎么操作都无法触发 Bug。用户不愿意提供详细步骤,客服也传达不清楚,最后只能对着日志干瞪眼。

如果有这样一种技术,能像“时光倒流”一样,完整还原用户出错前的每一步操作,那该多好?


一、为什么我们需要监控与回放?

1.1 沉默的流失

在产品运营中,愿意主动上报 Bug 的用户是极少数。 绝大多数用户遇到体验问题或 Bug 时,选择是直接关闭页面,卸载应用,然后永远不再回来。我们失去了挽留他们的机会,甚至不知道他们为什么离开。

1.2 “在我这里没问题”

前端开发的口头禅:“我没复现这个问题啊,tmd用户怎么操作的”。 因为线上环境太复杂了:

  • 用户的网络波动
  • 特定的浏览器版本
  • 特殊的操作顺序
  • 并发请求的竞争条件

1.3 解决方案:从“猜”到“看”

传统的日志监控(如 Sentry)只能告诉我们哪里错了(Error Stack),但很难告诉我们用户是怎么操作才导致错的会话回放技术填补了这个空白。


二、什么是 rrweb?

rrweb (record and replay web) 是一个开源的 Web 会话录制与回放库。

2.1 它不是视频录屏

很多人以为 rrweb 是像 OBS 一样录制视频流。这么想你就错了

  • 视频录屏: 录制像素点,文件大,无法检索,无法分析 DOM 结构。
  • rrweb 回放: 录制 DOM 变更事件(Mutation)。它记录的是“在什么时间,哪个节点,发生了什么变化”。

2.2 核心优势

  1. 体积极小: 传输的是 JSON 文本,经过压缩后,几分钟的操作可能只有几十 KB。
  2. 可检索: 因为记录的是 DOM 结构,你可以搜索页面上的文字来定位片段。
  3. 隐私可控: 可以在录制时配置忽略敏感输入框(如密码、银行卡号)。
  4. 跨平台: 录制的文件可以在任何地方回放,甚至离线回放。

三、核心原理:DOM 的序列化与反序列化

rrweb 的工作原理可以概括为:“把页面上的状态变成 JSON 数据,然后拿着这个 JSON 数据再转换成页面”。

3.1 录制阶段(Record)

rrweb 在用户浏览器中运行,主要依赖以下技术:

  1. MutationObserver: 监听 DOM 树的变化(节点新增、删除、属性修改、文本变化)。
  2. 事件监听: 监听鼠标移动、点击、滚动、输入等交互事件。
  3. 快照(Snapshot): 在录制开始时,通过遍历 DOM 树生成一份初始 HTML 结构的 JSON 快照。
  4. 增量数据: 之后只记录变化的部分(Incremental Data)。

数据流示例:

// 初始快照 { "type": 2, "data": { "node": { "tagName": "html", ... } } } // 增量操作:用户点击了按钮 { "type": 3, "data": { "source": 1, "positions": [{ "x": 100, "y": 200 }] } } // 增量操作:DOM 发生变化 { "type": 3, "data": { "source": 2, "adds": [...], "removes": [...] } }

3.2 回放阶段(Replay)

  1. 创建一个干净的 iframe 或容器。
  2. 读取初始快照,重建 DOM 树。
  3. 按照时间戳顺序,依次重放增量事件(模拟鼠标移动、触发 DOM 变更)。
  4. 我们排查问题的时候看到的就像是在看视频一样。

四、架构设计与实施步骤

要实现一套完整的回放系统,我们需要三部分组成:客户端 SDK数据存储服务回放管理平台

4.1 基本流程

  1. 用户端: 用户访问页面 -> SDK 自动录制 -> 触发异常或会话结束 -> 数据上报。
  2. 服务端: 接收数据 -> 压缩存储(如 MongoDB 或 OSS)。
  3. 管理端: 开发人员登录后台 -> 查看错误列表 -> 点击“查看回放” -> 加载数据播放。

4.2 代码实现 Demo

npm 地址 : https://www.npmjs.com/package/rrweb

1. 安装 rrweb

npm install rrweb

2. 前端录制与上报

import rrweb from 'rrweb'; // 开始录制 rrweb.record({   emit(event) {     // 将事件数据发送到你的服务器     // 建议:不要每个事件都发,要在本地缓存,攒够一定数量或时间后批量发送     fetch('/api/record', {       method: 'POST',       headers: { 'Content-Type': 'application/json' },       body: JSON.stringify({ events: [event], sessionId: getSessionId() })     });   },   // 隐私保护:忽略包含 .sensitive 类的元素   blockClass: 'sensitive',    // 忽略脚本标签   ignoreClass: 'ignore', }); // 模拟获取会话 ID,用于关联错误日志 function getSessionId() {   return localStorage.getItem('session_id') || generateUUID(); }

3. 后端接收(Node.js 示例)

app.post('/api/record', (req, res) => {   const { events, sessionId } = req.body;   // 将 events 存入数据库,key 为 sessionId   db.save(sessionId, events);    res.send({ success: true }); });

4. 管理端回放

import rrweb from 'rrweb'; // 从服务器获取录制数据 const events = await fetch(`/api/replay?sessionId=${id}`).then(r => r.json()); new rrweb.Player({   target: document.getElementById('replay-container'),   data: events, });

五、关键局限与解决方案(避坑指南)

虽然 rrweb 很强大,但它不是万能的。“它只记录了用户的界面操作,没法监控到具体出了什么问题,它无法录制 js 里面的代码。”

5.1 局限一:无法直接捕获 JS 错误堆栈

rrweb 记录的是“现象”,不是“病因”。如果页面白屏了,回放可能只看到页面加载了一半然后不动了,但不知道是哪个变量 undefined 导致的。

解决方案

  • rrweb + 错误监控 (把报错信息一起传到日志中):
    1. 当 JS 报错时,错误监控 SDK 捕获错误堆栈。
    2. 将当前的 sessionId 附带在错误日志中一起上报。
    3. 在查看错误详情时,提供一个“查看回放”的按钮,通过 sessionId 拉起 rrweb 回放。 这样你就既知道了*为什么错(堆栈),又知道了怎么错的(操作路径)。*

5.2 局限二:性能开销

全量录制所有 DOM 变化和鼠标移动,可能会占用用户主线程,导致页面卡顿。

解决方案:

  1. 采样录制: 只对 10% 的用户开启录制,或者仅在检测到错误时开启(需配合预加载)。
  2. 节流(Throttle): 限制鼠标移动事件的录制频率(如每 500ms 记录一次)。
  3. 暂停录制: 当页面切到后台(visibilitychange)时暂停录制。

5.3 局限三:隐私安全

如果不小心录下了用户的密码、身份证号或聊天记录,会引发严重的法律合规问题(GDPR/个人信息保护法)。

 解决方案:

  1. 配置 blockClass 强制将敏感输入框标记为不录制(rrweb 默认会隐藏 input[type="password"],但其他敏感信息需手动配置)。
  2. 数据脱敏: 在上传前,对 JSON 数据中的特定字段进行清洗。
  3. 访问控制: 回放平台必须权限严格,只有授权人员可查看,且查看日志需留痕。

5.4 局限四:数据量与存储

高频操作会产生大量 JSON 数据。

解决方案:

  1. 压缩: 前端使用 pako (gzip) 压缩后再上传。
  2. 生命周期: 设置数据保留期限(如只保留 7 天),过期自动删除。
  3. 触发式上传: 平时只存在本地 IndexedDB,只有发生 Error 或用户主动反馈时,才上传之前的录制数据。

六、总结

前端监控体系的建设,是衡量一个团队工程化成熟度的重要标志。

  • 错误监控(Sentry 等) 告诉我们 What(出了什么错)。
  • 会话回放(rrweb) 告诉我们 How(用户怎么操作的)。
  • 性能监控 告诉我们 Where(哪里慢)。

rrweb 集成到我们的监控体系中,它能极大地缩短 Bug 排查时间,减少开发与测试的沟通成本,更重要的是,它能让我们真正站在用户的视角去理解产品体验。

最后提醒: 技术虽好,隐私先行。在上线回放功能前,请务必更新隐私协议,并获得用户的同意。

Read more

【踩坑记录】使用 Layui 框架时解决 Unity WebGL 渲染在 Tab 切换时黑屏问题

【踩坑记录】使用 Layui 框架时解决 Unity WebGL 渲染在 Tab 切换时黑屏问题

【踩坑记录】使用 Layui 框架时解决 Unity WebGL 渲染在 Tab 切换时黑屏问题 在开发 Web 应用时,尤其是集成了 Unity WebGL 内容的页面,遇到一个问题:当 Unity WebGL 渲染内容嵌入到一个 Tab 中时,切换 Tab 后画面会变黑,直到用户点击黑屏区域,才会恢复显示。 这个问题通常是因为 Unity 渲染在 Tab 切换时被暂停或未能获得焦点所致。 在本文中,我们将介绍如何在使用 Layui 框架时,通过监听 Tab 切换事件并强制 Unity WebGL 渲染恢复,来解决这一问题。 1. 问题描述 当 Unity WebGL 内容嵌入到页面中的多个

前端 Axios 深度封装实战:拦截器 + 文件处理 + 业务接口统一管理

前端 Axios 深度封装实战:拦截器 + 文件处理 + 业务接口统一管理

嘿,开发的小伙伴们!今天咱来好好唠唠Axios,这可是在前端数据请求领域相当火的一个工具库。我第一次用Axios的时候,就被它的简洁易用和强大功能给吸引住了,感觉像是找到了一个能帮我轻松搞定数据请求的得力助手。 注:章节 1-4 是通过 AI 生成的入门介绍,人工进行了审核和勘误,如已比较熟悉可跳过,章节 5 是纯人工创作,结合真实项目详细说明如何封装与使用。 一、Axios是什么 Axios本质上是一个基于Promise的HTTP客户端,主要用于浏览器和Node.js环境。它就像是一座桥梁,负责在前端应用和后端服务器之间传递数据。无论是向服务器发送GET、POST、PUT、DELETE等各种请求,还是处理服务器返回的响应,Axios都能轻松应对。 想象一下,你的前端应用就像一个热闹的集市,各种组件都需要从服务器获取数据来展示,比如商品信息、用户资料等等。Axios就是那个勤劳的“采购员”,它穿梭于集市(前端应用)和仓库(服务器)之间,按需获取数据,确保每个组件都能及时拿到所需信息。 二、Axios的特点 1. 简洁易用的API

金三面了两家大厂前端岗,还没offer的可以试试我的方法(文档含答案)

前言:前所未有的挑战与机遇 2026年的前端面试,早已不再是刷几套“八股文”就能轻松过关的年代。如果你正准备冲击“金三银四”的大厂Offer,首先需要清醒地认识到:市场对前端工程师的定义正在被AI和行业寒冬彻底重塑。 当前,AI工具已能完成前端60%以上的基础页面构建工作,企业对初级岗位的需求急剧萎缩,而留下的岗位则对候选人提出了近乎严苛的要求。大厂前端岗的面试难度,已经从考察“你会不会写代码”,彻底转向了考察“你能否解决AI解决不了的复杂问题”以及“你是否具备从0到1搭建和维护系统的能力” 。这份《26年金三大厂前端岗面试1000道高频面试原题(含答案)》,正是基于这一背景,为你揭示高难度面试背后的真实逻辑。 一、难度升级:面试考察的三个维度转型 1. “八股文”消亡,场景题与架构设计成为主流 如果你还停留在背诵var和let区别的阶段,大概率会在初面就折戟沉沙。根据近期面试复盘,几乎没有大厂再单纯问语法细节,取而代之的是清一色的项目场景题。例如: * 性能优化: “当QPS达到峰值时,前端该如何处理?” “如何统计长任务时间并保证页面不卡顿?” 复杂场景实现: “

【Actix Web】Rust Web开发实战:Actix Web框架全面指南

【Actix Web】Rust Web开发实战:Actix Web框架全面指南

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,ZEEKLOG全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Rust开发,Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。 所属的专栏:Rust语言通关之路 景天的主页:景天科技苑 文章目录 * Rust Web开发 * 一、Actix Web框架概述 * 1.1 Actix Web的特点 * 1.2 Actix Web与其他Rust框架比较