目的
监控错误是为了更好的处理错误。 前端最接近用户,一旦出现线上问题,前端也会最先介入处理,此时如何最快定位问题原因成了一个挑战,因为某些错误比较难复现,可能和特定机型,网络,用户操作有关,一旦发生这种问题,如果不知道这些影响因素,自然很难解决。
特别是小程序,真机运行时基本是个黑盒,不能像浏览器中一样打断点,最多抓抓包看看网络情况,或者反编译看看源码,所以需要一个工具帮助我们监控错误,并上报相关的信息。
指标
那么,哪些信息需要上报?
环境
- 设备
- 操作系统
- 硬件信息 屏幕大小,像素比等
- 网络情况
- 小程序版本
- 微信版本
- 时间
- 启动参数(场景值,页面路径和参数等)
用户
- 用户标识
- 操作行为
- 访问路径
错误
- 运行时错误
- promise错误
- 静态资源错误
- 语法错误
- 网络请求错误
- 页面不存在
性能(可选)
用于优化性能,提高体验和稳定性。 使用小程序官方提供的api即可。
方案
- 以上指标皆有api可获取到相应信息,为了更好的可维护性和通用性选择将错误上报封装成sdk
- sdk是通用式的方案,需要通过劫持小程序生命周期,事件等等来实现,因为各个项目情况不一样,sdk必须要穿越这些不一样的地方,直达底层。
错误类型与捕获方式
- js语法错误 开发时就会发现
- js运行错误或者api调用报错 App.onError或者wx.onError
- 未处理的promise reject (包含async函数) App.onUnhandledRejection或者wx.onUnhandledRejection 需要注意的是prmoise中必须调用过reject才能触发这个事件
- 页面不存在 App.onPageNotFound 或者 wx.onPageNotFound
- 资源加载错误 image,audio,video,web-view提供了binderror方法,可监听错误事件,为了方便维护,项目中如果需要上报资源加载错误,应该将这些组件统一封装后再使用
- 网络请求错误 劫持wx.request进行上报,需要注意的应该去除request对象中无用的字段,避免上报数据过大
生命周期劫持
分为两类:
- app生命周期
- page生命周期
劫持函数中需要将页面,参数,场景值
等信息压入事件栈,
发生错误时,将事件栈一并上报,为防止数据过多,事件栈需要设置一个上限。
事件处理函数劫持
小程序中虽然有冒泡机制,但没有一个根节点能让我们通过冒泡机制收集所有的事件, 因此需要劫持小程序中定义的函数,判断其入参是否包含事件,来达到监听事件的目的
小程序的事件对象中有用的信息比较少,对于触发元素也只能拿到坐标,所以这里选择只将事件类型,相关页面信息压入记录栈。
console记录
劫持console上的函数,压入记录栈,需要限制栈大小
网络请求记录
劫持wx.request
函数,并记录信息,压入记录栈
需要注意的是wx对象的方法都是可配置但不可写的,故不能使用常规的引用方式去劫持
Object.getOwnPropertyDescriptor(wx, 'request')
{
configurable: true,
enumerable: true,
writable: false
}
只能使用 Object.defineProperty
来改写request的value属性
记录网络请求时,应该避免把cookie,响应等数据量过大,且比较敏感的信息上报,记录请求体,响应code即可
数据量限制
为了避免数据量过多,上文提到的所有数据栈都应限制大小,保留最近的几条即可
上报
由后端提供接口,将最近的事件栈,生命周期栈,console栈,网络请求,环境信息等等一并上报。
一般后端可使用kafka等消息中间件来提高吞吐率,提高上报接口的性能。在浏览器环境中,还会有使用请求gif图片,然后通过分析nginx访问日志的方案