第42章:日志收集与分析:bugreport、dumpsys、logcat组合、自动化分析脚本

说到Android系统调试,日志就是我们的眼睛。没有日志,你就像在黑夜里摸路,全靠运气。我个人习惯是,拿到一个系统问题,第一件事就是拉日志。但拉日志也有讲究,不是随便抓一把就能看出门道的。

今天咱们聊聊三个核心工具:bugreportdumpsyslogcat。以及怎么把它们组合起来,再配上自动化脚本,让分析效率翻倍。

42.1 bugreport:系统全貌的快照

bugreport 是什么?说白了,它就是系统当前状态的“全家福”。把系统所有关键信息打包成一个文件,包括日志、进程状态、内存信息、网络状态等等。

我遇到过不少同事,一上来就只抓 logcat,结果问题涉及系统服务状态,logcat 里根本看不出。这时候 bugreport 就派上用场了。

基本用法

# 抓取 bugreport
adb bugreport

# 指定输出路径
adb bugreport /path/to/save/

# 抓取并直接解压查看
adb bugreport bugreport.zip

执行后,你会得到一个 zip 包。解压后里面包含:

  • bugreport-xxx.txt:核心报告,文本格式
  • dumpstate_log.txt:dumpstate 的详细日志
  • logcat.txt:当前 logcat 缓冲区内容
  • kernel.log:内核日志
  • events.log:事件日志
我的小技巧:bugreport 文件通常很大,几十 MB 到上百 MB 都有。我习惯用 grepfindstr 先过滤关键信息,而不是直接打开全文。比如搜索 "FATAL EXCEPTION"、"ANR"、"crash" 等关键词。

42.2 dumpsys:系统服务的诊断利器

dumpsys 可以查看系统服务的内部状态。它比 bugreport 更灵活,你可以只 dump 你关心的服务。

举个例子,如果怀疑是内存泄漏,我会先 dump 内存信息:

# 查看所有可用服务
adb shell dumpsys -l

# 查看内存信息
adb shell dumpsys meminfo

# 查看某个进程的内存
adb shell dumpsys meminfo com.android.systemui

# 查看 Activity 栈
adb shell dumpsys activity activities

# 查看窗口信息
adb shell dumpsys window windows

我曾经遇到一个诡异的问题:应用启动后界面卡住,但 logcat 里没有任何异常。后来我用 dumpsys activity activities 一看,发现 Activity 栈里有个奇怪的 Fragment 卡在中间,导致界面无法正常显示。嗯,这种问题光靠 logcat 是抓不到的。

常用 dumpsys 服务一览

服务名 用途 典型场景
meminfo 内存使用详情 内存泄漏、OOM
activity Activity 栈、任务栈 界面跳转异常、启动卡顿
window 窗口层级、焦点 窗口遮挡、触摸无响应
cpuinfo CPU 使用率 性能瓶颈、高负载
battery 电池状态、耗电统计 异常耗电、充电问题
netstats 网络流量统计 后台流量异常
package 包信息、权限 安装失败、权限问题

42.3 logcat:实时日志的王者

logcat 是大家最熟悉的工具了。但你真的会用吗?

我见过很多人直接 adb logcat 然后盯着屏幕看,信息刷得飞快,根本抓不住重点。其实 logcat 有很多过滤技巧。

高效过滤

# 按标签过滤
adb logcat -s TAG_NAME

# 按优先级过滤(V/D/I/W/E/F)
adb logcat *:E

# 组合过滤
adb logcat -s TAG_NAME:E

# 按进程 PID 过滤
adb logcat --pid=1234

# 保存到文件
adb logcat -v threadtime > logcat.txt

我个人最常用的是 -v threadtime 格式,它会把时间、线程 ID、进程 ID 都显示出来,方便后续分析。

缓冲区管理

logcat 有多个缓冲区,默认只抓 main 和 system。但有些日志会写到 events 或 crash 缓冲区。

# 查看所有缓冲区
adb logcat -b all

# 只抓 crash 缓冲区
adb logcat -b crash

# 清空缓冲区(抓取前清空,避免干扰)
adb logcat -c
注意:生产环境或用户设备上,logcat 缓冲区大小有限。如果日志量太大,早期的日志会被覆盖。我建议在复现问题前先清空缓冲区,然后只抓关键标签,避免信息丢失。

42.4 三剑客组合:bugreport + dumpsys + logcat

单独用每个工具都有局限。组合起来,才能看到全貌。

我的标准流程是这样的:

  1. 先抓 bugreport:获取系统快照,包括所有服务的状态
  2. 再抓 dumpsys:针对怀疑的服务,深入查看内部状态
  3. 最后抓 logcat:实时监控,捕捉问题发生时的日志

举个例子,分析一次 ANR:

# 1. 抓取 bugreport(包含 ANR 时的系统状态)
adb bugreport

# 2. 查看 ANR 相关的服务状态
adb shell dumpsys activity processes
adb shell dumpsys window policy

# 3. 实时抓取 logcat,过滤 ANR 相关日志
adb logcat -b events -s am_anr
adb logcat -b main -s ActivityManager:I

这样组合下来,你既能知道 ANR 发生时系统在做什么,又能看到具体的调用栈和日志信息。

42.5 自动化分析脚本

手动分析日志太慢了。尤其是 bugreport 动辄几十 MB,人工翻找效率极低。我写了一些脚本来自动化分析。

Python 脚本示例:提取关键信息

import re
import sys

def parse_bugreport(filepath):
    """解析 bugreport,提取关键信息"""
    with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
        content = f.read()
    
    # 提取 ANR 信息
    anr_pattern = r'ANR in (.*?)\n'
    anrs = re.findall(anr_pattern, content)
    
    # 提取崩溃信息
    crash_pattern = r'FATAL EXCEPTION: (.*?)\n'
    crashes = re.findall(crash_pattern, content)
    
    # 提取内存信息
    mem_pattern = r'Total RAM: (.*?)\n'
    mem_info = re.findall(mem_pattern, content)
    
    return {
        'anrs': anrs,
        'crashes': crashes,
        'memory': mem_info
    }

if __name__ == '__main__':
    result = parse_bugreport(sys.argv[1])
    print(f"发现 {len(result['anrs'])} 个 ANR")
    print(f"发现 {len(result['crashes'])} 个崩溃")
    print(f"内存信息: {result['memory']}")

Shell 脚本:一键抓取并分析

#!/bin/bash
# 一键抓取日志并分析

# 设置时间戳
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_DIR="logs_$TIMESTAMP"
mkdir -p $LOG_DIR

# 抓取 bugreport
echo "抓取 bugreport..."
adb bugreport $LOG_DIR/bugreport.zip

# 抓取 dumpsys
echo "抓取 dumpsys..."
adb shell dumpsys meminfo > $LOG_DIR/meminfo.txt
adb shell dumpsys activity activities > $LOG_DIR/activities.txt
adb shell dumpsys window windows > $LOG_DIR/windows.txt

# 抓取 logcat
echo "抓取 logcat..."
adb logcat -v threadtime -d > $LOG_DIR/logcat.txt

# 分析 ANR
echo "分析 ANR..."
grep -i "anr" $LOG_DIR/logcat.txt > $LOG_DIR/anr_analysis.txt

# 分析崩溃
echo "分析崩溃..."
grep -i "fatal" $LOG_DIR/logcat.txt > $LOG_DIR/crash_analysis.txt

echo "日志已保存到 $LOG_DIR"
避坑指南:我曾经写过一个脚本,每天定时抓取日志。结果发现日志文件越来越大,把磁盘撑爆了。后来我加了日志轮转和压缩机制,只保留最近 7 天的日志。自动化是好,但别忘了清理。

42.6 知识体系总览

下面这张图总结了日志收集与分析的核心流程:

日志收集与分析知识体系 bugreport 系统全貌快照 dumpsys 服务内部状态 logcat 实时日志监控 三剑客组合分析 bugreport + dumpsys + logcat 自动化分析脚本 Python / Shell 脚本 分析报告 / 问题定位 ANR / 崩溃 / 内存泄漏 / 性能瓶颈

从图中可以看到,三个工具各有侧重,组合起来才能覆盖大部分调试场景。再加上自动化脚本,就能从海量日志中快速定位问题。

42.7 实战经验总结

最后分享几点我踩过的坑:

  • 不要只依赖一种日志:bugreport 看不到实时变化,logcat 看不到系统状态,dumpsys 看不到历史记录。三者互补才是王道。
  • 日志要有时序:我习惯在日志里加上时间戳,这样能还原问题发生的时间线。logcat 的 -v threadtime 就是为此设计的。
  • 自动化要适度:脚本能帮你过滤 80% 的噪音,但剩下 20% 的关键判断还是得靠人。别完全相信脚本的输出,要自己验证。
  • 保存原始日志:分析完后,原始日志别急着删。有时候回头看,会发现之前忽略的线索。

嗯,日志分析这事,说白了就是经验活。工具就这些,但怎么用、什么时候用、怎么组合,全靠实战积累。多抓几次 bugreport,多看看 dumpsys 的输出,慢慢你就能一眼看出问题在哪了。


专注资料整理