本文介绍了日志记录流程,包括日志标准、级别指南、类、用途和多堆栈近似值。
日志标准
Android 中的日志记录很复杂,因为使用的标准混合在 logcat 中。下面详细介绍了使用的主要标准
| 来源 | 示例 | 堆栈级别指南 |
|---|---|---|
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 |
严重 |
| 1 | 警报 | 必须立即采取措施 | Log.e / Log.wtf |
严重 |
| 2 | 严重 | 严重情况 | Log.e / Log.wtf |
严重 |
| 3 | 错误 | 错误情况 | Log.e |
严重 |
| 4 | 警告 | 警告情况 | Log.w |
警告 |
| 5 | 注意 | 正常但重要 | Log.w |
警告 |
| 6 | 信息 | 信息消息 | Log.i |
信息 |
| 7 | 调试 | 调试级别消息 | Log.d |
CONFIG、FINE |
| - | - | 详细消息 | Log.v |
FINER / FINEST |
图 2:syslog、Android 和 Java 日志记录级别。
日志级别指南
对于每个日志标准,都有现有的指南。选择的日志级别遵循正在使用的相应标准,例如内核开发使用 syslog 标准。
日志级别顺序(从最低到最高)显示在以下三个图中
错误 |
这些日志始终保留。 |
警告 |
这些日志始终保留。 |
信息 |
这些日志始终保留。 |
调试 |
这些日志已编译,但在运行时会被剥离。 |
详细 |
除非在开发期间,否则这些日志永远不会编译到应用中。 |
图 3:android.util.Log
配置 |
用于静态配置消息的消息级别 |
精细 |
提供跟踪信息的消息级别 |
更精细 |
指示相当详细的跟踪消息 |
最精细 |
指示高度详细的跟踪消息 |
信息 |
用于信息性消息的消息级别 |
严重 |
指示严重故障的消息级别 |
警告 |
指示潜在问题的消息级别 |
| 0 | 紧急 | 系统无法使用 |
| 1 | 警报 | 必须立即采取措施 |
| 2 | 严重 | 严重情况 |
| 3 | 错误 | 错误情况 |
| 4 | 警告 | 警告情况 |
| 5 | 注意 | 正常但重要的状况 |
| 6 | 信息性 | 信息性消息 |
| 7 | 调试 | 调试级别消息 |
图 5:RFC 5424 - 第 6.2.1 节。
应用日志记录
选择性日志记录是通过 android.util.Log 类使用 Log#isLoggable 按 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
|
|---|
这对于处理多种应用构建类型(例如,开发版本与发布版本)非常有用,在这些类型中,底层代码预计是相同的,但允许的日志级别却不同。必须为应用(尤其是系统应用)设置并遵循明确的政策,以决定构建类型和发布预期如何影响日志输出。
Android Runtime (ART) 中的系统日志记录
有几个可用于系统应用和服务的类
| 类 | 用途 |
|---|---|
android.telephony.Rlog |
无线日志记录 |
android.util.Log |
通用应用日志记录 |
android.util.EventLog |
系统集成商诊断事件日志记录 |
android.util.Slog |
平台框架日志记录 |
图 6:可用的系统日志类及其用途。
虽然 android.util.Log 和 android.util.Slog 使用相同的日志级别标准,但 Slog 是一个 @hide 类,仅供平台使用。EventLog 级别映射到 event.logtags 文件中的条目,该文件位于 /system/etc/event-log-tags 中。
原生日志记录
C/C++ 中的日志记录遵循 syslog 标准,其中 syslog(2) 对应于控制 printk 缓冲区的 Linux 内核 syslog,而 syslog(3) 对应于通用系统记录器。Android 使用 liblog 库进行通用系统日志记录。
liblog 使用以下宏格式为子日志组提供封装容器
[子日志缓冲区 ID] LOG [日志级别 ID] |
例如,RLOGD 对应于 [无线日志缓冲区 ID] LOG [调试级别]。主要的 liblog 封装容器如下所示
| 封装容器类 | 示例函数 |
|---|---|
log_main.h |
ALOGV、ALOGW |
log_radio.h |
RLOGD、RLOGE |
log_system.h |
SLOGI、SLOGW |
图 7:liblog 封装容器。
Android 具有更高级别的日志记录接口,这些接口优于直接使用 liblog,如下所示
| 库 | 用途 |
|---|---|
async_safe |
仅用于从异步信号安全环境进行日志记录的库 |
libbase |
日志记录库,提供类似于 Google 风格 (glog) 日志记录的 C++ 流接口。libbase 既可在外部项目中使用,也可在使用 libbase_ndk 的应用中使用。 |
图 8:更高级别的日志库。
多堆栈近似
由于粒度和级别意图的差异,不同的日志记录标准之间没有明确或完全匹配的对应关系。例如,用于错误日志的 java.util.logging.Level 和 android.util.Log 级别不是 1:1 匹配
| 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的前台应用请求访问设备日志,系统会提示用户批准或拒绝访问请求。