Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性-[Android取经之路]

[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

[Android取经之路] 系列文章:

《系统启动篇》

Android系统架构
Android是怎么启动的
Android 10.0系统启动之init进程
Android10.0系统启动之Zygote进程
Android 10.0 系统启动之SystemServer进程
Android 10.0 系统服务之ActivityMnagerService
Android10.0系统启动之Launcher(桌面)启动流程
Android10.0应用进程创建过程以及Zygote的fork流程
Android 10.0 PackageManagerService(一)工作原理及启动流程
Android 10.0 PackageManagerService(二)权限扫描
Android 10.0 PackageManagerService(三)APK扫描
Android 10.0 PackageManagerService(四)APK安装流程
《日志系统篇》

Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​
《Binder通信原理》:

Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
Android10.0 Binder通信原理(二)-Binder入门篇
Android10.0 Binder通信原理(三)-ServiceManager篇
Android10.0 Binder通信原理(四)-Native-C\C++实例分析
Android10.0 Binder通信原理(五)-Binder驱动分析
Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
Android10.0 Binder通信原理(七)-Framework binder示例
Android10.0 Binder通信原理(八)-Framework层分析
Android10.0 Binder通信原理(九)-AIDL Binder示例
Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
Android10.0 Binder通信原理(十一)-Binder总结

《HwBinder通信原理》

HwBinder入门篇-Android10.0 HwBinder通信原理(一)
 HIDL详解-Android10.0 HwBinder通信原理(二)
HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
HwServiceManager篇-Android10.0 HwBinder通信原理(五)
Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
HwBinder原理总结-Android10.0 HwBinder通信原理(十一)
《编译原理》

编译系统入门篇-Android10.0编译系统(一)
编译环境初始化-Android10.0编译系统(二)
make编译过程-Android10.0编译系统(三)
Image打包流程-Android10.0编译系统(四)
Kati详解-Android10.0编译系统(五)
Blueprint简介-Android10.0编译系统(六)
Blueprint代码详细分析-Android10.0编译系统(七)
Android.bp 语法浅析-Android10.0编译系统(八)
Ninja简介-Android10.0编译系统(九)
Ninja提升编译速度的方法-Android10.0编译系统(十)
Android10.0编译系统(十一)

1.概述
    在程序开发中,日志是必不可少的一个调试手段,Android的开发亦是如此。

    在Android的开发中,我们是通过LOGD\SLOGD等方式进行日志的写入,然后通过 logcat 读取相关的日志,但是日志是如何写入和读取的,底层的操作是什么,有时候没有深入研究。

    本节重点从底层源码级来分析一下Android的日志读写原理。

   在Android5.0(Android-L)之前,log由kernel的环形 buffer 保存,在Android5.0 之后,log保存在用户空间,通过Socket进行访问。

    在Android5.0之后,引入了Logd的守护进程用来进行日志的读写操作。

早期的Kernel ring buffer的原理可以参考 老罗-罗升阳的《Android日志系统驱动程序Logger源代码分析》

https://blog.csdn.net/luoshengyang/article/details/6595744

    本节主要来分析logd、logcat的相关实现原理。

2.核心源码
 

/frameworks/base/core/java/android/util/Log.java
/frameworks/base/core/java/android/util/SLog.java
/frameworks/base/core/java/android/util/EventLog.java
/frameworks/base/core/jni/android_util_Log.cpp
/system/core/logd/main.cpp
/system/core/logd/LogReader.cpp
/system/core/logd/LogWriter.java
/system/core/logcat/logcat.cpp
/system/core/liblog/logd_writer.cpp
/system/core/liblog/logd_reader.cpp

3.logcat相关指令说明
    手机连上电脑,安装adb驱动后,打开开发者模式,选择"USB调试",即可执行adb 命令

 

3.1 命令行语法
    要通过 adb shell 运行 Logcat,一般用法如下:

    [adb] logcat [

 

3.2 指令说明
    可以通过 "adb logcat --help" 来看logcat支持的指令。

    详细信息参考  《Logcat 命令行工具》

https://developer.android.google.cn/studio/command-line/logcat

3.3 日志过滤输出
    Android日志分为如下7个等级:

V:详细(最低优先级)--(Verbose:2)

D:调试 --(Debug:3)

I :信息 -- (Info:4)

W:警告 --(Warning:5)

E:错误 --(Error:6)

F:严重错误 --(Fatal:7)

S:静默(最高优先级,绝不会输出任何内容)--(Silent)

    我们在调试日志时,可以根据不同的等级来控制日志的输出

    下面这是一条正常输出的Android日志

    日志:02-05 12:44:15.357 17533 17545 D ActivityThread: caller system = false

    日志格式:<日期> <时间>    <日志等级> <日志标签TAG>  <日志内容>

    

我们可以通过过滤器只输出相应等级以下的日志:

1)例如:adb logcat  *:I

    指令含义:输出日志等级为Info以上的日志,即只输出Info、Error、Fatal、Silent的日志, Debug、Verbose的日志不会输出

 

2)例如:adb logcat ActivityManager:E ActivityThread:I *:S

    指令含义:该表达式会抑制除标记为“ActivityManager”、优先级不低于“Error”的日志消息,以及标记为“ActivityThread”、优先级不低于“Info”的日志消息以外的所有其他日志消息。

 

3)例如:adb logcat ActivityThread:E *:S

    指令含义:只输出标签为ActivityThread 且日志等级大于等于Error的日志,即只输出ActivityThread的 Error、Fatal的日志

 

3.4 日志输出格式
    除标记和优先级外,日志消息还包含许多元数据字段。您可以修改消息的输出格式,以便它们显示特定的元数据字段。为此,您可以使用 -v 选项,并指定下列某一受支持的输出格式。

brief:显示优先级、标记以及发出消息的进程的 PID。

long:显示所有元数据字段,并使用空白行分隔消息。

process:仅显示 PID。

raw:显示不包含其他元数据字段的原始日志消息。

tag:仅显示优先级和标记。

thread::旧版格式,显示优先级、PID 以及发出消息的线程的 TID。

threadtime(默认值):显示日期、调用时间、优先级、标记、PID 以及发出消息的线程的 TID。

time:显示日期、调用时间、优先级、标记以及发出消息的进程的 PID。

指令: [adb] logcat [-v ]

例如:adb logcat -v threadtime

注意:-v每次只能使用一种格式,默认情况下的threadtime格式即能满足我们现在的需求

 

3.5 日志缓冲区
    Android 日志记录系统为日志消息保留了多个环形缓冲区,而且并非所有的日志消息都会发送到默认的环形缓冲区。要查看其他日志消息,您可以使用 -b 选项运行 logcat 命令,以请求查看备用的环形缓冲区。您可以查看下列任意备用缓冲区:

radio:查看包含无线装置/电话相关消息的缓冲区。

events:查看已经过解译的二进制系统事件缓冲区消息。

main:查看主日志缓冲区(默认),不包含系统和崩溃日志消息。

system:查看系统日志缓冲区(默认)。

crash:查看崩溃日志缓冲区(默认)。

all:查看所有缓冲区。

default:报告 main、system 和 crash 缓冲区。

用法1:adb logcat -b main

说明1:上述指令只输出main缓冲区的日志。

用法2:adb logcat -b main,radio,system

说明2:上述指令输出 main、radio、system三个缓冲区的日志

 

4. 日志系统相关属性说明
属性

说明

log.tag.*

setprop log.tag.MyApp D

用来设置应用日志TAG的等级,上面是输出MyApp的Debug日志,主要控制的是isLoggable(MyApp,Debug) 这种方式控制的日志

手动设置后,重启失效

ro.logd.auditd

启动selinux审核守护进程

ro.logd.auditd.dmesg

selinux审核信息发送到dmesg log

ro.logd.kernel

cat “/proc/kmsg” 节点,用来读取kernel日志

ro.logd.size

所有日志缓存区大小的默认大小,默认为256K,可实时修改,重启失效

ro.logd.timestamp

修改日志时间戳,可以实时修改,重启失效

ro.logd.filter

日志黑白名单是否生效,disable为失效,enable为生效

logd.statistics

使能logcat -S statistics,在输出中包含统计信息,以帮助您识别和定位日志垃圾信息发送者

persist.logd.timestamp

修改日志时间戳,只能编译版本的时候修改,或者手机root后修改,重启继续生效

persist.logd.size

所有日志缓存区大小的默认大小,默认为256K只能编译版本的时候修改,或者手机root后修改,重启继续生效

persist.logd.size.main

main日志缓冲区大小,默认为256K

persist.logd.size.system

system日志缓冲区大小,默认为256K

persist.logd.filter

日志黑白名单是否生效,disable为失效,enable为生效,

通过adb logcat -p 来读取黑白名单;

通过logcat -P '' 来设置黑白名单,通过PID\UID来进行控制

persist.log.tag

setprop persist.log.tag Debug

控制Debug以上等级的日志输出,只有在版本预制,或者root后,设置生效,重启也生效

ro.logd.kernel

setprop ro.logd.kernel true

控制内核日志输出到logd的buffer中,通过lgocat抓取

ro.logd.auditd

setprop ro.logd.auditd true

控制selinux日志输出到logd的buffer中,通过lgocat抓取

 

我们在日常调试或者CTS测试时,会遇到日志丢失或者不全的情况,主要原因是日志量很大,但是日志缓冲区很小,此时只要把日志的缓冲区调大即可。

方法1: setprop ro.logd.size 5120     即把日志缓冲区都调整为5M

方法2:开发者模式->日志记录缓冲区大小-> 选择相应的缓冲区大小,可以选择64K -16M等5个大小

 

5. 日志分类
Android10.0上日志分为8类:

ID

对应值

说明

LOG_ID_MAIN

 

0

main日志缓冲区,这是应用程序唯一可用的日志缓冲区,在应用框架层提供了android.util.Log接口通过liblog库来往Logger日志驱动程序中写入日志,在运行时库提供了宏LOGV、LOGD、LOGI、LOGW、LOGE用来写入main类型的日志。

LOG_ID_RADIO

1

查看包含无线装置/电话相关消息的日志,可以调用android.telephony.Rlog进行打印

LOG_ID_EVENTS

2

类型为events的日志是用来诊断系统问题的,在应用框架层提供了android.util.EventLog接口通过liblog库来往Logger日志驱动程序中写入日志,在运行时库提供了宏LOG_EVENT_INT、LOG_EVENT_LONG、LOG_STRING用来写入event类型的日志。

LOG_ID_SYSTEM

3

类型为system的日志是系统级别的,在应用框架层提供了android.util.SLog接口通过liblog库来往Logger日志驱动程序中写入日志,在运行时库提供了宏SLOGV、SLOGD、SLOGI、SLOGW、SLOGE用来写入system类型的日志。

LOG_ID_CRASH

4

应用或者进程crash日志缓冲区

LOG_ID_STATS

5

统计日志缓冲区

LOG_ID_SECURITY

6

安全日志缓冲区

LOG_ID_KERNEL

7

kernel日志缓冲区

 

我们常用的日志缓冲区为:main、radio、system、events、crash

另外内核的日志,主要是输出到/proc/kmsg, 通过 cat /proc/kmsg 获取内核日志。
 

你可能感兴趣的:(AndrCompile)