Logcat使用
介绍
logcat是android中的一个命令行工具,可以用于得到程序的log信息
logcat缓冲区
android log输出量巨大,特别是通信系统的log,因此,android把log输出到不同的缓冲区中,目前定义了四个log缓冲区:
1)Radio:输出通信系统的log
2)System:输出系统组件的log
3)Event:输出event模块的log
4)Main:所有java层的log,遗迹不属于上面3层的log
缓冲区主要给系统组件使用,一般的应用不需要关心,应用的log都输出到main缓冲区中
默认log输出(不指定缓冲区的情况下)是输出System和Main缓冲区的log
logcat命令参数
参数 | 描述 |
-b <buffer> | 加载一个可使用的日志缓冲区供查看,比如event和radio。默认值是main |
-c | 清除缓冲区中的全部日志并退出(清除完后可以使用-g查看缓冲区) |
-d | 将缓冲区的log转存到屏幕中然后退出 |
-f <filename> | 将log输出到指定的文件中<文件名>.默认为标准输出(stdout) |
-g | 打印日志缓冲区的大小并退出 |
-n <count> | 设置日志的最大数目<count>,默认值是4,需要和-r选项一起使用 |
-r <kbytes> | 没<kbytes>时输出日志,默认值是16,需要和-f选项一起使用 |
-s | 设置过滤器 |
-v <format> | 设置输出格式的日志消息。默认是短暂的格式。支持的格式列表 |
一般长时间输出log的话建议-f,-n,-r三个参数连用,这样当一个文件日志输出满了之后可以马上在另一个中进行输出
优先级语法
优先级使用字符标识,一下优先级从低到高
V –Verbose(最低优先级)
D – Debug
I – Info
W – Warning
E – Error
F – Fatal
S – Silent
为了减少不想要日志的输出,可以建立一个过滤器过滤语法:tag:priority
//过滤TAG为ActivityManager输出级别大于I的日志与TAG为MyApp输出级别大于D的日志 adb logcat ActivityManager:I My App:D *:S
adb logcat *:W设置过滤级别为W以上
如果用的比较多可以设置环境变量:export ANDROID_LOG_TAGS="ActivityManager:I MyApp:D*:S"
logcat源码分析
logat源码主要分布在:
system/core/logcat/logcat.cpp system/core/include/cutils/logger.h
使用了liblog库,编译出来的结果是可执行程序logcat。
我们从logcat.cpp的main函数开始分析。
主函数代码:
1 int main(int argc, char **argv) 2 { 3 ...... 4 5 //1. 参数check和解析 6 g_logformat = android_log_format_new(); 7 8 if (argc == 2 && 0 == strcmp(argv[1], "--test")) { 9 logprint_run_tests(); 10 exit(0); 11 } 12 13 if (argc == 2 && 0 == strcmp(argv[1], "--help")) { 14 android::show_help(argv[0]); 15 exit(0); 16 } 17 18 // 参数的继续处理 19 for (;;) { 20 int ret; 21 ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:B"); 22 23 if (ret < 0) { 24 break; 25 } 26 27 switch(ret) { 28 case 's': 29 // default to all silent 30 android_log_addFilterRule(g_logformat, "*:s"); 31 break; 32 33 case 'c': 34 clearLog = 1; 35 mode = O_WRONLY; 36 break; 37 ...... 38 } 39 40 //2. devices的过滤 41 if (!devices) { 42 devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm'); 43 android::g_devCount = 1; 44 int accessmode = 45 (mode & O_RDONLY) ? R_OK : 0 46 | (mode & O_WRONLY) ? W_OK : 0; 47 // only add this if it's available 48 if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) { 49 devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's'); 50 android::g_devCount++; 51 } 52 } 53 54 //dev = devices; 55 while (dev) { 56 dev->fd = open(dev->device, mode); 57 58 ...... 59 60 //3.读取一行 61 android::readLogLines(devices); 62 }
-
根据输入的参数,进行相应的分析和处理:
例如,输入--help,就调用
android::show_help(argv[0]); //显示help信息。
然后进入for无限循环。在for循环中,进一步进行参数处理。包括getopt中的这些值。
ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:B");
函数用于过滤参数中是否包含cdt:gsQf:r::n:v:b:B中的值,并且返回这个值。
对于ret的处理就是switch case
-
对devices的分析:
devices的类型定义在logger.h中,如下:
#define LOGGER_LOG_MAIN "log/main" #define LOGGER_LOG_RADIO "log/radio" #define LOGGER_LOG_EVENTS "log/events" #define LOGGER_LOG_SYSTEM "log/system"
可见,logcat -b后面的参数对应于文件系统中的某一个(几个)文件。
-
readLines函数的实现:
实现原理,实现一个无限循环,采用select多路复用,从
devices
中获取数据,这里的devices就是前面提到的
system, events , radio, main
多路复用select: 监听devices设备,超时时间设置为5ms。将结果通过read函数读取到内存中,即读取到queued_entry_t相关的内存中。
C 输出log到LogCat
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <jni.h> 4 #include <android/log.h> 5 6 #define LOG "c_log" 7 #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG,__VA_ARGS__) 8 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG,__VA_ARGS__) 9 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG,__VA_ARGS__) 10 #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG,__VA_ARGS__) 11 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG,__VA_ARGS__) 12 #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__) 13 14 JNIEXPORT void JNICALL 15 Java_com_myc_jni_Jni_cLog(JNIEnv *env, jobject instance) { 16 LOGV("c输出测试: 等级v"); 17 LOGD("c输出测试: 等级d"); 18 LOGI("c输出测试: 等级i"); 19 LOGW("c输出测试: 等级w"); 20 LOGE("c输出测试: 等级e"); 21 LOGF("c输出测试: 等级f"); 22 }