Android 日志系统分析(三):logcat

一、前言

logcat 作为读取日志的工具,相当于client 的角色;在前两篇文章中,关于 logcat 如何与其他部分沟通获取日志信息的流程已经介绍的比较清晰,本文不在赘述,转而归纳一下 logcat 的一些常用指令,并对其中一些做详细分析

二、命令简介

选项描述eg
-s输出指定 tag 的日志,相当于过滤器表达式 '*:S'logcat -s tag
-f <file>设置logcat 内容保存的位置,默认是stdoutlogcat -f sdcard/log.txt
-r <kbytes>每输出 <kbytes> 时轮替日志文件,默认是16 必须配合 -f (暂不明白)logcat -f sdcard/log.txt -r 1
-n <count>设置日志输出的最大数目, 需要 -r 参数暂不明白
-v <format>设置日志消息的输出格式。详见下文 格式化输出logcat -v thread
-D输出各个日志缓冲区之间的分隔线logcat -D ...
-c清除(清空)所选的缓冲区并退出,默认清除 main、system 和 crashlogcat -c / -b all -c
-d将日志转储到屏幕并退出logcat -d > log.txt
-e <expr>输出正则匹配的日志消息logcat -e 匹配数据 -m 5
-m <count>输出 <count> 行后退出......
-t <count>仅输出最新的行数,此选项包括 -d 功能logcat -t 5
-t '<time>'输出自指定时间以来的最新行,此选项包括 -d 功能logcat -t '01-26 20:52:41.820'
-g获取指定日志缓冲区的大小并退出logcat -g
-G设置日志环形缓冲区的大小,可以在结尾处添加 K 或 Mlogcat -G 2M
-b加载可供查看的日志缓冲区,更多可见下文 日志缓冲区logcat -b system
-B以二进制文件形式输出日志......
-S在输出中包含统计信息,以识别和定位日志垃圾信息发送者......
--pid=<pid>仅输出来自给定 PID 的日志logcat --pid=4355

三、日志缓冲区

Android 日志系统为日志消息保留了多个环形缓冲区,但并非多有的日志消息都会发送到默认的环形缓冲区。这里可以采用 logcat -b 命令查看设备的其他缓冲区:

缓冲区描述eg
radio输出通信系统的日志,包含无线装置/电话相关消息logcat -b radio
events输出event模块的日志logcat -b events
main主日志缓冲区(默认),不包含系统和崩溃日志消息logcat -b main
system输出系统日志logcat -b system
crash输出崩溃日志logcat -b crash
all输出所有缓冲区日志logcat -b all
default输出main、system、crash缓冲区日志logcat -b default

如果需要查看内核空间日志信息,可采用如下几种方式查看:

1、读取 /proc/kmsg ,命令如下

adb shell cat /proc/kmsg

读取/proc/kmsg属于消费型读取,读取之后再次读取不会显示已经读取过的日志信息

2、读取 /dev/kmsg,命令如下

adb shell cat /dev/kmsg

读取/dev/kmsg会显示缓存区里面的所有日志信息。新写入的日志信息会不断累加到日志缓冲器中

3、使用 dmesg 命令读取

adb shell dmesg

dmesg命令读取一次只显示一部分日志,非阻塞执行

四、格式化输出

使用 -v 命令来修改 log 的输出格式,以显示特定的元数据字段:

格式描述eg
brief显示优先级、标记以及发出消息的进程的 PID......
long显示所有元数据字段,并使用空白行分隔消息......
process仅显示 PID......
raw显示不包含其他元数据字段的原始日志消息......
tag仅显示优先级和标记......
thread旧版格式,显示优先级、PID 以及发出消息的线程的 TID......
threadtime(默认值)显示日期、调用时间、优先级、标记、PID 以及发出消息的线程的 TID......
time显示日期、调用时间、优先级、标记以及发出消息的进程的 PID......
color使用不同的颜色来显示每个优先级......
descriptive显示日志缓冲区事件说明。此修饰符仅影响事件日志缓冲区消息,不会对其他非二进制文件缓冲区产生任何影响......
epoch显示自 1970 年 1 月 1 日以来的时间(以秒为单位)......
monotonic显示自上次启动以来的时间(以 CPU 秒为单位)......
printable确保所有二进制日志记录内容都进行了转义......
uid如果访问控制允许,则显示 UID 或记录的进程的 Android ID......
usec显示精确到微秒的时间......
UTC显示 UTC 时间......
year将年份添加到显示的时间......
zone将本地时区添加到显示的时间......

优先级:

选项描述eg
V–Verbose(最低优先级)adb logcat *:v
D– Debugadb logcat *:d
I– Infoadb logcat *:i
W– Warningadb logcat *:w
E– Erroradb logcat *:e
F– Fataladb logcat *:f
S– Silentadb logcat *:s

五、logcat -f 命令详解

logcat -f 命令可以将日志消息输出到指定的文件中。这里我们需要确定的一件事是 logcat 作为客户端的角色,会将通过 liblog 获得的日志信息进行格式解析、格式化处理,而 liblog 库本身并不存在保存、解析的功能。这里来对 -f 指令做一下解析:

logcat_main.cpp # main()
    ---> logcat.cpp # android_logcat_run_command()
        ---> __logcat()
            {
                ......

                case 'f':
                    if ((tail_time == log_time::EPOCH) && !tail_lines) {
                        tail_time = lastLogTime(optctx.optarg);
                    }
                    // redirect output to a file
                    context->outputFileName = optctx.optarg;   //注释 ①
                    break;

                ......


                setupOutputAndSchedulingPolicy()   //注释 ②

 
                 while (...) {   //注释 ③

                    int ret = android_logger_list_read(logger_list, &log_msg);
                    
                    if (context->printBinary) {
                        printBinary(context, &log_msg);
                    } else {
                        processBuffer(context, dev, &log_msg);
                    }

                ......

            }

5.1 注释① :解析 -f 指令

          case 'f':
                  if ((tail_time == log_time::EPOCH) && !tail_lines) {
                        tail_time = lastLogTime(optctx.optarg);
                    }
                    // redirect output to a file
                    context->outputFileName = optctx.optarg;   //注释 ①
                    break;

_logcat() 函数中解析 -f 指令,设置日志输出文件。例如 logcat -f sdcard/log.txt ,则 context->outputFileName 赋值为 sdcard/log.txt

5.2 注释② :设置输出路径

static void setupOutputAndSchedulingPolicy(
    android_logcat_context_internal* context, bool blocking) {
    
    if (!context->outputFileName) return;

    ......
  
    // 打开文件获得 fd 
    context->output_fd = openLogFile(context->outputFileName);

    if (context->output_fd < 0) {
        logcat_panic(context, HELP_FALSE, "couldn't open output file");
        return;
    }

    ......
}

5.3 注释③ :写入日志


               while (...) {   
                                // 调用 liblog 库中的 android_logger_list_read 函数获取日志 
                    int ret = android_logger_list_read(logger_list, &log_msg);
                    
                    if (context->printBinary) {       
                                    // 根据上面获取的文件 fd ,将日志消息写入文件
                        printBinary(context, &log_msg);
                    } else {
                        processBuffer(context, dev, &log_msg);
                    }

printBinary() 函数为例:

logcat.cpp # printBinary() :

void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
    size_t size = buf->len();

    TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
}



作者:猫咪不吃鱼
链接:https://www.jianshu.com/p/26252ee91726
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值