了解日志记录

本文将介绍日志记录流程,包括日志标准、级别准则、类、用途和多堆栈类似项。

日志标准

由于在 logcat 中合并使用了多个标准,Android 中的日志记录较为复杂。使用的主要标准详情如下:

来源 示例 堆栈级别准则
RFC 5424syslog 标准) 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 CONFIGFINE
- - 提供详细消息 Log.v FINER / FINEST

图 2:syslog、Android 和 Java 日志记录级别。

日志级别准则

各个日志标准都有现有的准则。所选日志级别遵循正在使用的适当标准,例如使用 syslog 标准进行内核开发。

以下三个图表按照由少到多的顺序显示了日志级别顺序:

ERROR 这些日志始终会保留。
WARN 这些日志始终会保留。
INFO 这些日志始终会保留。
DEBUG 这些日志会被编译,但在运行时会被删除。
VERBOSE 除开发期间外,系统不会将这些日志编译到应用中。

图 3android.util.Log

CONFIG 静态配置消息的消息级别
FINE 提供跟踪信息的消息级别
FINER 表示相当详细的跟踪消息
FINEST 表示非常详细的跟踪消息
INFO 参考性消息的消息级别
SEVERE 表示严重故障的消息级别
WARNING 表示潜在问题的消息级别

图 4java.util.Logging.Level

0 紧急 系统无法使用
1 提醒 必须立即采取措施
2 严重 严重情况
3 错误 错误情况
4 警告 警告情况
5 通知 正常,但值得注意的情况
6 参考 参考性消息
7 调试 调试级消息

图 5RFC 5424 - 第 6.2.1 节

应用日志记录

选择性日志记录是由使用 Log#isLoggableandroid.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;

… If (DEBUG) { Log.v("FOO_TAG", "Extra debug logging."); }

在编译时,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.Logandroid.util.Slog 使用相同的日志级别标准,但 Slog 是一个仅供平台使用的 @hide 类。EventLog 级别会映射到 /system/etc/event-log-tagsevent.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 ALOGVALOGW
log_radio.h RLOGDRLOGE
log_system.h SLOGISLOGW

图 7liblog 封装容器。

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
    • 仅访问事件日志缓冲区。
    • 使用 EventLog API。
    • 使用插桩测试。
  • 如果具有 READ_LOGS 的前台应用请求访问设备日志,系统会提示用户批准或拒绝访问请求。