日志分析基础:logcat的buffer分类与高级用法
做Android稳定性排查,说白了就是跟日志打交道。我见过太多工程师,一上来就 adb logcat 然后盯着屏幕看,其实这样效率很低。你得先搞清楚日志存在哪、怎么取、怎么筛。
今天我们就聊聊logcat的buffer分类、命令行高级用法,以及grep和正则的实战技巧。这些都是我每天吃饭的家伙。
一、logcat的四大buffer分类
Android的日志系统不是一个大池子,而是分了几个独立的缓冲区。为什么要这么设计?你想想看,如果所有日志都混在一起,系统崩溃的信息可能被App的调试日志刷掉,那就麻烦了。
| Buffer名称 | 存储内容 | 典型场景 |
|---|---|---|
| main | 应用层日志(Log.i/d/w/e) | App崩溃、ANR时的应用日志 |
| system | 系统服务日志 | AMS、WMS、PMS等系统服务异常 |
| events | 二进制格式的系统事件 | ANR、crash、boot事件、电量统计 |
| crash | Native崩溃日志 | libc crash、signal 11/7等 |
我个人习惯,排查稳定性问题时,先看events buffer。为什么?因为events buffer里记录了系统关键事件的"时间线",比如什么时候发生ANR、什么时候进程被杀、什么时候系统重启。这些信息在main和system里反而不容易找到。
实战经验:有一次客户反馈手机频繁重启,我拉了一天的logcat,main buffer里全是第三方App的垃圾日志。后来我改用 logcat -b events -b crash 同时抓取事件和崩溃日志,5分钟就定位到了是某个Native库的内存越界导致system_server崩溃。
二、logcat命令行高级用法
很多人只会 adb logcat,其实它的参数组合起来非常强大。我整理了几个我常用的"组合拳":
2.1 多buffer同时抓取
# 同时抓取main和system,排除events(events是二进制,可读性差)
adb logcat -b main -b system -v threadtime
# 抓取所有buffer(不推荐,数据量太大)
adb logcat -b all
2.2 按日志等级过滤
# 只显示WARNING及以上级别
adb logcat *:W
# 只看某个TAG的ERROR日志
adb logcat -s "ActivityManager:E"
# 组合:只看system buffer的WARNING以上
adb logcat -b system *:W
2.3 时间戳与进程过滤
# 显示线程时间、进程ID、TAG(我最常用的格式)
adb logcat -v threadtime
# 只看某个PID的日志(比如system_server的PID是1234)
adb logcat --pid=1234 -v threadtime
# 按时间范围过滤(抓取最近10秒的日志)
adb logcat -t 10
我的小技巧:排查ANR时,我会用 logcat -b events -v threadtime | grep "am_anr"。events buffer里有一个叫 am_anr 的事件,每次ANR都会记录。这样你就不用翻几千行日志去找ANR发生的时间点了。
三、grep与正则表达式实战
日志分析的核心能力,其实就是"在垃圾堆里找金子"。grep和正则就是你的筛子。
3.1 基础grep用法
# 查找包含"FATAL"的行
adb logcat | grep "FATAL"
# 忽略大小写
adb logcat | grep -i "error"
# 显示匹配行的前后5行(看上下文)
adb logcat | grep -C 5 "native_crash"
# 显示匹配行的后10行(看后续)
adb logcat | grep -A 10 "signal 11"
3.2 正则表达式实战
正则才是真正拉开差距的地方。我遇到过很多同事,日志就在眼前,但就是找不到关键信息,因为不会写正则。
# 匹配所有以"E/"开头的行(ERROR级别日志)
adb logcat | grep "^E/"
# 匹配包含PID为1234或5678的行
adb logcat | grep -E "pid-1234|pid-5678"
# 匹配时间戳在10:00:00到10:59:59之间的日志
adb logcat -v threadtime | grep -E "10:[0-5][0-9]:[0-5][0-9]"
避坑指南:我曾经在排查一个内存泄漏问题时,用 grep "OutOfMemory" 搜了半天没结果。后来才发现,日志里写的是 Out of memory(带空格)。所以,写正则时一定要考虑日志的实际格式,最好先用 cat 看一眼原始数据。
3.3 高级组合:多条件过滤
# 查找system_server进程中包含"ANR"或"crash"的日志
adb logcat -b system --pid=$(pidof system_server) | grep -E "ANR|crash"
# 排除包含"debug"和"verbose"的行(减少噪音)
adb logcat | grep -v -E "debug|verbose" | grep "error"
四、实战案例:快速定位一次Native Crash
假设你接到一个bug:手机在打开相机时闪退。你手头只有一份logcat。怎么快速定位?
- 第一步:看events buffer
找到crash发生的时间点,比如adb logcat -b events -v threadtime | grep -E "am_crash|am_anr"10:15:30.123 - 第二步:看crash buffer
查看crash buffer里记录的signal信息和backtraceadb logcat -b crash -v threadtime -t "10:15:30" - 第三步:确认进程和库
如果看到
signal 11 (SIGSEGV),并且backtrace指向libcamera.so,那基本可以确定是相机HAL层的Native代码有问题。
总结一下我的排查思路:先events找时间线,再crash看具体崩溃信息,最后用main和system确认上下文。这个流程我用了快十年,从来没失手过。
五、一些实用的小技巧
- 日志太长怎么办?用
logcat -d把日志dump到文件,然后用less或vim打开,配合/搜索关键词。 - 怎么清空buffer?
adb logcat -c,在复现bug前清空一次,避免历史日志干扰。 - 日志格式太乱?用
-v brief或-v process切换格式,我一般用threadtime,信息最全。
嗯,今天就先聊这么多。日志分析是稳定性排查的"基本功",但基本功往往最容易被忽视。下次遇到问题,别急着翻代码,先看看日志里有没有线索。很多时候,答案就在logcat里,只是你没找对地方。