最近工作有点忙碌,遇到了两次莫名不知如何解决的错误,由此暴露的问题让人不禁反思:
好的分析排查习惯比问题本身更值得关注。
首先是前天晚上遇到的一个问题是这样的:
我需要定时去从redis的zset里面取得一些key,然后查询数据库,得到一些原始数据,再通过外部的一些webservice去发送微信消息,通知到相关用户。由于取出来的key可能是多个,所以在最外层我是包了一层for循环,整个代码都是promise化的异步(nodejs技术栈)
在发送微信消息之前的步骤里面我每个关键部分都有log输出,然而最后还是遇到了报错,运行到调用webservice发送请求之后,脚本无输出了很长一段时间,然后打印出了out of memory的js stack信息。我最开始的思路是去google查询相关错误信息的解决办法,然而这种堆栈报错更多是因为循环回调太深造成内存耗尽,一般出现在数据量较大的循环调用中,而我在测试的时候只有一条数据,应该不是这个表面造成的原因。
按照一般的思路,我开始增加更多的日志,一段一段去屏蔽代码,在最后一次输出正确之后的代码往往是问题所在。而那一段是使用superagent发送post请求的代码块,我单独屏蔽之后发生没有报错,于是我单独写了一个测试脚本來运行这段post请求,然而并没有报错。走到这里我开始感觉郁闷和无奈,到底错在哪里?
后面去请教后端老大,他首先格式化了一下我的代码,让代码更整洁(由于使用的是简单ide,写多了就会有些乱)。之后他提醒我在每一段promise段中增加catch代码,因为我嵌套了多个promise对象操作,而有一部分并没有最终return出来,所以有些error可能没有catch到。
在增加这些异常捕获之后还是没发现问题,之后关注重点就回到post请求,先修改了返回延迟,然后终于打印除了response,最终发现了问题所在是因为post的数据不符合对方接口要求,而因为请求容忍时间太长了,导致了内存异常不能暴露出真正问题。
其实我已经确认了问题所在,但没有继续深挖以及完善日志记录。而且在重现问题写单独脚本的时候,我并没有使用相同的数据作为测试,而是写死了一个测试数据,最终没能暴露出真相。
还原现场的前提是:
数据、条件、逻辑三者保持完全一致。
第二个遇到的问题是:
突然前端同学找到我,说某单独部署的项目出现了不能正常生成一些数据,每次都报:Not a string or buffer的错误。首先我拿到这个问题也是去理清逻辑和增加必要log,然而那段报错信息太少了,我最终也排查到了错误段,又一次遇到了和之前那个问他一样无法更进一步的窘境。
最终也是在老大的协助下,通过使用console.error(e|| e.stack) 打印出了堆栈错误信息,追踪定位到了出错的文件和文件行,发现是由于配置文件造成无法加密出salt,造成TypeError报错。
总结:调试的思路和方法需要多注意,catch中多使用console.error打印堆栈信息而不是简单的Log.感谢团队的同事以及老大耐心地帮助,我也会好好反思,提高自己。