写点什么

基于 rrweb 框架,搭建前端技术运营监控体系的实践分享

作者:智在碧得
  • 2024-04-26
    广东
  • 本文字数:5810 字

    阅读完需:约 19 分钟

基于rrweb框架,搭建前端技术运营监控体系的实践分享

本文作者:何家伟,碧服务前端开发高/级工程师,拥有 10 年开发经验。

1 背景

在工程化的前端项目中,通常使用 webpack 进行打包优化并上线。打包后的产物经过压缩和优化,对于一般开发者来说难以理解。当这样的产物交付到线上生产时,由于生产环境的状态是不可监控的,且代码已被压缩,导致如果发生前端 js 报错,报错信息无法准确地映射到源代码中的具体位置,从而给问题的定位带来了很大的挑战。因此,迫切需要一种前端监控手段来记录和收集这些报错,以便快速定位问题。

为了实现前端监控和快速定位问题,必须解决各种类型的数据记录、收集和监控,例如 js 报错、接口报错、文件加载报错、用户行为等。同时,如果要实现实时的视频回放能力,还需要解决视频文件过大不方便保存和上传的问题。本方案的目标就是解决这些问题,实现前端全方位的监控。

2 总体技术方案

总体的技术方案包括两部分:

第一部分是页面报错、静态资源加载、用户行为、接口报错、埋点等数据的收集记录,这部分已经有技术方案实现,后面工程实践会具体介绍原理和实现。

第二部分是用户视频回放能力,将以视频的方式无失真地呈现用户的操作路径。在本方案中会解决视频的上传体积、存储位置、上传时机等问题。

第二部分的数据会作为第一部分数据的一个类型嵌入,一起上传到服务端。这两部分数据的收集将会统一到集成监控工具下。 

2.1 视频回放方案选择

本方案的核心是实现用户操作视频回放。下图是实现视频回放能力的业界方案对比:

从实验结果来看,可以得出一下结论:

  • rrweb 压缩后的文件大小跟其他方案文件大小差异比较大。

  • 时间越长,rrweb 压缩算法的优势越明显。


实际上,除了压缩方法,官网还提供了其他优化存储容量的方式:

  • 通过屏蔽 DOM 元素,减少录制的内容。

  • 通过 sampling 配置抽样策略,减少录制的数据。

  • 通过去冗、压缩,减少数据存储体积。

2.1.2 结论

通过前面的方案对比可以看到,rrweb 无论从体验(是否需要授权)、兼容性或者录制数据大小都有较为明显的优势。

2.2 数据收集和存储

集成的前端监控工具收集包括页面报错监控、静态资源加载监控、用户行为监控、接口报错监控等方面的数据,这些数据会存储在浏览器 indexDb 中。因为 rrweb 数据的量一般会达到几百 kb,所以会先用 fflate 库对数据进行压缩处理,然后再存放在 indexDb 中等待上传。每个类型的监控都是独立的表结构存储,rrweb 的具体数据是放到文件服务器中,以 json 文件格式存储。

2.3 数据上传

存在 indexDb 的数据会根据创建时间来进行筛选,每次产生新数据的时候都会判断第一个数据时间是否超过 10 分钟,这个时间是可以配置的。如果超过 10 分钟,就会把数据出栈,然后才会把新数据进栈,这样就能固定数据量大少,不至于过大。

整体的数据要经过 fflate 压缩,包括 rrweb 数据和其他类型的数据,然后用 blob 类型转换成文件的形式上传(后续要支持断点续传),后台 express 服务接收数据生成 json 文件形式存储在服务器文件系统中(后期可以考虑保存在阿里云)。

2.4 用户操作路径实现

(作者开发了后台监控中心专门用于监控数据的呈现,下文会有介绍。)

rrweb 的数据呈现是先读取数据库中 rrweb 表的。每条数据有用户系统和页面 url 的维度,还有对应生成的 json 文件的 url 路径。播放 rrweb 数据的时候先通过 axios 库加载 json 文件,再通过 rrwebPlayer 播放 json 文件数据。下部的用户操作数据也在 json 文件中,只是存在不同字段下。rrwebPlayer 有播放时间回调功能,在回调函数中调用相同时间段的用户操作数据就能实现 rrweb 和用户操作数据双屏播放。

2.5 总体技术架构图

3 前端监控工程实践

3.1 集成监控工具,嵌入 rrweb 功能


上屏是 rrweb 回放用户的真实的操作,下屏是记录用户的操作路径数据,例如点击是什么按钮,调用了什么接口,页面报了什么错误。

3.1.1 集成监控工具的原理

监控工具用 vite 框架库进行快速打包,与 webpack 相比能极速的服务启动。如果使用原生 ESM 文件,则无需打包。此外。轻量快速的热重载无论应用程序大小如何,都始终以极快的模块热重载,具有 “库” 模式的预配置 Rollup 构建,所以用来编写监控工具比较合适。

项目结构如下:


页面性能监控:

这个监控的主要目的是要页面从请求开始,到加载完成的各个环节耗时,一般来说会用到 Performance API。Performance 是前端性能监控的 API,它可以检测页面中的性能,也可以检测到白屏时间、首屏时间、用户可操作的时间节点,页面总下载的时间、DNS 查询的时间、TCP 链接的时间等关键性能指标,页面性能监控会用到这个 API 的 timing 属性。

Performance.time 属性如下图所示:


原理:监听 window.addEventListener('load', ()=>{}); 时间,这个时候页面加载完成,在回调函数中读取。

window.performance.timing 的各个属性值,用相关的属性相减得到网页各个阶段的用时。

页面报错监控:

经过了大量测试及联调的项目在有些时候还是会有十分隐蔽的 Bug 存在,这种复杂而又不可预见性的问题唯有通过完善的监控机制才能有效的减少其带来的损失。因此对于直面用户的前端而言,异常捕获与上报是至关重要。window.onerror 提供了全局监听异常的能力,通常情况下监听其回调事件,即可获得有用的参数:

原理:通过监听全局错误对象 window.onerror 在回调函数中,取得错误的信息,位置(列号,行好),错误文件名字进行保存。在进一步的实现中,最好的能把错误位置打源代码上下 6 行,记录下来。

Promise 的全局错误信息用 window.addEventListener(‘unhandledrejection’, function (event) {}) 进行捕获。

用户行为监控:

记录用户行为路径,对于产品优化具有重要意义,前端通过监控用户点击的元素,记录用户行为路径,用户点击按钮的位置可以用 xpath 来描述。

前端原理:点击每个元素记录其在页面的 xpath。


接口监控:

前端对应接口的监控有非常重要的意义,现在行业流行的单页应用非常依赖接口的速度。

对接口的监控可以定位系统流程问题,定位到具体哪一个接口有问题。前端现在还没有对接口监控专门的 API 处理,需要重写送接口的 API,在重写的 API 上加上发送的时间,在成功回调的时候减去发送的时间,得到整个接口的请求时间。

前端原理:通过装饰器模式,缓存原来的方法,记录开始发送时间,监听完成时刻,最终算出接口用时。

3.1.2 嵌入 rrweb 功能


{ init() { if (this.config.listenModules === "all") { this.config.listenModules = this.moduleMap } let upModules = this.config.listenModules if (upModules) { Object.keys(upModules).forEach((key) => { if (upModules[key]) { this.moduleMap[key].init((data) => { //监听每个模块的回调事件 this.moduleEvent(key, data, this.config.callback) }) } }) } if (this.config.rrweb) { let vm = this; //嵌入rrweb功能 this.stopFn = rrweb.record({ emit(event) { // 保存获取到的 event 数据,event里面是序列号后的DOM和鼠标事件等 let inputEvent = Object.assign({},event) inputEvent.createTime = new Date().getTime() vm.rrwebEvent.system = vm.system vm.rrwebEvent.userName = vm.userName vm.rrwebEvent.pageUrl = window.location.pathname vm.rrwebEvent.createTime = new Date().getTime() vm.rrwebEvent.dataType="rrweb" vm.arrPush(vm.rrwebEvent.rrwebData,inputEvent)//收集rrweb数据 } }) } }}
复制代码

通过遍历所有的监控模块,监听模块的回调,在回调的时候通过算法计算是否应该上传 wweb 数据。rrweb 的监控是随着其他的监控模块一起记录数据以及记录创建时间 createTime,方便在回放的时候播放。

3.2 监控数据和 rrweb 的数据上传的服务器

当监控工具在项目中运行时,发生页面报错或者接口报错时,监控数据上传到我们专门写的后台服务中,代码如下:

export const uploadData = async (req: Request | any, res: Response | any, next: NextFunction) => {  //monitorData是监控数据,包括行为监控和接口监控,错误监控等等,通过dataType字段区分  //rrwebData是rrweb监控数据   let { monitorData = [], rrwebData = [] } = req.body;    //rrweb和监控数据保存在file文件中,方便上传以及存储    let fileUrl = `${req.protocol}://${req.headers.host}/uploads/${req?.file?.filename}`;    const fileJSON = require(path.join(__dirname, '../public', 'uploads')+`/${req?.file?.filename}`)    const atouData= JSON.parse(atou(fileJSON.utoaData))    if(monitorData.length==0&&rrwebData.length==0){         monitorData =  atouData.monitorData        delete atouData.rrwebData.rrwebData        rrwebData =  atouData.rrwebData    }    let moduleMap = {        behavior: Behavior, errorCatch: ErrorCatch, pref: Pref, resources: Resources, xhrHook: XhrHook    }    let mapArr = {        behavior: [],        errorCatch: [],        pref: [],        resources: [],        xhrHook: []    }    monitorData.forEach(element => {//保存各个监控模块的数据        if (element.dataType) {            mapArr[element.dataType].push(element)        }    });    Object.keys(mapArr).forEach(key => {        moduleMap[key].create(mapArr[key])    });    rrwebData.fileUrl = fileUrl    Rrweb.create(rrwebData) ///保存rrweb数据    try {        res.json({            fileUrl,            success: true,            code: 200        });    } catch (error) {        next(error);    };}
复制代码

在后台监控管理中心中播放,代码如下:

methods: {        userPlay() {            let inputItem = this.monitorData[this.userPlayIndex]            if (!inputItem) return            if(this.userPlayIndex==0){                this.userDataPlayArr.push(inputItem)            }                      let fCreateTime = inputItem.createTime;            let allLen = this.monitorData.length            this.userTimer = setInterval(() => {                let next = this.monitorData[this.userPlayIndex + 1]                if (next) {                    if (fCreateTime + 100 >= next.createTime) {                        this.userDataPlayArr.push(next)                        this.userPlayIndex++                        if (this.userDataPlayArr.length > 10) {                            this.userDataPlayArr.shift()                        }                    }                    fCreateTime+=100                }                if (this.userPlayIndex >= allLen||!next) {                    if (this.userTimer) {                        clearInterval(this.userTimer)                    }                }
}, 100);
} },
复制代码


后台监控中心与最终的播放效果,如下图所示:

后台监控中心


最终播放效果

3.3 系统接入

其他业务系统要接入,参考下面的引入和使用方法,同时关注对应的 API。

3.3.1 自研监控工具+rrweb 的引入

支持 npm 和 cdn 的方式直接引入。cdn 方式引入注意是放到 index.html 的 body 结束标签前面。


<link rel="stylesheet" href="https://unpkg.com/monitor-bgy/dist/style.css"/>//这个标签放到body结束标签前面<script src="https://unpkg.com/monitor-bgy/dist/monitor-bgy.js"></script>
//也可以使用npm 引入npm install --save monitor-bgy
复制代码

3.3.2 使用方法



//1.cdn直接引用方式let monitorObj = new window.MonitorBgy({ userName: "hejiawei",//登录用户名称 uploadData(data) {//rrweb已经其他监控数据上传回调 }, }) monitorObj.init()
//2.另外也可以使用npm包的形式使用import MonitorBgy from 'monitor-bgy'let monitorObj = new MonitorBgy({ userName: "hejiawei",//登录用户名称 uploadData(data) {//rrweb已经其他监控数据上传回调 }, })monitorObj.init()
复制代码

3.3.3 主要 API

let config ={  saveDataTime:1000 * 60 * 10, //当报错的时候,上传用户前n分钟操作 默认10分钟  rrweb:true, //监控是否加上rrweb功能  system:window.location.hostname,//系统名称,默认系统域名  userName:"XXX",//用户名,当前的登录用户,或者用户唯/一标识  uploadUrl:"/rrweb/uploadData",//传出数据url,默认/rrweb/uploadData  listenModules:"all",//配置监控的类目,对象类型,可取的值如下,默认all是全部类型监控   // {   //  pref, //页面性能监控   //  resources,//静态资源监控   //  errorCatch,//js错误监控   //  xhrHook, //接口请求监控   //  behavior //用户行为监控   // }  uploadmMonitorFile:()=>{ //监控数据自动上传函数,可以自定义      }  }let monitorObj = new MonitorBgy(config)monitorObj.init()
复制代码

4 总结

以下是传媒系统接入监控工具(初步实现)后的实际监控成果展示:


页面报错监控

系统发生 js 报错的时候,就会上传对应的报错信息,通过系统、页面、用户能具体定位报错信息。

用户行为监控

用户点击了按钮、进入了页面、进行的操作,都会被记录,通过系统、页面、用户、时间等能查询出用户一段时间的操作


接口监控

当接口发生 500 错误的时候,会产生一条对应的接口报错信息,包括了 url 地址和参数信息。

rrweb 监控

系统在登录的时候,记录了系统、用户和页面等维度信息,当用户操作过程中产生产生报错信息的时候,例如页面报错、接口报错等,会触发监控工具上报数据,数据上报完成后,就会生成一条 rreweb 数据,通过查询系统、用户、页面和发生时间等条件就能查询具体用户数据。

rrweb 双屏监控

在 rrweb 监控页面定位用户数据后,点击查看按钮,就能看到双屏监控功能,上屏是用户操作视频,下屏是用户操作数据,因为视频文件存储量较大,后台 express 会有一个定时服务,删除创建日期超过 1 个月的数据,包括视频 json 文件。​

发布于: 刚刚阅读数: 5
用户头像

智在碧得

关注

还未添加个人签名 2024-04-08 加入

科创驱动智慧未来!碧桂园服务的创新引擎,物业数字化的开路先锋

评论

发布
暂无评论
基于rrweb框架,搭建前端技术运营监控体系的实践分享_框架设计_智在碧得_InfoQ写作社区