1. 日志系统概述:Android日志系统架构、logd与logcat工作原理、日志缓冲区分类
做Android系统开发这些年,我遇到最多的问题就是——「这玩意儿到底崩在哪了?」。你想想看,一个App闪退了,或者系统服务挂了,如果没有日志,那基本就是大海捞针。所以,理解Android的日志系统,是每个系统工程师的必修课。
今天我们就从最底层开始,把Android日志系统的骨架摸清楚。嗯,这部分内容虽然基础,但非常重要。我个人习惯是先看架构图,再抠细节。
1.1 Android日志系统整体架构
Android的日志系统,说白了就是一个生产者-消费者模型。各个进程(App、系统服务、Native层)往缓冲区里写日志,然后logcat工具从缓冲区里读出来展示给你看。
整个架构分三层:
- 应用层:Java代码通过
android.util.Log类写日志 - Native层:C/C++代码通过
liblog库写日志 - 内核层:通过
logger内核驱动写入日志缓冲区
我在项目中遇到过一个问题:某个系统服务在启动阶段疯狂写日志,导致logd进程CPU飙升。后来发现是日志级别设置太低,把DEBUG级别的日志都写进去了。所以,生产环境一定要控制日志级别。
核心要点:所有日志最终都通过logd守护进程统一管理,logcat只是它的一个客户端。
1.2 logd与logcat工作原理
logd是Android 4.2之后引入的守护进程。它替代了之前的内核logger驱动,把日志管理从内核空间移到了用户空间。为什么要这么做?说白了就是为了更灵活地控制日志的读写权限和缓冲区大小。
logd的工作原理其实不复杂:
- 各个进程通过socket连接到logd
- logd将日志写入对应的缓冲区(环形缓冲区)
- logcat通过socket从logd读取日志
我记得有一次线上问题,logcat突然读不到日志了。排查了半天,发现是logd进程被OOM killer干掉了。嗯,这里要注意:logd的优先级要设置高一些,避免被误杀。
小技巧:你可以用 adb shell dumpsys logd 查看logd的统计信息,包括缓冲区使用率、写入速率等。这在排查日志丢失问题时非常有用。
1.3 日志缓冲区分类
Android把日志分成了几个不同的缓冲区。为什么要分?你想想看,如果把所有日志都混在一起,那找问题的时候得多痛苦。所以,按类型分开存放,各取所需。
| 缓冲区名称 | 标签 | 主要用途 | 常见内容 |
|---|---|---|---|
| main | main | 应用层日志 | App的Log.d/v/i/w/e输出 |
| system | system | 系统服务日志 | ActivityManager、WindowManager等 |
| events | events | 事件日志(二进制格式) | AM/PM/WM的关键事件 |
| crash | crash | 崩溃日志 | Java异常、Native崩溃 |
| kernel | kernel | 内核日志 | dmesg输出、驱动日志 |
这里我特别想强调一下 events 缓冲区。它用的是二进制格式,不是纯文本。所以你不能直接用 logcat -b events 看懂它,得配合 logcat -b events -v long 或者用 dumpsys 来解析。我曾经在分析系统启动时间时,就是靠events日志定位到某个服务启动超时的。
避坑指南:我曾经遇到过一个问题——crash缓冲区满了,导致新的崩溃日志写不进去。后来排查发现是某个App一直在崩溃,但没人处理。所以,建议定期清理或监控crash缓冲区的使用率。
1.4 日志缓冲区大小与配置
每个缓冲区的大小是有限的。默认情况下,main和system是256KB,events是256KB,crash是256KB。但不同厂商可能会调整。
你可以通过以下命令查看当前缓冲区大小:
adb shell logcat -g
输出示例:
main: ring buffer size 256Kb
system: ring buffer size 256Kb
events: ring buffer size 256Kb
crash: ring buffer size 256Kb
kernel: ring buffer size 128Kb
如果觉得不够用,可以动态调整:
adb shell logcat -G 1M # 将所有缓冲区设为1MB
但要注意,这个修改是临时的,重启后恢复默认值。要永久修改,得改 /etc/logd.conf 或者通过属性 persist.logd.size 来设置。
个人建议:在开发调试阶段,可以把缓冲区设大一些,比如4MB。但在生产环境,建议保持默认大小,避免日志占用过多内存。
1.5 日志优先级与过滤
Android日志有五个优先级,从低到高分别是:
- V (Verbose) — 最详细,一般不用
- D (Debug) — 调试信息
- I (Info) — 普通信息
- W (Warn) — 警告
- E (Error) — 错误
你可以在logcat中按优先级过滤:
adb logcat *:E # 只显示Error及以上级别
也可以按标签过滤:
adb logcat -s ActivityManager:I # 只显示ActivityManager的Info及以上日志
嗯,这里有个小坑要注意:优先级过滤是包含关系。比如你设了 *:W,那W和E都会显示,但V和D不会。我刚开始做开发时,经常搞混这个,导致漏掉一些关键日志。
1.6 总结
好了,这一章的内容就到这里。我们讲了Android日志系统的整体架构、logd和logcat的工作原理、五个缓冲区的分类和用途,以及一些实用的配置技巧。
说白了,日志系统就是你的「眼睛」。没有它,你根本不知道系统里发生了什么。所以,花点时间把这块搞透,绝对值得。
下一章,我们会深入logcat的实战用法,包括如何抓取特定进程的日志、如何分析ANR日志等。到时候见。