前言
你是否经常碰到业务反馈,线上的小程序某个页面打不开了,订单没法结算了,但是你当时测试的时候都是好好的。
由于线上环境复杂,一些问题只会在特定网络环境或者设备上发生,对于这类问题,异常信息的收集就显得格外重要了,我们不但希望收集错误的堆栈信息,还需要用户操作流程,设备信息等,以便复现错误。
简单收集
小程序App()生命周期里提供了onError函数,可以通过在onError里收集异常信息
App({
// 监听错误
onError: function (err) {
// 上报错误
wx.request({
url: "https://url", // 自行定义报告服务器
method: "POST",
errMsg: err
})
}
})
用户操作路径收集
一些较隐蔽的错误如果只有错误栈信息,排查起来会比较难,如果有用户操作的路径,在排查时就方便多了。
方法一:暴力打点方法收集
优点:简单直接
缺点:污染业务代码,造成较多垃圾代码
方法二:函数劫持(推荐使用)
需要在App函数中的onLaunch、onShow、onHide生命周期插入监控代码,我们通过重写App生命周期函数来实现。
App = function(app) {
["onLaunch", "onShow", "onHide"].forEach(methodName => {
app[methodName] = function(options) {
// 构造访问日志对象
var breadcrumb = {
type: "function",
time: utils.now(),
belong: "App", // 来源
method: methodName,
path: options && options.path, // 页面路径
query: options && options.query, // 页面参数
scene: options && options.scene // 场景编号
};
self.pushToBreadcrumb(breadcrumb); // 把执行对象加入到面包屑中
})
}
但是这样写,会把用户自定义的内容给覆盖掉,所以我们还需要把用户定义的函数和监控代码合并。
var originApp = App // 保存原对象
App = function(app) {
// .... 此处省略监控代码
// .... 此处省略监控代码
originApp(app) // 执行用户定义的方法
}
记录结果
可以从下面的json看出,用户到了detail页面,执行了onLoad => getDetail => onReady => buy 当执行buy方法的时候报错。
[{"method":"onLoad","route":"pages/film/detail","options":{"id":"4206"}},
{"method":"getDetail","route":"pages/film/detail","options":{"id":"4206"}}, {"method":"onReady","route":"pages/film/detail","options":{"id":"4206"}},{"method":"buy","route":"pages/film/detail","options":{"id":"4206"}}]
上报策略
考虑到在大型应用中,日志量比较大,我们采取抽样,合并,过滤三个方法减少日志的输出,代码实现可以参考lib/report.js
代码组织
项目使用rollup作为构建工作,实现ES6转ES5,模块加载功能。
项目目录如下:
config.js // 配置文件
core.js // 劫持小程序核心代码
events.js // 监听自定义事件
report.js // 上报类utils.js // 工具类
https://github.com/zhengguorong/xbossdebug-wechat
错误监控,日志上报还有小程序的性能监控也可以试一下腾讯的前端性能监控
https://console.cloud.tencent.com/rum
不错,看起来很强大,只是需要后端配合,但如果没有可视化界面呢,会不会也不好排查。
另外打包后的代码是丑化以后的,那收集到的日志会不会也不好排查。
可视化界面后面有时间会做一个,因为很多企业日志系统后台有自己一套体系,一般是用ELK的,你也可以ELK快速做个日志整合。
打包后的代码会被压缩,所以onError的信息是很难定位的,所以才有一个叫用户执行路径的字段,方便bug定位
太棒了!现在看到的唯一开源的很成熟的监控和收集错误方案!
非常好啊