目的

监控错误是为了更好的处理错误。 前端最接近用户,一旦出现线上问题,前端也会最先介入处理,此时如何最快定位问题原因成了一个挑战,因为某些错误比较难复现,可能和特定机型,网络,用户操作有关,一旦发生这种问题,如果不知道这些影响因素,自然很难解决。

特别是小程序,真机运行时基本是个黑盒,不能像浏览器中一样打断点,最多抓抓包看看网络情况,或者反编译看看源码,所以需要一个工具帮助我们监控错误,并上报相关的信息。

指标

那么,哪些信息需要上报?

环境

  • 设备
  • 操作系统
  • 硬件信息 屏幕大小,像素比等
  • 网络情况
  • 小程序版本
  • 微信版本
  • 时间
  • 启动参数(场景值,页面路径和参数等)

用户

  • 用户标识
  • 操作行为
  • 访问路径

错误

  • 运行时错误
  • 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访问日志的方案