一、前言
logcat 作为读取日志的工具,相当于client 的角色;在前两篇文章中,关于 logcat 如何与其他部分沟通获取日志信息的流程已经介绍的比较清晰,本文不在赘述,转而归纳一下 logcat 的一些常用指令,并对其中一些做详细分析
二、命令简介
选项 | 描述 | eg |
---|---|---|
-s | 输出指定 tag 的日志,相当于过滤器表达式 '*:S' | logcat -s tag |
-f |
设置logcat 内容保存的位置,默认是stdout | logcat -f sdcard/log.txt |
-r |
每输出 |
logcat -f sdcard/log.txt -r 1 |
-n |
设置日志输出的最大数目, 需要 -r 参数 | 暂不明白 |
-v |
设置日志消息的输出格式。详见下文 格式化输出 | logcat -v thread |
-D | 输出各个日志缓冲区之间的分隔线 | logcat -D ... |
-c | 清除(清空)所选的缓冲区并退出,默认清除 main、system 和 crash | logcat -c / -b all -c |
-d | 将日志转储到屏幕并退出 | logcat -d > log.txt |
-e |
输出正则匹配的日志消息 | logcat -e 匹配数据 -m 5 |
-m |
输出 |
...... |
-t |
仅输出最新的行数,此选项包括 -d 功能 | logcat -t 5 |
-t ' | 输出自指定时间以来的最新行,此选项包括 -d 功能 | logcat -t '01-26 20:52:41.820' |
-g | 获取指定日志缓冲区的大小并退出 | logcat -g |
-G | 设置日志环形缓冲区的大小,可以在结尾处添加 K 或 M | logcat -G 2M |
-b | 加载可供查看的日志缓冲区,更多可见下文 日志缓冲区 | logcat -b system |
-B | 以二进制文件形式输出日志 | ...... |
-S | 在输出中包含统计信息,以识别和定位日志垃圾信息发送者 | ...... |
--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 | – Debug | adb logcat *:d |
I | – Info | adb logcat *:i |
W | – Warning | adb logcat *:w |
E | – Error | adb logcat *:e |
F | – Fatal | adb logcat *:f |
S | – Silent | adb 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));
}
参考
[ 1 ] Android物语:logcat
[ 2 ] android调试——logcat详解
[ 3 ] 玩转Android10源码开发定制(12)内核篇之logcat输出内核日志