2. logcat基础使用:adb logcat命令详解、日志级别、过滤与格式控制
好,咱们正式开始聊logcat。说实话,这玩意儿是Android开发调试的“第一把交椅”。我见过不少新人,一上来就只会 adb logcat 然后看花眼,其实根本不知道自己在看什么。今天我就把logcat的底裤给你扒干净。
2.1 adb logcat 命令详解
先说说最基本的。你连上设备,敲下 adb logcat,终端就开始疯狂刷屏。嗯,这时候你看到的其实是系统所有进程的日志流。为什么说“所有”?因为logcat默认不区分进程,也不区分标签,一股脑全给你吐出来。
我个人习惯,从来不会直接裸敲 adb logcat。为啥?信息量太大,根本没法看。你想想看,一个跑着的手机,每秒可能产生几百条日志,你盯着屏幕找自己的那条,跟大海捞针没区别。
所以,咱们得学会加参数。常用的几个:
adb logcat -c:清空日志缓冲区。我经常在开始一轮测试前先清一下,避免被旧日志干扰。adb logcat -d:打印当前缓冲区所有日志,然后退出。适合抓取一段快照。adb logcat -s:静默模式,只显示你指定的标签。这个后面细说。adb logcat -v <format>:控制日志的输出格式。这个很重要,咱们单独讲。adb logcat -b <buffer>:指定查看哪个缓冲区。默认是main,还有system、events、crash等。
adb logcat -c 清空一下。曾经有一次我排查一个ANR问题,被一条半小时前的旧日志误导了整整一下午。从那以后,清空缓冲区成了我的肌肉记忆。
2.2 日志级别:V/D/I/W/E/F
Android的日志分6个级别,从低到高分别是:
| 级别 | 缩写 | 含义 | 使用场景 |
|---|---|---|---|
| Verbose | V | 啰嗦信息 | 开发调试时最细粒度的日志,发布版本通常不输出 |
| Debug | D | 调试信息 | 开发阶段用于跟踪变量、流程等 |
| Info | I | 普通信息 | 记录关键节点、状态变化等 |
| Warning | W | 警告信息 | 可能有问题但不影响运行的情况 |
| Error | E | 错误信息 | 发生了错误,功能可能受影响 |
| Fatal | F | 致命错误 | 非常严重的错误,通常会导致进程崩溃 |
你可能会问:这些级别怎么用?说白了,就是过滤。比如我只想看错误和致命错误,可以这样:
adb logcat *:E
这个 *:E 的意思是“所有标签,只显示E级别及以上的日志”。同理,*:W 就是W及以上(W/E/F)。
*:W,它会显示W、E、F三个级别。不会显示V和D。我曾经有个同事,调试时用了 *:W 然后说“怎么没有D级别的日志?”,嗯,这就是没搞懂包含关系。
2.3 过滤特定标签与优先级
光有级别过滤还不够。你想想看,一个系统里可能有几百个标签,比如 ActivityManager、WindowManager、SurfaceFlinger……你只想看自己App的日志,怎么办?
用标签过滤。假设你的App的TAG是 MyApp:
adb logcat -s MyApp
这个 -s 参数是“静默模式”,它只显示你指定的标签。等价于:
adb logcat MyApp:V *:S
解释一下:MyApp:V 表示MyApp这个标签显示V及以上所有级别,*:S 表示其他所有标签都静默(不显示)。
更灵活的组合:
adb logcat MyApp:D *:S
这样只显示MyApp的D级别及以上日志。其他标签一概不显示。
还可以同时过滤多个标签:
adb logcat MyApp:W SystemServer:E *:S
这个命令的意思是:显示MyApp的W及以上,以及SystemServer的E及以上,其他全部静默。
标签:级别,多个过滤条件用空格隔开,最后用 *:S 关掉其他所有日志。这是我最常用的调试套路,没有之一。
2.4 日志格式控制
默认的logcat输出格式是这样的:
08-15 14:23:45.678 1234 5678 D MyApp: This is a debug message
但有时候这个格式不够用。比如你想看线程ID,或者想看精确到纳秒的时间戳。这时候就需要 -v 参数了。
常用的格式有:
| 格式 | 说明 | 示例输出 |
|---|---|---|
brief |
默认格式,显示优先级/标签(进程ID): 消息 | D/MyApp(1234): msg |
process |
只显示进程ID | D(1234) msg |
tag |
只显示优先级和标签 | D/MyApp: msg |
thread |
显示进程ID和线程ID | D(1234:5678) msg |
threadtime |
显示日期、时间、进程ID、线程ID | 08-15 14:23:45.678 1234 5678 D MyApp: msg |
long |
显示所有元数据,包括日志行号 | 包含微秒、标签、行号等详细信息 |
我个人最常用的是 threadtime。为什么?因为它同时显示了进程ID和线程ID。在排查多线程问题或者跨进程通信时,这两个ID太关键了。比如:
adb logcat -v threadtime -s MyApp
这样输出就是:
08-15 14:23:45.678 1234 5678 D MyApp: 开始加载数据
08-15 14:23:45.680 1234 5678 D MyApp: 数据加载完成
08-15 14:23:45.681 1234 5678 D MyApp: 开始渲染
你看,时间精确到毫秒,进程ID和线程ID一目了然。如果某个线程卡住了,你一眼就能看出来。
-v threadtime,排查性能问题用 -v long(能看到微秒级时间戳)。曾经有一次定位一个竞态条件,就是靠 long 格式下的微秒时间戳才找到两个线程的执行顺序问题。
还有一种格式叫 color,可以给不同级别的日志上色。不过这个需要终端支持,我一般不用,因为抓取日志到文件时颜色就没了。
2.5 组合使用:实战套路
说了这么多,咱们来一个实战组合。假设我要调试一个App的启动流程,只看MyApp标签的D级别及以上日志,并且要带线程信息:
adb logcat -c
adb logcat -v threadtime MyApp:D *:S
第一行清空旧日志,第二行开始实时过滤。这样终端里只会出现MyApp的调试信息,干净利落。
如果你想同时看多个标签,比如MyApp和SystemServer的错误:
adb logcat -v threadtime MyApp:D SystemServer:E *:S
嗯,这就是我日常调试的标配。你想想看,如果不懂这些过滤技巧,面对几千行日志,你根本无从下手。
adb logcat -s MyApp 结果什么日志都没有。折腾了半天才发现,代码里打印日志用的TAG是 myapp(小写),而我过滤的是 MyApp(大写)。logcat的标签过滤是大小写敏感的!从那以后,我每次都会先确认一下TAG的准确写法。
好了,logcat的基础使用就这些。记住:不加过滤的logcat就是耍流氓。下一节咱们聊聊怎么把日志保存到文件,以及如何用grep做更高级的过滤。