Android 日志抓取与 adb logcat 排查实战
刚接手一个测试任务时被测应用在同事的机器上启动两秒就退我打开日志窗口满屏都是系统层刷出来的 GC 和 SurfaceFlinger 输出真正的错误信息被埋在第 400 多行。那次之后我养成了一个习惯先把日志抓取链路捋顺再谈复现。安卓设备上查看和抓取应用日志这件事看起来就是一条 adb logcat 命令的事但真到排查现场环境差异、缓冲区策略、过滤写法、日志类型选择每一步都能决定你能不能在有限的时间里捞到那几行关键信息。这篇内容面向的是刚接触 Android 测试的同学以及做了几年但一直靠复制粘贴网上命令过日子的开发者我会把日志的产生来源、有线与无线两种抓取路径、测试阶段常用的工具组合以及我踩过的坑一条条拆开讲清楚命令全部可以照抄原理部分尽量说明白为什么这么写。1. 日志到底从哪儿来先搞清楚 Android 日志的分层结构很多人上手就敲adb logcat抓到一堆东西却不知道这些东西分属不同的缓冲区、由不同的进程写入。搞清楚分层后面的过滤和排查才有方向感。1.1 Logcat、内核日志、崩溃转储是三个不同的东西Android 的日志体系大致可以分成四类来源日志类型产生方典型查看方式典型用途应用日志应用进程调用 android.util.Logadb logcat业务埋点、状态流转、异常捕获系统事件日志system_server、各系统服务adb logcat -b events、adb logcat -b system生命周期切换、Activity 启动耗时内核日志Linux 内核adb shell dmesg驱动异常、内存不足被杀、OOM崩溃转储debuggerdtombstone 文件、/data/anr/native 崩溃、ANR 堆栈日常说的抓日志九成指的是第一类。但一个应用闪退你只看应用日志往往看不出原因——如果是 native 层崩溃堆栈会落到 tombstone如果是被系统杀进程线索在dmesg和dumpsys activity exit-info里如果是主线程卡死触发 ANR要去看/data/anr/traces.txt。我刚入行时最典型的一次误判就是把一个 native 崩溃当成 Java 异常找了半天因为应用日志最后一行只有一个Fatal signal 11相关的提示真正的堆栈压根不在 logcat 的 main 缓冲区。logcat 本身还有多个缓冲区常用的有main应用日志、system系统服务、crash崩溃、events结构化事件、radio通信相关。默认只读 main、system、crash 这三块想看全部要显式指定-b all。1.2 为什么同一条日志在不同设备上表现不一样这是新手最困惑的一点同一个 APK在 A 手机上有完整日志装到 B 手机上啥也没有。常见原因有这么几个。第一是缓冲区大小不同。logcat 的环形缓冲区有容量上限默认每块 256KB 到 2MB 不等厂商定制 ROM 会改这个值。应用疯狂打印日志时早期内容会被滚动覆盖。用adb logcat -g可以看当前各缓冲区大小用adb logcat -G 16M可以临时调大需要 root 或设备的 logd 权限允许。第二是release 包把日志关掉了。不少团队会用 ProGuard/R8 在打包时剔除Log.d调用或者通过一个开关变量统一降级日志级别。测试前务必要一份带日志的构建别拿着一份正式包在那边死磕。第三是厂商对 logd 做了限制。部分定制系统会限制非系统应用写日志的频率或者对第三方应用屏蔽 system 缓冲区。遇到这种情况只能换设备或者在系统设置里找开发者选项中的日志相关开关。第四是日志级别过滤。命令行默认输出 Verbose 以上所有级别但 Android Studio 面板默认可能只显示 Info 以上看起来就像日志少了。理解这四点你就不会再把日志抓不到当成玄学问题。2. 有线抓日志adb logcat 的完整玩法数据线直连是最稳的方式没有网络抖动也不会因为设备休眠断流。下面这一整套命令是我这些年用得最频繁的部分。2.1 环境准备与设备连接确认先把 ADB 环境弄好。可以直接装 Android Studio自带 platform-tools也可以只下 platform-tools 压缩包解压后把目录加进 PATH。# 确认设备已连接且授权 adb devices -l # 输出示例 # List of devices attached # 1A2B3C4D device product:xxx model:xxx device:xxx transport_id:1 # 如果显示 unauthorized需要在设备上点允许 USB 调试 # 如果显示 offline尝试重启 adb 服务 adb kill-server adb start-server # 查看设备系统版本决定后续能用哪些参数 adb shell getprop ro.build.version.release adb shell getprop ro.build.version.sdk注意-l参数会输出机型、产品名这些信息多设备同时连接时非常必要因为后续所有命令都要用-s 序列号指定目标设备否则 adb 会直接报错more than one device。2.2 过滤才是核心按 tag、PID、优先级筛日志原始 logcat 输出没法看必须过滤。我常用的过滤维度是四种按精确度从低到高排列。按优先级过滤VVerbose、DDebug、IInfo、WWarn、EError、FFatal、SSilent。# 只看 Error 及以上 adb logcat *:E # 应用日志只看 Debug 以上系统日志只看 Warn 以上 adb logcat *:D system:*:W按 tag 过滤-s参数等价于把其他 tag 设为 Silent。adb logcat -s MyApp:V NetworkLayer:D按进程 PID 过滤这是最干净的方式直接把范围锁定到你的应用进程。# 获取应用 PID需要 Android 7.0 及以上支持 pidof adb shell pidof -s com.example.myapp # 直接按 PID 过滤一条命令搞定 adb logcat --pid$(adb shell pidof -s com.example.myapp)进程重启后 PID 会变这条命令抓不到重启之后的日志。如果测试的是杀进程再启动这种场景我更推荐用包名过滤新版平台工具支持adb logcat --pid$(adb shell pidof -s com.example.myapp) -v threadtime或者用应用包名匹配部分平台工具版本已支持adb logcat -v threadtime | grep --line-buffered com.example.myapp按关键字正则过滤Android 10 以上版本 logcat 自带-e参数支持正则比外挂 grep 更省事而且不会因为 grep 缓冲导致日志延迟。adb logcat -e NullPointerException|Timeout|FATAL # 忽略大小写 adb logcat -e (?i)exception我个人的建议是定位具体问题时用--pid加-v threadtime做长期监控时用-e正则加文件输出。前者精准后者不漏。2.3 输出到文件两种写法的取舍抓日志几乎都要落盘方便事后翻查和提交给开发。有两种思路。第一种是PC 端重定向adb logcat -v threadtime app_log.txt缺点是终端缓冲区有限长时间抓取时如果终端卡顿可能丢行而且 Windows 下 PowerShell 的重定向编码默认是 UTF-16LE开发拿到文件会看到一堆乱码。我在 Windows 上更习惯用adb logcat -v threadtime | Out-File -Encoding utf8 app_log.txt或者干脆切到 Git Bash 里跑。第二种是设备端写文件adb logcat -v threadtime -f /sdcard/app_log.txt -r 4096 -n 10-r 4096表示每个文件写到 4096KB 就滚动-n 10表示最多保留 10 个文件总共约 40MB。这种方式不依赖 PC 端缓冲长时间压测时非常稳。抓完再拉下来adb pull /sdcard/app_log.txt注意设备端写文件会占用存储空间压测前先确认剩余空间同时记得跑完删掉避免残留文件影响下次测试。2.4 缓冲区与时间戳两个最容易翻车的地方缓冲区覆盖做长时间稳定性测试时如果日志量巨大环形缓冲区很快被覆盖。我曾经跑了一个 8 小时的 monkey回头一看最早两小时的日志全没了。解决办法一是用-f直接落盘二是用-G临时调大缓冲区三是在测试脚本里分时段切割输出文件。时间戳与时区-v time输出的是设备本地时间如果设备和 PC 时区不一致你拿日志对操作步骤就会错位。三种格式化参数各有用途-v time01-15 10:23:45.123人类可读适合人工比对-v threadtime多了一个 TID 线程号排查多线程问题必用-v epochUnix 时间戳毫秒适合脚本解析和日志对齐排查偶现问题时我一般会让测试同学在操作时记下手机上的当前时间精确到秒然后用-v time抓误差控制在几秒内就能定位。3. 没有数据线怎么办设备端与无线方式的日志导出不是所有场景都能插线比如兼容性测试要拿着手机来回走动或者被测设备只有一个 Type-C 口还要接外设。3.1 开发者选项里的错误报告Android 系统自带的错误报告Bug report功能能从设备设置里直接生成一份完整诊断包。路径一般在设置 → 系统 → 开发者选项 → 错误报告或者在设置 → 关于手机里连点版本号进开发者选项后找到。这份报告里包含 logcat 全量日志、dumpsys 快照、电池统计、ANR 记录等等信息量非常大。缺点是生成过程要等一两分钟而且文件很大几十 MB打开需要专门的解析工具。我的用法是偶现问题、现场无法连电脑时先抓一份 bug report 存着带回来再慢慢挖。命令行也可以直接生成adb bugreport ./bugreport.zip解压后重点看bugreport-*.txt里的SYSTEM LOG、VM TRACES、EVENT LOG这几段。3.2 无线调试的正确配对顺序Android 11 起系统内置了无线调试不需要额外工具。顺序很重要很多人卡在配对上就是因为跳步了。手机和电脑连同一个局域网手机开发者选项里打开无线调试点进使用配对码配对设备记下屏幕上的 IP、端口和六位配对码电脑上执行配对命令注意这里的端口是配对端口不是无线调试主端口adb pair 192.168.1.100:37123 # 提示输入配对码输入屏幕上的六位数 # 配对成功后再用无线调试主页面上显示的端口连接 adb connect 192.168.1.100:37000 # 确认 adb devices注意配对端口每次进入配对界面都会变主连接端口在关闭无线调试前基本不变。配对成功后再执行adb connect时如果用的还是配对端口会一直连不上。无线连接的问题是稳定性受网络影响大日志量传输时容易断。我的经验是无线只用于看需要长时间抓取落盘时还是插线。另外部分设备锁屏后会关闭无线调试测试时记得把保持唤醒打开。3.3 设备上直接看日志的小工具有些场景需要现场直接确认比如外场测试时没有电脑。这种时候可以在设备上装一个日志查看应用常见的做法是读取系统日志并提供关键字过滤和保存导出功能。这类工具适合快速看一眼错误信息不适合长时间抓取因为手机端过滤能力弱、耗电明显而且部分工具需要设备有特殊权限才能读到完整缓冲区。需要屏幕和日志同时留证的场景我一般会配合录屏adb shell screenrecord --time-limit 180 --bit-rate 8M /sdcard/record.mp4三分钟一段配合日志时间戳事后能把画面和日志对齐。4. 测试阶段真正好用的工具组合命令只是底座日常测试真正提效的是工具链。下面这几个是我在不同阶段反复使用的组合。4.1 Android Studio 的 Logcat 面板新版 Android StudioFlamingo 之后的 Logcat 面板重构过一次体验提升很明显。它支持结构化查询语法比命令行直观得多。常用的查询写法package:mine level:ERROR package:mine tag:NetworkLayer package:mine message:NullPointerException package:com.example.myapp level:WARNpackage:mine表示只显示当前工程对应的应用这个功能在多模块工程里非常省事。面板还支持按 tag 分色、按进程折叠、保存过滤条件为快捷方式我一般会针对网络数据库UI各存一套过滤条件切换排查主题时一键调出来。它的一个隐藏好处是会自动关联崩溃和 ANR 的堆栈点击堆栈行能跳到源码对应位置比手动对着日志文件找行号快太多。4.2 稳定性与性能monkey、Perfetto、dumpsysMonkey是做稳定性压测的老牌工具随机事件流能覆盖大量边界场景。adb shell monkey -p com.example.myapp \ --throttle 300 \ --pct-touch 40 --pct-motion 25 --pct-syskeys 5 \ --ignore-crashes --ignore-timeouts --ignore-security-exceptions \ -v -v 20000 monkey_log.txt 21几个参数的实际意义要讲清楚--throttle 300是每次事件间隔 300ms太快会让应用一直处于被打断状态反而测不出真实问题--ignore-crashes让 monkey 在崩溃后继续跑适合长跑但排查问题时一定要去掉这个参数否则崩溃点会被淹没在后续事件流里。跑 monkey 必须同时抓 logcat否则事后只能看到某次事件触发崩溃这种无用的结论。Perfetto是现在做性能追踪的主流方案取代了以前的 systrace。# 采集 10 秒的通用性能数据 adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace \ -t 10s sched freq idle am wm gfx view binder_driver adb pull /data/misc/perfetto-traces/trace.perfetto-trace拉下来的文件拖进浏览器里的 Perfetto UI 就能看主线程阻塞、帧率掉点、Binder 调用耗时一目了然。日志解决发生了什么Perfetto 解决为什么慢两者配合才是完整的排查手段。dumpsys用来补日志看不到的信息几个高频子命令# 应用退出原因Android 11 以上非常好用 adb shell dumpsys activity exit-info com.example.myapp # 内存状态 adb shell dumpsys meminfo com.example.myapp # 网络连接状态 adb shell dumpsys connectivity # 最近一次的 ANR 记录 adb shell dumpsys dropbox --print | grep -A 50 anrexit-info这个命令值得单独说它能告诉你是进程自己主动退出、被系统 lowmemorykiller 杀掉还是因异常崩溃直接省掉大量猜测时间。4.3 自动化链路UiAutomator、Appium 与脚本化抓取做回归测试时日志抓取最好和自动化脚本绑定。我常用的一种轻量方案是纯 shell 脚本编排#!/bin/bash PKGcom.example.myapp TIMESTAMP$(date %Y%m%d_%H%M%S) OUTDIR./logs/$TIMESTAMP mkdir -p $OUTDIR # 清空旧日志保证这次是干净的 adb logcat -c # 后台抓取 adb logcat -v threadtime $OUTDIR/logcat.txt LOGPID$! # 启动应用并执行用例 adb shell am start -n com.example.myapp/.MainActivity sleep 30 # 结束抓取 kill $LOGPID adb shell dumpsys activity exit-info $PKG $OUTDIR/exit_info.txt如果是用 Appium 或 UiAutomator 搭的框架可以在每个用例结束时调用driver.getLogs()类似接口把日志挂到测试报告上这样失败用例自带上下文不用再回头翻大文件。Espresso 则是另一种思路它在进程内运行能直接断言日志或捕获异常适合做单元级和组件级验证。4.4 抓包与日志的配合边界有些问题日志里看不出所以然比如接口返回了错误码但应用处理逻辑有问题这时需要抓包配合。这里要说清楚适用范围只对你自己开发或有明确授权的应用做调试抓包用于定位自己产品的网络行为。常见的做法是通过系统代理配合应用自身的调试配置让应用在 Debug 构建下信任本地代理证书。Android 7.0 之后应用默认不信任用户安装的证书需要在network_security_config.xml里显式配置 Debug 覆盖。如果你们团队没做这个配置抓包会一直失败现象就是走代理后所有请求直接报连接错误——这属于工程配置问题不是网络问题。抓包拿到的是请求响应日志拿到的是应用内部状态把两者的时间戳对齐才能看出请求成功但界面没刷新这类问题的真正断点在哪。5. 三类高频场景的完整排查链路工具和命令说完了下面用三个真实场景把流程串起来这部分是我觉得对新手最有价值的内容。5.1 启动即闪退日志里只有一行遇到这种情况按下面的顺序走第一步确认是不是 native 崩溃。抓日志时同时看 crash 缓冲区adb logcat -b crash -b main -v threadtime如果看到Fatal signal、backtrace、tombstone这类关键字就是 native 层问题堆栈会在 tombstone 文件里需要通过 bugreport 获取。第二步如果是 Java 异常搜索AndroidRuntime这个 tagadb logcat -s AndroidRuntime:E绝大多数未捕获异常的堆栈都从这里出来。第三步如果连 AndroidRuntime 都没有用退出原因排查adb shell dumpsys activity exit-info com.example.myapp有可能是被系统杀掉了比如内存不足或者应用被安全策略拦截。这种情况下日志里干干净净看起来什么都没发生其实原因在系统层。我踩过最深的一次坑是一个只在特定机型上复现的闪退日志里只有一行Process is dead。最后是靠 exit-info 里显示的REASON_LOW_MEMORY才定位到原因是我们的图片缓存策略在低内存设备上没有及时释放。这个问题靠看应用日志是永远找不出来的。5.2 日志刷屏怎么从噪音里捞出关键行日志刷屏一般有两种来源应用自己的无节制打印以及系统层的 GC、渲染、Binder 输出。处理思路是先做减法再做定位# 过滤掉常见噪音 tag adb logcat -v threadtime | grep -v -E dalvikvm|art|GC|SurfaceFlinger|Choreographer|OpenGLRenderer # 或者反过来只保留自己的 tag 前缀 adb logcat -v threadtime | grep -E MyApp|Network|Crash必须提醒一点用管道 grep 时一定要加--line-buffered否则 grep 会做块缓冲日志不是实时出现的排查实时问题时你会误以为某条日志没打印。这个坑我至少踩过三次每次都是怀疑代码逻辑最后发现是缓冲问题。另外长期看应该推动开发团队做日志规范化统一 tag 前缀、区分级别、Debug 日志在 release 包里自动降级。测试同学如果能提出这套规范并被采纳后面的排查效率会成倍提升。5.3 日志对不上复现步骤现象是测试说点这个按钮就崩溃你抓的日志里却什么都没打印。常见原因有三个。第一是日志时机问题。有些异常在子线程里被捕获后静默处理了日志在另一个进程或另一个缓冲区。这时候用-b all全量抓一次。第二是时间错位。设备时间与服务器或 PC 不一致导致你看的日志时间段和实际操作时间差了。建议测试前统一校时或者直接用-v epoch抓取事后按时间戳筛选。第三是进程重启。崩溃后应用自动重启新的 PID 覆盖了旧日志的观感。用--pid抓日志时特别容易遇到因为进程一变过滤就失效了。这种情况改用包名或 tag 过滤更稳妥。6. 这些年攒下来的几条实操经验说几条文档里不太会写、但实际工作中很救命的东西。给日志文件起个能被检索的名字。我见过太多团队里堆着log.txt、log(1).txt、新建文本文档.txt。建议命名格式统一成包名_机型_场景_日期时间.txt比如myapp_Pixel6_launchcrash_20250115_1430.txt。半年后回头看这个习惯能救你半天时间。抓日志前先清缓冲。adb logcat -c一行命令能避免你在几千行历史日志里翻找本次操作。但要注意清缓冲会丢掉之前的日志如果问题已经发生了千万别清先把现有数据 pull 出来。给测试设备固定一套标准环境。把常用命令写成一个.bat或.sh脚本比如清缓冲 按包名抓取 落盘到固定目录团队共享。我见过效率差距巨大的两个团队差别就在于一个是每次都现敲命令另一个是双击脚本。提交缺陷时附日志片段而不是整个文件。截取错误前后各 30 秒的范围标出关键行开发的处理速度快得多。整份几十 MB 的日志贴到缺陷系统里多数时候只会被忽略。注意日志里的敏感信息。有些应用会把用户标识、手机号、地址这类内容直接打到日志里。测试过程中拿到的日志属于敏感数据传播范围要控制缺陷单里提交前最好做脱敏处理。这既是合规要求也是职业素养。别迷信单一工具。logcat 看状态、Perfetto 看性能、dumpsys 看系统决策、bugreport 看全貌各有各的边界。我现在的习惯是遇到复杂问题先把四样都抓一份再坐下来对比分析比来回补抓效率高得多。最后分享一个我最近才用顺手的小技巧Android Studio 的 Logcat 面板支持把当前过滤条件导出成快捷方式我按崩溃网络数据库生命周期做了四套切来切去只要点一下。配合命令行那边的--pid实时抓取一套看历史一套看现场排查偶现问题的节奏明显快了不少。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →