客户端调试日志编码之道

本文约 1300 字,阅读需 3 分钟。

日志分很多种了,这里针对的是客户端的、开发调试用的日志,为什么要加这两个限制呢?

  1. 后台日志都在服务器,开发容易操作,客户端日志大部分在本机,有的测试甚至不会捞日志,开发想帮忙都鞭长莫及

  2. 开发调试的日志,其目的是辅助开发,不同于正式环境的日志

当然了,相信下面的内容对其他场景也有启发。

什么时候打日志

任何时候都应该打日志,很多人喜欢Debug按钮调试,效率不说,很多场景是无法调试的:

  1. App的启动过程,你可能都来不及attach

  2. 多线程、多进程应用,本身就和Debug这种固定在单一时间、空间的模式违背

  3. 存在未知Crash,直接从Debug处闪退了

  4. 等等

一开始你觉得Debug更顺手,更多是因为你的日志打得不够好,就好像一开始我们学习C语言的时候,也不会单步调试,用的是printf打印中间结果,这其实就是一种朴素的日志思想。

下文主要讨论,我踩过的坑以及我心中好的日志。

保持齐整

一个模块的日志最重要的就是齐齐整整。

摘录几个项目里面的日志(不算什么机密代码吧~):

//1
if (Global.ASSISTANT_DEBUG) {
    XLog.d("ResourcePreloader", "[ResourcePreloader][onDownloaded]: taskId=" + taskId + ", ResourceInfo=" + downloadedRes);
}

//2
if (Global.ASSISTANT_DEBUG) XLog.d(TAG,"onDestroy");

//3
XLog.d("harry", "qrcode: " + qrcode);

可以看到,3个日志的TAG各具特色,有的业务名、有的是TAG(一般是类名)、有的是名字,然后第一个的日志看起来带了格式,第三个也有点,第二个没有,就是提示作用。

说实话,第一个日志的格式,对于grep之类的文本处理工具其实不算友好,写起来也不能充分利用编辑器的补全功能。

所以,第一个建议就是:日志信息要有自己的格式,不要直接塞字符串,例如:

class VLog {
    String author; // 打印人
    String module; // 业务模块
    HashMap<String, String> info;
    HashMap<String, String> desc;
}

那打印的类、打印的时间呢?这些其实系统自带的,不需要多此一举。当然,也可以为了避免内部匿名类、混淆的干扰加上一个字段。

关键节点

日志的目的是辅助定位问题,所以好的日志应该是信息充分的。一般来说,主要关注两个问题:

  1. 分叉节点前的关键值。比如某个条件判断之前打印相关变量,就能分析进入非预期分支的原因类。

  2. 收敛节点后的关键值。比如App首页可能有闪屏、直接进入、外部Call起、Push、其他页面等多个入口,这时候最后打印出前几层的调用路径(stacktrace)。

存至本地

一开始的时候,我的日志都很奔放,自己都是adb logcat+ grep,再凌乱的日志也能过滤的恰到好处,然后联调测试的时候,就会发现其他开发根本不会logcat,很多测试外包还在用Eclipse的logcat,更别说grep了。导致别人要么什么日志都拿不出、要么几秒钟的操作,给你一个几十M的日志文件。

真的要换位思考,没有人把熟练掌握logcat等命令行工具和正则表达式当作理所当然。

后面打算开发一个工具类,一方面做日志的格式化,另一方面打印的同时写一份到本地,尤其是遇到一些偶现问题时,对于保留现场十分有帮助。

同时这个日志工具类有类似logcat的正则过滤功能,因为项目的日志会越来越膨胀,但每次关心的其实是某一小块的日志。

有机整体

前面说的,其实是一个有机的整体,只有格式齐整了,才能很好地过滤;只要关键节点覆盖到了,才能辅助解决问题,切忌本末倒置,为了打日志而打日志。

暂时想到这么多,以后再做补充。

总阅读量次。