本文将介绍日志记录流程,包括日志标准、级别准则、类、用途和多堆栈类似项。
日志标准
由于在 logcat 中合并使用了多个标准,Android 中的日志记录较为复杂。使用的主要标准详情如下:
| 来源 | 示例 | 堆栈级别准则 | 
|---|---|---|
| RFC 5424(syslog标准) | Linux 内核、许多 Unix 应用 | 内核、系统守护程序 | 
| android.util.Log | Android 框架 + 应用日志记录 | Android 框架和系统应用 | 
| java.util.logging.Level | Java 中的常规日志记录 | 非系统应用 | 
图 1:日志级别标准。
尽管这些标准都有类似的级别结构,但它们在粒度上存在差异。各个标准的近似等效项如下所示:
| RFC 5424 级别 | RFC 5424 严重性 | RFC 5424 说明 | android.util.Log | java.util.logging.Level | 
|---|---|---|---|---|
| 0 | 紧急 | 系统无法使用 | Log.e / Log.wtf | SEVERE | 
| 1 | 提醒 | 必须立即采取措施 | Log.e / Log.wtf | SEVERE | 
| 2 | 严重 | 严重情况 | Log.e / Log.wtf | SEVERE | 
| 3 | 错误 | 错误情况 | Log.e | SEVERE | 
| 4 | 警告 | 警告情况 | Log.w | WARNING | 
| 5 | 通知 | 正常,但值得注意 | Log.w | WARNING | 
| 6 | 信息 | 提供参考信息 | Log.i | INFO | 
| 7 | 调试 | 调试级消息 | Log.d | CONFIG,FINE | 
| - | - | 提供详细消息 | Log.v | FINER/FINEST | 
图 2:syslog、Android 和 Java 日志记录级别。
日志级别准则
各个日志标准都有现有的准则。所选日志级别遵循正在使用的适当标准,例如使用 syslog 标准进行内核开发。
以下三个图表按照由少到多的顺序显示了日志级别顺序:
| ERROR | 这些日志始终会保留。 | 
| WARN | 这些日志始终会保留。 | 
| INFO | 这些日志始终会保留。 | 
| DEBUG | 这些日志会被编译,但在运行时会被删除。 | 
| VERBOSE | 除开发期间外,系统不会将这些日志编译到应用中。 | 
图 3:android.util.Log
| CONFIG | 静态配置消息的消息级别 | 
| FINE | 提供跟踪信息的消息级别 | 
| FINER | 表示相当详细的跟踪消息 | 
| FINEST | 表示非常详细的跟踪消息 | 
| INFO | 参考性消息的消息级别 | 
| SEVERE | 表示严重故障的消息级别 | 
| WARNING | 表示潜在问题的消息级别 | 
| 0 | 紧急 | 系统无法使用 | 
| 1 | 提醒 | 必须立即采取措施 | 
| 2 | 严重 | 严重情况 | 
| 3 | 错误 | 错误情况 | 
| 4 | 警告 | 警告情况 | 
| 5 | 通知 | 正常,但值得注意的情况 | 
| 6 | 参考信息 | 参考性消息 | 
| 7 | 调试 | 调试级消息 | 
图 5:RFC 5424 - 第 6.2.1 节。
应用日志记录
选择性日志记录是由使用 Log#isLoggable 的 android.util.Log 类通过 TAG 执行的,如下所示:
| 
if (Log.isLoggable("FOO_TAG", Log.VERBOSE)) {
 Log.v("FOO_TAG", "Message for logging.");
}
 | 
|---|
在运行时可以调整日志,以提供特定级别的日志记录,如下所示:
| adb shell setprop log.tag.FOO_TAG VERBOSE | 
|---|
log.tag.* 属性会在重启时重置。此外,还有永久变体在重启后保持不变。请参阅下文:
| adb shell setprop persist.log.tag.FOO_TAG VERBOSE | 
|---|
Log#isLoggable 检查会在应用代码中留下日志轨迹。布尔值 DEBUG 标志会使用已设置为 false 的编译器优化来绕过日志跟踪记录,如下所示:
| private final static boolean DEBUG = false; | 
|---|
在编译时,R8 可利用 ProGuard 规则集逐个 APK 地移除其上的日志记录。以下示例可为 android.util.Log 移除 INFO 级别以下的所有日志记录:
| 
# This allows proguard to strip isLoggable() blocks containing only <=INFO log
# code from release builds.
-assumenosideeffects class android.util.Log {
  static *** i(...);
  static *** d(...);
  static *** v(...);
  static *** isLoggable(...);
}
-maximumremovedandroidloglevel 4
 | 
|---|
当多个应用 build 类型(例如开发 build 与发布 build)的底层代码预计相同、但所允许的日志级别不同时,这对于处理这些 build 类型将非常有用。必须设置并遵循显式政策,以便应用(尤其是系统应用)确定 build 类型和发布预期如何影响日志输出。
Android 运行时 (ART) 中的系统日志记录
有多个类可用于系统应用和服务:
| 类 | 用途 | 
|---|---|
| android.telephony.Rlog | 电台日志记录 | 
| android.util.Log | 常规应用日志记录 | 
| android.util.EventLog | 系统集成商诊断事件日志记录 | 
| android.util.Slog | 平台框架日志记录 | 
图 6:可用的系统日志类和用途。
虽然 android.util.Log 和 android.util.Slog 使用相同的日志级别标准,但 Slog 是一个仅供平台使用的 @hide 类。EventLog 级别会映射到 /system/etc/event-log-tags 中 event.logtags 文件中的条目。
原生日志记录
C/C++ 中的日志记录遵循 syslog 标准,其中 syslog(2) 对应于用于控制 printk 缓冲区的 Linux 内核 syslog,而 syslog(3) 对应于常规系统日志记录器。Android 将 liblog 库用于常规系统日志记录。
liblog 使用以下宏表单提供用于子日志组的封装容器:
| [Sublog Buffer ID] LOG [Log Level ID] | 
例如,RLOGD 对应于 [Radio log buffer ID] LOG [Debug Level]。主要的 liblog 封装容器如下所示:
| 封装容器类 | 示例函数 | 
|---|---|
| log_main.h | ALOGV、ALOGW | 
| log_radio.h | RLOGD、RLOGE | 
| log_system.h | SLOGI、SLOGW | 
图 7:liblog 封装容器。
Android 提供更高级别的日志记录接口,比直接使用 liblog 更受欢迎,如下所示:
| 库 | 用途 | 
|---|---|
| async_safe | 此库仅适合来自 async-signal-safe 环境的日志记录 | 
| libbase | 此日志记录库提供日志记录的 C++ 流接口,类似于 Google 样式 (glog) 的日志记录。 libbase可在外部项目中使用,并可用于使用libbase_ndk的应用。 | 
图 8:更高级别的日志库。
多堆栈类似项
由于粒度和级别 intent 方面存在差异,不同日志记录标准不存在明确或完全的匹配。例如,错误日志的 java.util.logging.Level 级别和 android.util.Log 级别不是一对一匹配的:
| java.util.Logging.Level | android.util.Log | 
|---|---|
| 严重 | Log.wtf | 
| 严重 | Log.e | 
图 9:标准 Java 日志记录与 Android 日志记录的错误级别对比。
在这类情况下,请使用单独标准来确定要应用的级别。
在具有多个堆栈级组件的系统开发中,请按照图 1 确定每个组件要使用的标准。如需关于分层消息传递的大致指南,请见图 2。
安全和隐私设置
不得记录个人身份信息 (PII),包括诸如以下的详细信息:
- 电子邮件地址
- 电话号码
- 名称
类似地,某些详细信息即便不是明确的个人身份信息,也被视为敏感信息。
例如,虽然时区信息不被视为个人身份信息,但它确实可以指示用户的大概位置。
作为安全和隐私审核的一部分,必须在发布之前对日志政策和可接受的详细信息进行处理。
设备日志
限制访问所有设备日志,包括使用 android.permission.READ_LOGS:
- 如果有后台应用请求访问所有设备日志,除非应用符合以下条件,否则系统会自动拒绝该请求:
- 共用系统 UID。
- 使用原生系统进程 (UID<APP_UID)。
- 使用 DropBoxManager。
- 仅访问事件日志缓冲区。
- 使用 EventLogAPI。
- 使用插桩测试。
- 如果具有 READ_LOGS的前台应用请求访问设备日志,系统会提示用户批准或拒绝访问请求。
