本文系统梳理 Android 系统中可用的日志抓取工具与方法,详解 dmesg、logcat、串口、adb 等工具的的原理、联系与区别,并附实战案例与对比表。
一、为什么需要理解 Android 日志体系?
Android 系统基于 Linux 内核构建,其日志体系是两条并行又交织的线:
- 内核日志线 :由 Linux 内核的
printk()系列函数产生,存储在环形缓冲区(Ring Buffer)中,通过dmesg命令读取。 - Framework 日志线 :由 Android 框架层(App、SystemServer、各类 Service)通过
liblog库写入,由logd守护进程分类管理,通过logcat命令读取。
此外,串口(UART) 作为最底层的硬件调试接口,可以在系统甚至内核启动的最早期阶段捕获输出;而 adb 则是连接宿主机与设备的桥梁,绝大多数日志命令都需要通过它来执行。
理解这些工具的来源、路径、局限性,是高效排查 Android 系统问题的基本功。

图1:Android 日志系统整体架构 ------ 内核日志与 Framework 日志两条主线,通过不同工具呈现
二、各日志抓取方式详解
2.1 dmesg ------ 内核环形缓冲区的窗口
原理:
Linux 内核启动时会分配一块连续的物理内存作为 Kernel Ring Buffer (环形缓冲区),默认大小通常为 128KB(可通过内核配置 CONFIG_LOG_BUF_SHIFT 调整)。内核中的所有驱动、子系统通过 printk()、pr_info()、dev_info() 等宏将日志写入这个缓冲区。当缓冲区写满后,新的日志会从头覆盖旧的日志(环形覆盖策略)。
dmesg 命令的本质是读取 /proc/kmsg 文件(或调用 klogctl() 系统调用),将环形缓冲区中的内容格式化输出到终端。
常用命令:
# 查看所有内核日志
dmesg
# 过滤包含 "error" 的日志
dmesg | grep -i "error"
# 过滤特定驱动相关的日志
dmesg | grep -i "usb"
dmesg | grep -i "gpu"
# 设置控制台日志级别(0-7,数字越小越详细)
dmesg -n 4
# 清空环形缓冲区(重启后失效)
dmesg -C
# 以人类可读格式显示时间戳
dmesg -T
# 实时监控内核日志(类似 tail -f)
dmesg -w
特点:
| 项目 | 说明 |
|---|---|
| 日志来源 | Linux 内核 Ring Buffer |
| 写入方式 | printk() / pr_info() / dev_info() 等 |
| 读取方式 | dmesg 命令 或 cat /proc/kmsg |
| 是否需要 root | 不需要(普通用户可读,但某些敏感信息可能被限制) |
| 重启后是否保留 | 否 (除非配置 kernel.printk_delay 或保存到 persistent storage) |
| 性能影响 | 极小(内核环形缓冲,零拷贝读取) |
| 典型场景 | 驱动加载失败、内核 panic/oops、硬件初始化问题、内存分配失败 |

图2:dmesg 原理 ------ printk 写入环形缓冲区,dmesg 工具读取展示
实战案例:
# 场景:设备开机后蓝牙驱动加载失败
# 步骤1:清空旧日志,重启设备
adb shell dmesg -C
adb reboot
# 步骤2:等待启动完成后抓取内核日志
adb shell dmesg | grep -iE "bluetooth|hci|btusb"
# 典型输出:
# [ 12.345678] btusb: Failed to load firmware rtl_bt/rtl8761b_config.bin
# [ 12.345679] btusb: -2: Unable to load firmware file
# [ 12.345680] Bluetooth: hci0: Failed to load rtl_bt/rtl8761b_config.bin
从上述输出可以迅速定位到:固件文件缺失是导致蓝牙驱动加载失败的根本原因。
2.2 logcat ------ Android Framework 日志的入口
原理:
Android Framework 层的日志体系与 Linux 内核完全独立。其核心组件包括:
-
liblog 库 :每个进程链接
libc/liblog.so,通过LOGD()、LOGI()、LOGW()、LOGE()、LOGV()等宏写入日志。日志以二进制格式(priority/tag(12): message)通过 binder IPC 发送到 logd 守护进程。 -
logd daemon:以 root 权限运行的守护进程,接收来自各进程的日志,按分类写入不同的日志分区(backing store):
- anonymous:匿名日志(没有 tag 的日志)
- main:主应用日志(默认输出)
- system:系统服务日志
- event:事件日志(结构化事件)
- radio:无线电/基带日志
-
logcat 命令 :通过 socket
/dev/socket/adb连接 logd,读取并格式化展示日志内容。
常用命令:
# 查看主日志(默认)
adb logcat
# 按时间格式显示
adb logcat -v time
# 同时查看多个日志缓冲区
adb logcat -b main -b system -b event -b radio
# 按优先级过滤(只显示 Warn 及以上)
adb logcat *:W
# 按 tag 过滤(只显示特定标签)
adb logcat -s ActivityManager WindowManager
# 清空日志缓冲区
adb logcat -c
# 保存日志到文件
adb logcat > logcat_output.txt
# 实时监控并保存
adb logcat -v threadtime | tee debug.log
特点:
| 项目 | 说明 |
|---|---|
| 日志来源 | Android Framework(App/SystemServer/Service) |
| 写入方式 | LOGD/I/W/E/V 宏 → liblog → logd daemon |
| 读取方式 | logcat 命令 或 adb logcat |
| 是否需要 root | 不需要(adb logcat 无需 root) |
| 重启后是否保留 | 否(日志分区循环覆盖,除非配置保存) |
| 性能影响 | 中等(log 写入有同步 IPC 开销,-v verbose 模式影响较大) |
| 典型场景 | App 崩溃/ANR、系统服务异常、权限拒绝、Activity 生命周期追踪 |

图3:logcat 原理 ------ App 通过 liblog 写入,logd 守护进程分类存储,adb logcat 读取展示
实战案例:
# 场景:App 发生 ANR(应用无响应)
# 步骤1:触发 ANR(长时间不操作 App)
# 步骤2:抓取 trace 文件和分析日志
adb shell ls /data/anr/ # 查看 trace 文件
adb pull /data/anr/traces.txt # 拉取 trace 文件到本地
adb logcat -b main -v time | grep -i "anr"
# 典型输出:
# I/ActivityManager( 1234): ANR in com.example.app (com.example.app/.MainActivity)
# I/ActivityManager( 1234): PID: 5678, UID: 10087
# I/ActivityManager( 1234): Loading: false waitedMs: 5001
通过分析 traces.txt 文件中的线程堆栈,可以精确定位到 ANR 发生的代码行。
2.3 串口(UART)------ 最底层的硬件调试通道
原理:
UART(通用异步收发传输器)是嵌入式系统中最早的调试接口。在 Android 设备上,串口通常通过以下引脚引出:
- TX:数据发送引脚
- RX:数据接收引脚
- GND:地线
串口调试的优势在于:它不依赖操作系统。从 Bootloader(U-Boot)阶段开始,到内核初始化早期,所有打印到控制台的输出都会通过串口实时传输。这意味着即使 Android 系统完全无法启动,你仍然可以通过串口看到内核的启动信息。
所需工具:
- USB 转串口模块(如 CH340、CP2102)
- 杜邦线(连接 TX/RX/GND)
- 终端软件(minicom、screen、Putty)
常用命令(宿主机端):
# 使用 minicom 连接串口(波特率通常为 115200)
minicom -D /dev/ttyUSB0 -b 115200
# 使用 screen 连接
screen /dev/ttyUSB0 115200
# 记录串口输出到文件(配合 script 命令)
script -t 2> timing.log -a serial_output.txt
# 然后操作设备,退出后得到完整记录
特点:
| 项目 | 说明 |
|---|---|
| 日志来源 | 内核早期启动 + Bootloader + 内核控制台输出 |
| 写入方式 | 硬件串口直接输出,不经过操作系统 |
| 读取方式 | USB转串口模块 + 终端软件 |
| 是否需要 root | 不需要(物理连接即可) |
| 重启后是否保留 | 是(串口抓包工具可全程录制) |
| 性能影响 | 无(纯硬件输出,不影响系统性能) |
| 典型场景 | Bootloader 阶段调试、内核早期启动问题、分区表问题、内核 panic 时的完整堆栈 |
实战案例:
# 场景:设备开机卡在 logo 界面,无法进入系统
# 步骤1:连接串口(TX→RX,RX→TX,GND→GND)
minicom -D /dev/ttyUSB0 -b 115200
# 步骤2:重启设备,观察串口输出
# 典型输出:
# U-Boot 2020.01 (Mar 15 2024 - 10:30:00 +0000)
# CPU: Qualcomm Snapdragon 660
# Model: Custom Board
# Loading Firmware... OK
# Starting kernel ...
# [ 0.000000] Linux version 4.19.0-custom (builder@host) (gcc version 8.4.0)
# [ 0.000001] Boot CPU: AArch64 Processor [410fd034]
# [ 1.234567] Initializing cgroup subsys cpuset
# [ 2.345678] Kernel panic - not syncing: VFS: Unable to mount root fs
# [ 2.345679] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.0-custom
# [ 2.345680] Call trace:
# [ 2.345681] dump_backtrace+0x0/0x1c0
# [ 2.345682] show_stack+0x14/0x20
# [ 2.345683] dump_stack+0xc4/0x110
# 分析:Kernel panic 显示 VFS 无法挂载根文件系统,说明 initramfs 或设备树配置有问题
串口是排查启动失败类问题的终极手段,因为此时 logcat 和 dmesg 都还不可用。
2.4 adb shell ------ 万能调试入口
原理:
adb shell 是在设备上启动一个完整的 Linux shell 环境(通常是 /system/bin/sh)。通过这个 shell,你可以执行设备上所有可用的命令,包括 dmesg、logcat、dumpsys、getprop、ls、cat 等。它是最灵活的调试入口,因为你可以组合任意命令来完成复杂的诊断任务。
常用命令:
# 进入设备 shell
adb shell
# 在 shell 中执行单条命令
adb shell dmesg | grep error
adb shell getprop ro.build.version.release
adb shell dumpsys activity top
# 组合命令:查看当前前台 Activity
adb shell dumpsys activity activities | grep Resume
# 查看内存信息
adb shell dumpsys meminfo
# 查看电池信息
adb shell dumpsys battery
# 查看网络接口
adb shell ip addr
# 查看进程列表
adb shell ps | grep system
# 查看文件内容
adb shell cat /proc/meminfo
adb shell cat /sys/class/power_supply/battery/status
特点:
| 项目 | 说明 |
|---|---|
| 日志来源 | 设备上所有可用的命令和输出 |
| 写入方式 | 通过 adb socket 转发 shell 命令执行结果 |
| 读取方式 | adb shell <command> |
| 是否需要 root | 不需要(需 USB 调试授权) |
| 重启后是否保留 | 否 |
| 性能影响 | 小(远程命令执行开销) |
| 典型场景 | 系统属性查看、服务状态诊断、文件操作、日志组合抓取 |
2.5 adb bugreport ------ 最全面的系统诊断包
原理:
adb bugreport 是 Android 系统中最强大的诊断命令。它会在设备上执行一系列诊断命令,将所有关键日志和系统状态信息打包成一个 ZIP 文件。具体包括:
- 完整的
logcat输出(所有缓冲区) dmesg内核日志- 所有服务的
dumpsys输出 - 线程堆栈信息(
dumpsys threadinfo) - 内存信息
- 网络状态
- 电池信息
- 安装的应用列表
- 系统属性
- 等等...
使用方式:
# 生成完整的 bugreport(可能较大,建议用 -n 限制数量)
adb bugreport > bugreport.zip
# 仅获取最近 N 条日志(减少文件大小)
adb bugreport -n 50000 > bugreport.zip
# 指定输出目录
adb bugreport ./output/bugreport.zip
特点:
| 项目 | 说明 |
|---|---|
| 日志来源 | 完整系统 dump(logcat + dmesg + dumpsys + thread info 等) |
| 写入方式 | 设备端打包为 ZIP 文件 |
| 读取方式 | adb bugreport > file.zip |
| 是否需要 root | 不需要(但需 USB 调试) |
| 重启后是否保留 | 是(每次生成独立文件) |
| 性能影响 | 较大(全面 dump 会暂停部分服务,耗时较长) |
| 典型场景 | 系统崩溃分析、性能问题诊断、OTA 更新问题、向厂商提交 bug |
三、各方式的联系与区别
3.1 核心区别对比表

图4:Android 日志抓取方式对比总览
3.2 联系与依赖关系
这几种日志抓取方式并非完全独立,它们之间存在以下联系:
-
adb 是桥梁 :
adb logcat、adb shell dmesg、adb bugreport都依赖 adb 服务。没有 adb 连接,你只能直接在设备控制台(如果有屏幕和输入)或使用串口来查看日志。 -
dmesg 和 logcat 互补:
dmesg看内核层问题(驱动、硬件、内存管理)logcat看Framework 层问题(App、Service、权限)- 实际调试中通常同时打开两个终端 ,一个跑
dmesg一个跑logcat
-
串口是兜底:当 adb 不可用时(设备无法识别、USB 驱动问题、系统完全死机),串口是唯一能获取信息的通道。反过来,串口输出内容通常比 dmesg 更早期、更原始。
-
bugreport 是汇总 :
adb bugreport内部实际上就是依次执行了logcat、dmesg、dumpsys等命令,将结果汇总打包。可以说它是上述所有方式的集大成者。
3.3 日志流向总结
┌─────────────────────────────────────────────────────┐
│ 用户操作/系统事件 │
└──────────────┬──────────────────────────┬────────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ Linux Kernel │ │ Android Framework │
│ (驱动/硬件/内存管理) │ │ (App/SystemServer) │
│ │ │ │
│ printk() → Ring Buffer│ │ LOGD/I/W/E → liblog │
└──────────────┬──────────┘ └────────────┬───────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ dmesg 命令 │ │ logd daemon │
│ cat /proc/kmsg │ │ (分类存储到分区) │
└──────────────┬──────────┘ └────────────┬───────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ 串口 UART │ │ logcat 命令 │
│ (硬件直连,最早输出) │ │ adb logcat │
└──────────────┬──────────┘ └────────────┬───────────┘
│ │
└──────────┬────────────────┘
▼
┌────────────────────────┐
│ adb bugreport │
│ (汇总所有日志+系统状态) │
└────────────────────────┘
四、调试实战流程

图5:Android 调试日志抓取标准流程 ------ 四步法适用于绝大多数调试场景
4.1 标准调试四步法
第一步:连接设备
adb devices
# 确认设备状态为 device(非 unauthorized)
第二步:清空旧日志
# 清空 Android 日志
adb logcat -c
# 清空内核日志(需要 root 或特定权限)
adb shell dmesg -C
清空旧日志非常重要!Android 日志分区大小有限(通常几 MB 到几十 MB),如果不清空,旧日志会混入新日志中,增加分析难度。
第三步:复现问题
在设备上执行触发问题的操作。如果是启动问题,则直接重启设备。
第四步:抓取日志
# 方案A:日常调试(快速)
adb logcat -v time > debug_log.txt
# 方案B:内核问题排查
adb shell dmesg > kernel_log.txt
# 方案C:完整诊断(推荐提交 bug 时使用)
adb bugreport > bugreport_$(date +%Y%m%d_%H%M%S).zip
4.2 常见场景速查表
| 问题类型 | 首选工具 | 辅助工具 | 关键命令 |
|---|---|---|---|
| App 崩溃 | logcat | adb bugreport | `adb logcat *:E |
| ANR | logcat | adb bugreport | adb pull /data/anr/traces.txt |
| 驱动加载失败 | dmesg | 串口 | `adb shell dmesg |
| 开机卡 logo | 串口 | dmesg | minicom -D /dev/ttyUSB0 -b 115200 |
| 内存泄漏 | dumpsys | logcat | adb shell dumpsys meminfo <pkg> |
| 性能卡顿 | traceview | logcat | adb shell am trace-trace |
| 蓝牙/WiFi 问题 | dmesg + logcat | 串口 | `adb shell dmesg | grep -iE "bt |
| 相机问题 | logcat | dmesg | adb logcat -s CameraService Camera |
| 音频问题 | logcat | dmesg | adb logcat -s AudioService AudioFL |
| OTA 更新失败 | bugreport | 串口 | adb bugreport > ota_bug.zip |
五、高级技巧与避坑指南
5.1 日志分区大小查看与调整
# 查看各日志分区大小
adb shell dumpsys log
# 查看分区使用情况
adb shell ls -la /dev/log/
# 临时调整日志缓冲区大小(需要 root)
adb shell su -c "logcat -G 4M" # 设置主日志缓冲区为4MB
5.2 内核日志级别动态调整
# 查看当前控制台日志级别
adb shell cat /proc/sys/kernel/printk
# 输出: 4 4 1 7 (当前/默认/启动时/磁盘日志级别)
# 动态调整控制台日志级别(需要 root)
adb shell su -c "echo 8 > /proc/sys/kernel/printk"
# 设置为 8 后,debug 级别的日志也会输出到控制台
5.3 持久化内核日志(防止重启丢失)
在内核配置中启用以下选项:
CONFIG_PRINTK_DELAY=y # 延迟打印,避免死锁
CONFIG_LOG_BUF_SHIFT=18 # 增大缓冲区到 256KB
CONFIG_PANIC_PRINTK=y # panic 时强制打印
或者在启动参数中添加:
androidboot.log_dir=/data/log
5.4 常见坑点
-
logcat 看不到新日志 :检查是否切换了日志缓冲区(
-b main/system/event/radio/anonymous),默认只显示 main。 -
dmesg 输出乱码:终端编码问题,确保终端设置为 UTF-8。
-
adb 设备未识别 :检查 USB 调试授权(
adb authorize),或重启 adb 服务(adb kill-server && adb start-server)。 -
串口无输出:检查 TX/RX 是否交叉连接(设备的 TX 接模块的 RX,反之亦然),波特率是否正确(常见 115200、9600)。
-
bugreport 文件过大 :使用
-n参数限制日志条数,或只抓取特定缓冲区。 -
logcat 过滤无效 :注意 tag 名称区分大小写,使用
-s TagName:E语法时 tag 名不能超过 12 字符(Android 日志 tag 最大 12 字符)。
六、总结
| 工具 | 最佳用途 | 关键优势 | 主要局限 |
|---|---|---|---|
| dmesg | 内核/驱动调试 | 直接读取内核环形缓冲,无需 root | 重启后丢失,看不到 Framework 日志 |
| logcat | App/Framework 调试 | 分类详细,支持丰富过滤 | 无法捕获内核早期日志 |
| 串口 | 启动/硬件调试 | 最早可用,不依赖操作系统 | 需要物理连接,带宽有限 |
| adb shell | 综合诊断 | 万能入口,可执行任意命令 | 依赖 adb 服务正常运行 |
| adb bugreport | 完整诊断包 | 一次性获取所有关键信息 | 文件大,生成耗时 |
核心原则:清空旧日志 → 复现问题 → 立即抓取 → 分析定位。
在实际工作中,建议根据问题类型灵活组合使用上述工具:
- 日常 App 调试 :
adb logcat -v time | grep -i error足够 - 驱动/内核问题 :
adb shell dmesg | grep -iE "panic|oops|error"+ 串口兜底 - 系统级问题 :
adb bugreport一次性打包所有信息 - 启动失败 :串口是唯一选择,配合
dmesg和logcat补充