原因
这几天,公司开发的小程序有部分用户用户打开咨询列表页显示空白,我们查后台日志没有错误。最让人头疼的是,这种前端显示不正确,只是在某些机型上才出现,我们的测试人员无法复现该问题。无法复现,又没有错误日志记录,这种问题咋解决?
方案1
思前想后,我们需要将小程序端所有的错误日志记录下来,并上传给后台。因为小程序的特性,除了App.js,所有其他的逻辑都在Page函数中,我们只需要处理Page函数中所有的方法,结合之前的一篇《微信小程序重写Page函数,实现全局日志记录》,我们可以在那基础上继续改写Page函数,使用try catch捕获所有函数的异常,然后将捕获的异常信息上传到服务端。
微信小程序最近推出了小程序的实时日志,结合小程序的实时日志后,我们不需要写相关的后端代码,可将异常信息上传到小程序的平台。(点击链接可查看实时日志详情和使用方式,这里不在多描述)
function init(_data) {
data = _data
// 重写page函数,增加阿里云监控
let oldPage = Page
Page = function(obj) {
// 对Page的传参obj对象的所有函数进行异常捕捉
for (let key in obj) {
let oldFunction = obj[key]
if (typeof oldFunction === 'function') {
obj[key] = function() {
try {
// 日志埋点
if (['onShow', 'onHide'].includes(key)) {
log.info('执行了' + this.__route__ + '的' + key + '方法')
key === 'onShow' ? enterPageLog() : leavePageLog()
}
return oldFunction.call(this, ...arguments)
} catch (e) {
// 上传信息到小程序实时日志
var log = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : null
log || log.error.call(log, '发生了错误!页面:' + this.__route__ + ',方法:' + key + ',错误信息:' + e.message)
throw e
}
}
}
}
return oldPage(Monitor.hookPage(obj))
}
}
这样就通过实时日志功能上传了错误日志到微信小程序的管理后台,通过https://mp.weixin.qq.com登录小程序管理端,就能看到实时日志,通过openid筛选就能查看到对应用户的错误日志,从而排查问题了。
方案2
但微信小程序的错误日志感觉不全,微信那边应该是做了流控或是采样,有些错误没有抓到,造成排查问题的困难。
还是自己动手吧,将错误信息上传到后端。代码如下,只修改了上面上传日志的那一部分
function init(_data) {
data = _data
// 重写page函数,增加阿里云监控
let oldPage = Page
Page = function(obj) {
// 对Page的传参obj对象的所有函数进行异常捕捉
for (let key in obj) {
let oldFunction = obj[key]
if (typeof oldFunction === 'function') {
obj[key] = function() {
try {
// 日志埋点
if (['onShow', 'onHide'].includes(key)) {
log.info('执行了' + this.__route__ + '的' + key + '方法')
key === 'onShow' ? enterPageLog() : leavePageLog()
}
return oldFunction.call(this, ...arguments)
} catch (e) {
const errInfo = '发生了错误!页面:' + this.__route__ + ',方法:' + key + ',错误信息:' + e.message
console.error(errInfo)
// 上传信息到服务端
wx.request({
url: host + '/addWebLog',
method: 'POST',
data: {
errInfo: errInfo
}
})
throw e
}
}
}
}
return oldPage(Monitor.hookPage(obj))
}
}
上传到服务器的错误信息包含当前用户、报错页面、报错方法和错误信息,这样我们在服务端就能查看到前端的异常了。
后端处理
新建一个controller来接收这个请求,用于接收前端上传的数据。
@RestController
public class LogController {
private Logger logger = LoggerFactory.getLogger(LogController.class);
private LinkedBlockingQueue<WebLog> queue;
private Integer Queue_Capacity = 1000; //队列的容量
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {
queue = new LinkedBlockingQueue<>(Queue_Capacity);
Thread thread = new Thread(() -> {
while (true) {
try {
// 从队列末尾取,取不到则阻塞
WebLog webLog = queue.take();
logger.info("前端出错:" + JSONObject.toJSONString(webLog));
// 数据库操作
jdbcTemplate.update("insert into web_log(err_info) values(?)", webLog.getErrInfo());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
@PostMapping("/addWebLog")
public Mono<Void> addWebLog(WebLog webLog) {
// 到达capacity则抛出异常
try {
queue.add(webLog);
} catch (IllegalStateException e) {
System.out.println("队列达到上限:" + queue.size());
}
return Mono.empty();
}
public class WebLog {
private String errInfo;
public String getErrInfo() {
return errInfo;
}
public void setErrInfo(String errInfo) {
this.errInfo = errInfo;
}
}
}
为了防止某个功能有bug、大批量出错造成服务器压力,这里添加了一个队列来异步处理请求。Controller收到请求只是将请求丢到队列里,另外一个线程中队列在一个一个取出请求进行处理。队列长度为1000,超过1000的请求直接丢弃。
结语
微信虽然提供了小程序实时日志
其实微信提供了一种错误查询,在微信公众平台小程序可以查看到,但是和小程序实时日志一样有点不好用,我感觉这俩都是对错误进行采样,不是抓取全部错误。例如:开头我描述的那个错误就没找到。
还是自己动手,重写Page函数,catch所有异常,将错误上传给后台比较好排查问题。