扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
logd 守护进程是日志系统的管家,内部维持三个日志 Socket : logd、logdr、logdw 来与客户端进行通信。同时负责维护几个环形缓冲区,用于存放系统中的各种日志,缓冲区包含 main、system、events、radio、crash、kernel ;但是在 Android 5.0 之前, logd 进程并不存在,日志是保留在 /dev/log/main、/dev/log/system、/dev/log/radio、/dev/log/event 等节点中,但是这样面临的一个问题就是当 Android 系统大版本升级时, linux kernel 需要升级对应的日志驱动,因此在后续的版本中就有了 logd 进程。
目前创新互联公司已为数千家的企业提供了网站建设、域名、虚拟主机、网站托管维护、企业网站设计、龙口网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
在 Android 日志系统分析(一):概述 一文中,总结了整个日志读写的主要流程,因此对于 logd 进程是如何同外界沟通进而读写日志的过程不再赘述,而着重于 logd 本身的一些知识点,这里先看一下 logd 的系统框图:
知识点:
① logd 是日志系统的核心进程,由 init 启动,是属于守护进程常驻后台
② logd 维护各个日志节点缓存队列,提供 socket 接口进行读、写、控制功能
③ logd 进程启动后,分别启动 LogReader、LogListener、CommandListener 三个线程,监听并处理来自三个 socket 的消息。在收到消息后,会通过 LogBuffer 类保存日志到对应的 RAM buffer 中
④ LogAudit 模块用于接收 Kernel selinux 信息,即可以在用户空间打印 selinux 日志信息
⑤ LogKlog 用于接收 kernel 日志信息,通过设置 property ,可以通过 logcat 命令读取内核日志
⑥ LogStatistics 是日志统计模块,默认开启统计数据较少,仅能以 pid/uid 纬度统计打印日志的数量。如果设置了 logd.statistic = true 。会打印更多纬度的统计信息,包括哪些 pid/uid/tid/TAG 日志量比较大,可用于日志裁剪相关
在 main 函数中,会打开 /dev/kmsg 来读取内核日志,通过 LogKlog 来进行存储;若是配置了 ro.logd.kernel 属性,则打开 /proc/kmsg 读取内核日志;
logd 作为 Native Service ,系统启动时会读取 init.rc 脚本去启动,它的相关属性被定义在 logd.rc 文件中:
这里主要分为两部分: 启动 logd 服务 和 启动 logd-reinit 服务 (在Android 10 上添加了 logd-auditctl 服务,目的是为了限制 selinux denia打印日志为5秒一次);先来看一下 启动 logd 服务 的同时做了些什么:
① 创建 logd、logdr、logdw 这三个 socket 为后面的通信做准备
② logdw 定义为 dgram 类型的 socket ,类似与 UDP类型的 Socket ,这么做的原因是考虑到性能问题,在多个进程同时写日志的情况下, write 函数写入到 socket 的 buffer 中即可返回,这样不会 block 业务逻辑太长时间。如果是 TCP 类型的 Socket ,客户端需要等到 TCP 收到 ACK 响应才能返回,这样就会过多的消耗性能和资源;
启动 logd-reinit 服务:
这个服务的主要作用是重新初始化 logd 的 LogBuffer,在配置中 oneshot 表示开机只启动一次。在上面的 main.cpp 中的 main 函数内, logd 在启动后,会创建一个线程 reinit_thread_start () ,当 logd-reinit 传入参数 reinit 后,进行功能执行:
① 如果 reinit 启动后,并且 /deg/kmsg 打开成功,把 logd.daemon: renit 写入 kmsg
② 重新初始化各个 log buffer 的大小,以及其他参数的初始化,但不会重新生成 LogBuffer 对象
main.cpp##main
main.cpp#reinit_thread_start()
[ 1 ] 深入理解安卓日志系统(logcat / liblog / logd)
[ 2 ] Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
日志系统设计需要考虑的问题:
1.需要保证日志系统收集到的数据的有效性和完整性,在app崩溃时日志不丢失。
2.保证收集日志的过程不影响app的性能,不能占用过高cpu资源,或者频繁IO造成卡顿现象。
简单来说,日志收集就是把app运行过程中产生的一些关键数据,给他采集保存起来,然后上传到后台服务器的过程。
这个采集过程,如果是每产生一次日志数据,就写一次文件,势必造成系统IO调用的频繁操作,可能会造成app运行卡顿现象。
MMAP就非常适用于解决这个问题。
MMAP是什么?
MMAP是一种内存映射文件的方法,将文件或者一些对象映射到进程的地址空间。实现文件磁盘地址跟进程虚拟地址空间中的一段内存空间一一映射关系。在写入进程的这段映射内存后,进程内存页的脏数据会自动回写到文件磁盘空间,这就以写内存的效果来实现了写文件的目的。
使用MMAP进行写日志,不仅解决了逐条日志写文件造成的频繁IO卡顿问题,而且也保证了日志数据的完整性,因为在app崩溃的时候,MMAP写入的映射内存数据也会被操作系统自动回写到文件磁盘空间,所以MMAP确实是高效日志系统的首选。
微信的XLog,美团的Logan都是用的MMAP。
还有另一个问题,如何对日志内容进行压缩,以达到写日志更加高效?
微信的XLog的方案是每产生一条日志,就进行压缩并写一次MMAP,压缩算法是LZ77压缩(基于字典的压缩算法),压缩率可达83.7%。
美团的Logan采用的是流式Gzip压缩方式,每次以16kb为单位,通过z_stream将日志流式进行Gzip压缩,流式可以避免CPU峰值。当然这种方案有个缺点,就是没有完美的解决日志丢失问题,当app发生Crash,有可能丢失尚未写入MMAP的16kb的日志。
先清除之前的日志:adb logcat -c
操作闪退步骤后,输出干净的闪退日志:adb logcat -b crash error.txt
adb bugreport log.txt
搜索fatal exception 或者crash;
app出现anr时会在data/anr目录下生成traces.txt文件
adb pull data/anr/traces.txt anr_log.txt
traces.txt文件里存放设备所有app的anr日志
打开日志文件,搜索包名
查看手机的所有日志
adb logcat
只获取该应用的日志
adb logcat | findstr 应用包名
开始抓取日志并保存到D盘的crash.txt
adb logcat D:\crash.txt
ctrl +C 停止抓取
ctrl +F 搜索 force finishing查看闪退的日志
安卓的手机日志需要在手机文件管理中查看。
1、首先在桌面上找到【文件管理器】图标,点击进入文件浏览页面。
2、在文件浏览页面,找到手机的系统文件夹。
3、在系统的文件夹中,找到带有【debug_log】字符的文件。这是手机的日志文件存放的位置,中文意思是【手机调试日志】。
4、进入文件夹后,可以文件夹中存放着以数字串命名的文件。这就是手机的日志文件。一般以手机日志生成日期命名,【、log】是日志的文件格式后缀。
5、打开任意一个日志文件即可进行查看,日志文件中记录了手机许多信息,其中也包括一些隐私信息,用户操作需要注意隐私保护。
以小米手机为例,其他机型操作方法大致相同:
1.首先在手机桌面上找到【文件管理】,进入手机的文件管理页面。
2.进入系统文件浏览页面后,点击手机的系统文件夹。一般此类文件夹命名是根据手机系统名称来命名的。(如华为EMUI,OPPO的Color OS等)
3.进入系统的文件夹后,找到【debug_log】这个文件夹。(这个文件夹的中文意思是:手机调试信息_log日志)
4.进入文件夹页面后,可以看到文件夹内存放着许多以log结尾的文件,这些就是手机的日志文件。日志文件一般以日志创建日期命名,【.log】是日志文件的格式后缀。
5.打开任何一个日志文件进行查看,日志文件记录了手机中的许多信息情况,这其中也包括了一些个人隐私信息,仅用于开发人员调试使用,个人需要注意隐私安全的保护。
logcat 作为读取日志的工具,相当于client 的角色;在前两篇文章中,关于 logcat 如何与其他部分沟通获取日志信息的流程已经介绍的比较清晰,本文不在赘述,转而归纳一下 logcat 的一些常用指令,并对其中一些做详细分析
Android 日志系统为日志消息保留了多个环形缓冲区,但并非多有的日志消息都会发送到默认的环形缓冲区。这里可以采用 logcat -b 命令查看设备的其他缓冲区:
如果需要查看内核空间日志信息,可采用如下几种方式查看:
1、读取 /proc/kmsg ,命令如下
读取/proc/kmsg属于消费型读取,读取之后再次读取不会显示已经读取过的日志信息
2、读取 /dev/kmsg ,命令如下
读取/dev/kmsg会显示缓存区里面的所有日志信息。新写入的日志信息会不断累加到日志缓冲器中
3、使用 dmesg 命令读取
dmesg命令读取一次只显示一部分日志,非阻塞执行
使用 -v 命令来修改 log 的输出格式,以显示特定的元数据字段:
优先级:
logcat -f 命令可以将日志消息输出到指定的文件中。这里我们需要确定的一件事是 logcat 作为客户端的角色,会将通过 liblog 获得的日志信息进行格式解析、格式化处理,而 liblog 库本身并不存在保存、解析的功能。这里来对 -f 指令做一下解析:
在 _logcat() 函数中解析 -f 指令,设置日志输出文件。例如 logcat -f sdcard/log.txt ,则 context-outputFileName 赋值为 sdcard/log.txt ;
以 printBinary() 函数为例:
logcat.cpp # printBinary() :
[ 1 ] Android物语:logcat
[ 2 ] android调试——logcat详解
[ 3 ] 玩转Android10源码开发定制(12)内核篇之logcat输出内核日志
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流