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的工作原理其实不复杂:

  1. 各个进程通过socket连接到logd
  2. logd将日志写入对应的缓冲区(环形缓冲区)
  3. 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日志等。到时候见。