Android中Log相关流程介绍
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Android 中 Log 相关
Author: E-mail: Date : PS : 灯下野狐 dengxiayehu@ 2012-03 欢迎交流,转载请务必注明出处。
=================================================================== 在 C\C++层写入 log 的简单示例: #define LOG_TAG "SimpleTest" #include <utils/Log.h> int main(int argc, const char { LOGV("Use LOGV() to print LOGI("Use LOGI() to print LOGD("Use LOGD() to print LOGW("Use LOGW() to print LOGE("Use LOGE() to print return(0); } **argv) me!"); me!"); me!"); me!"); me!");
return write_to_log(log_id, vec, 3); } 上面的 write_to_log 实际是一个函数指针,初始指向__write_to_log_init()函数,具体如下: static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nrg)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; 所以先看下__write_to_log_init()函数,具体如下: =================================================================== >> 相关全局变量及宏定义 #ifdef HAVE_PTHREADS static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif typedef enum { LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_MAX } log_id_t; static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; #define LOGGER_LOG_MAIN "log/main" #define LOGGER_LOG_RADIO "log/radio" #define LOGGER_LOG_EVENTS "log/events" #define LOGGER_LOG_SYSTEM "log/system" =================================================================== static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { #ifdef HAVE_PTHREADS pthread_mutex_lock(&log_init_lock); #endif if (write_to_log == __write_to_log_init) { // 一开是走这里,调用 log_open()函数打开 log 驱动 log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); write_to_log = __write_to_log_kernel; if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || log_fds[LOG_ID_EVENTS] < 0) { log_close(log_fds[LOG_ID_MAIN]); log_close(log_fds[LOG_ID_RADIO]); log_close(log_fds[LOG_ID_EVENTS]); log_fds[LOG_ID_MAIN] = -1; log_fds[LOG_ID_RADIO] = -1; log_fds[LOG_ID_EVENTS] = -1; write_to_log = __write_to_log_null; } if (log_fds[LOG_ID_SYSTEM] < 0) { // 将 system 大类的日志信息“重定向”到 main 大类中 log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; } } #ifdef HAVE_PTHREADS pthread_mutex_unlock(&log_init_lock); #endif return write_to_log(log_id, vec, nr); } 上面的 log_open 也是一个宏(open()函数),所以这里就需要转入 log 的驱动层,看下其大致流程。 (代码路径:kernel/drivers/android/logger.c) 最先找到了 device_initcall 这个宏,传入的参数是 logger_init,所以整个 log 日志驱动的初始化函数就是它了, 看下具体定义: =================================================================== >> 相关宏、结构体 #define LOGGER_LOG_MAIN "log/main" // 看到下面定义了 3 个 logger_log 对象,分别对应 main、events、radio 大类的日志信息的驱动 DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024) DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 64*1024) DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024) // 单条日志最长 4k 字节 #define LOGGER_ENTRY_MAX_LEN // 最大有效负载为日志最大长度-日志头结构体 (4*1024)
使用 logcat 查看结果: W/SimpleTest( 683): Use LOGW() to print me! E/SimpleTest( 683): Use LOGE() to print me! 我们这里只走一下 LOGV 的流程,其他类似。 看到用 LOGV、LOGI、LOGD 等宏打印的日志信息没有出来,具体原因见下。 =================================================================== 在 C\C++层是通过一些预定义的宏来使用 Android 的日志系统的,对应文件为:system/core/include/cutils/log.h 看下 LOGV 的具体定义: /* * Simplified macro to send a verbose log message using the current LOG_TAG. */ #ifndef LOGV #if LOG_NDEBUG #define LOGV(...) ((void)0) #else #define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #endif #endif 开始 LOGV 这个宏肯定未被定义过,然后判断 LOG_NDEBUG 宏的值是否非 0,若是,则 LOGV 日志被忽略,否则就会可能被保 留(到这里只能说可能,因为不知 LOG 这个宏做了哪些事情,不知其是否会过滤掉此日志)。 接下来看 LOG 这个宏: /* * Basic log message macro. * * Example: * LOG(LOG_WARN, NULL, "Failed with error %d", errno); * * The second argument may be NULL or "" to indicate the "global" tag. * 说明:"global" tag,效果就是日志信息的 tag 那项为空而已。 */ #ifndef LOG #define LOG(priority, tag, ...) \ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) #endif 看到实际做事的是 LOG_PRI 宏,结合上面的 LOGV 宏,等价于如下的调用语句: LOG_PRI(ANDROID_LOG_VERBOSE, tag, __VA_ARGS__) 其中 LOG_PRI 宏可自定义日志级别,具体如下: =================================================================== >> 相关枚举量 enum { ANDROID_LOG_UNKNOWN = 0, ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ }; =================================================================== /* * Log macro that allows you to specify a number for the priority. * * Since all of the above variations end up here, this is where the lower * priority messages can be filtered out. The if statement below can be * optimizaed out by the compiler since all of the expressions are
* constant. * 翻译:(英文不大行,将就了~) * 由于上面所有的变种(各种以 LOG 开头用于记录日志的宏)都在这里结束,低级别的日志信息可以在这里被过滤掉。下面 * 的这个 if 语句由于它的所有表达式都是宏定义,若以可以被编译器定制化。 */ #ifndef LOG_PRI #define LOG_PRI(priority, tag, ...) \ ({ \ if (((priority == ANDROID_LOG_VERBOSE) && (LOG_NDEBUG == 0)) || \ ((priority == ANDROID_LOG_DEBUG) && (LOG_NDDEBUG == 0)) || \ ((priority == ANDROID_LOG_INFO) && (LOG_NIDEBUG == 0)) || \ (priority == ANDROID_LOG_WARN) || \ (priority == ANDROID_LOG_ERROR) || \ (priority == ANDROID_LOG_FATAL)) \ (void)android_printLog(priority, tag, __VA_ARGS__); \ }) #endif 看到,假如未定义 LOG_NDEBUG 这个宏为 0,那么默认 LOG_VERBOSE 级别的日志信息是不会显示的;LOG_DEBUG、 LOG_INFO 同理。而对于 LOG_WARN、LOG_ERROR、LOG_FATAL 级别的信息则肯定会被显示出来。 到这里我们看到,对于 LOGV 级别的日志信息,必需经过“两道关卡”才能打印出来。 下面看下 android_printLog()这个函数(实际也是一个宏): /* * =========================================================================== * * The stuff in the rest of this file should not be used directly. */ #define android_printLog(prio, tag, fmt...) \ __android_log_print(prio, tag, fmt) 这里__android_log_print()终于是一个函数了,在 system/core/liblog/logd_write.c 中定义,具体如下: int __android_log_print(int prio, const char *tag, const char *fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); // 组装可变参成字符串 vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); return __android_log_write(prio, tag, buf); } 看下__android_log_write()这个函数: =================================================================== >> 相关枚举量 typedef enum { LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_MAX } log_id_t; =================================================================== int __android_log_write(int prio, const char *tag, const char *msg) { // 使用 struct iovec 结构体变量数组作为载体和驱动通信的话可以一次传递多个 buffer 信息,提高效率 struct iovec vec[3]; log_id_t log_id = LOG_ID_MAIN; // 默认是写入到 main 大类日志驱动中 // 此时 tag 的值为"SimpleTest" if (!tag) tag = ""; /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS")) { // 判断是否是无线相关的日志信息 log_id = LOG_ID_RADIO; } vec[0].iov_base vec[0].iov_len vec[1].iov_base vec[1].iov_len vec[2].iov_base vec[2].iov_len = = = = = = (unsigned char *) &prio; 1; (void *) tag; strlen(tag) + 1; (void *) msg; strlen(msg) + 1; // 先写入日志级别,1 个字节 // 再写入日志 tag,"SimpleTest",11 字节 // 最后写入日志内容 // 注意这里已经将 tag 和 msg 的'\0'考虑进去了
Author: E-mail: Date : PS : 灯下野狐 dengxiayehu@ 2012-03 欢迎交流,转载请务必注明出处。
=================================================================== 在 C\C++层写入 log 的简单示例: #define LOG_TAG "SimpleTest" #include <utils/Log.h> int main(int argc, const char { LOGV("Use LOGV() to print LOGI("Use LOGI() to print LOGD("Use LOGD() to print LOGW("Use LOGW() to print LOGE("Use LOGE() to print return(0); } **argv) me!"); me!"); me!"); me!"); me!");
return write_to_log(log_id, vec, 3); } 上面的 write_to_log 实际是一个函数指针,初始指向__write_to_log_init()函数,具体如下: static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nrg)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; 所以先看下__write_to_log_init()函数,具体如下: =================================================================== >> 相关全局变量及宏定义 #ifdef HAVE_PTHREADS static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif typedef enum { LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_MAX } log_id_t; static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; #define LOGGER_LOG_MAIN "log/main" #define LOGGER_LOG_RADIO "log/radio" #define LOGGER_LOG_EVENTS "log/events" #define LOGGER_LOG_SYSTEM "log/system" =================================================================== static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { #ifdef HAVE_PTHREADS pthread_mutex_lock(&log_init_lock); #endif if (write_to_log == __write_to_log_init) { // 一开是走这里,调用 log_open()函数打开 log 驱动 log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); write_to_log = __write_to_log_kernel; if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || log_fds[LOG_ID_EVENTS] < 0) { log_close(log_fds[LOG_ID_MAIN]); log_close(log_fds[LOG_ID_RADIO]); log_close(log_fds[LOG_ID_EVENTS]); log_fds[LOG_ID_MAIN] = -1; log_fds[LOG_ID_RADIO] = -1; log_fds[LOG_ID_EVENTS] = -1; write_to_log = __write_to_log_null; } if (log_fds[LOG_ID_SYSTEM] < 0) { // 将 system 大类的日志信息“重定向”到 main 大类中 log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; } } #ifdef HAVE_PTHREADS pthread_mutex_unlock(&log_init_lock); #endif return write_to_log(log_id, vec, nr); } 上面的 log_open 也是一个宏(open()函数),所以这里就需要转入 log 的驱动层,看下其大致流程。 (代码路径:kernel/drivers/android/logger.c) 最先找到了 device_initcall 这个宏,传入的参数是 logger_init,所以整个 log 日志驱动的初始化函数就是它了, 看下具体定义: =================================================================== >> 相关宏、结构体 #define LOGGER_LOG_MAIN "log/main" // 看到下面定义了 3 个 logger_log 对象,分别对应 main、events、radio 大类的日志信息的驱动 DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024) DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 64*1024) DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024) // 单条日志最长 4k 字节 #define LOGGER_ENTRY_MAX_LEN // 最大有效负载为日志最大长度-日志头结构体 (4*1024)
使用 logcat 查看结果: W/SimpleTest( 683): Use LOGW() to print me! E/SimpleTest( 683): Use LOGE() to print me! 我们这里只走一下 LOGV 的流程,其他类似。 看到用 LOGV、LOGI、LOGD 等宏打印的日志信息没有出来,具体原因见下。 =================================================================== 在 C\C++层是通过一些预定义的宏来使用 Android 的日志系统的,对应文件为:system/core/include/cutils/log.h 看下 LOGV 的具体定义: /* * Simplified macro to send a verbose log message using the current LOG_TAG. */ #ifndef LOGV #if LOG_NDEBUG #define LOGV(...) ((void)0) #else #define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #endif #endif 开始 LOGV 这个宏肯定未被定义过,然后判断 LOG_NDEBUG 宏的值是否非 0,若是,则 LOGV 日志被忽略,否则就会可能被保 留(到这里只能说可能,因为不知 LOG 这个宏做了哪些事情,不知其是否会过滤掉此日志)。 接下来看 LOG 这个宏: /* * Basic log message macro. * * Example: * LOG(LOG_WARN, NULL, "Failed with error %d", errno); * * The second argument may be NULL or "" to indicate the "global" tag. * 说明:"global" tag,效果就是日志信息的 tag 那项为空而已。 */ #ifndef LOG #define LOG(priority, tag, ...) \ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) #endif 看到实际做事的是 LOG_PRI 宏,结合上面的 LOGV 宏,等价于如下的调用语句: LOG_PRI(ANDROID_LOG_VERBOSE, tag, __VA_ARGS__) 其中 LOG_PRI 宏可自定义日志级别,具体如下: =================================================================== >> 相关枚举量 enum { ANDROID_LOG_UNKNOWN = 0, ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ }; =================================================================== /* * Log macro that allows you to specify a number for the priority. * * Since all of the above variations end up here, this is where the lower * priority messages can be filtered out. The if statement below can be * optimizaed out by the compiler since all of the expressions are
* constant. * 翻译:(英文不大行,将就了~) * 由于上面所有的变种(各种以 LOG 开头用于记录日志的宏)都在这里结束,低级别的日志信息可以在这里被过滤掉。下面 * 的这个 if 语句由于它的所有表达式都是宏定义,若以可以被编译器定制化。 */ #ifndef LOG_PRI #define LOG_PRI(priority, tag, ...) \ ({ \ if (((priority == ANDROID_LOG_VERBOSE) && (LOG_NDEBUG == 0)) || \ ((priority == ANDROID_LOG_DEBUG) && (LOG_NDDEBUG == 0)) || \ ((priority == ANDROID_LOG_INFO) && (LOG_NIDEBUG == 0)) || \ (priority == ANDROID_LOG_WARN) || \ (priority == ANDROID_LOG_ERROR) || \ (priority == ANDROID_LOG_FATAL)) \ (void)android_printLog(priority, tag, __VA_ARGS__); \ }) #endif 看到,假如未定义 LOG_NDEBUG 这个宏为 0,那么默认 LOG_VERBOSE 级别的日志信息是不会显示的;LOG_DEBUG、 LOG_INFO 同理。而对于 LOG_WARN、LOG_ERROR、LOG_FATAL 级别的信息则肯定会被显示出来。 到这里我们看到,对于 LOGV 级别的日志信息,必需经过“两道关卡”才能打印出来。 下面看下 android_printLog()这个函数(实际也是一个宏): /* * =========================================================================== * * The stuff in the rest of this file should not be used directly. */ #define android_printLog(prio, tag, fmt...) \ __android_log_print(prio, tag, fmt) 这里__android_log_print()终于是一个函数了,在 system/core/liblog/logd_write.c 中定义,具体如下: int __android_log_print(int prio, const char *tag, const char *fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); // 组装可变参成字符串 vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); return __android_log_write(prio, tag, buf); } 看下__android_log_write()这个函数: =================================================================== >> 相关枚举量 typedef enum { LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_MAX } log_id_t; =================================================================== int __android_log_write(int prio, const char *tag, const char *msg) { // 使用 struct iovec 结构体变量数组作为载体和驱动通信的话可以一次传递多个 buffer 信息,提高效率 struct iovec vec[3]; log_id_t log_id = LOG_ID_MAIN; // 默认是写入到 main 大类日志驱动中 // 此时 tag 的值为"SimpleTest" if (!tag) tag = ""; /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS")) { // 判断是否是无线相关的日志信息 log_id = LOG_ID_RADIO; } vec[0].iov_base vec[0].iov_len vec[1].iov_base vec[1].iov_len vec[2].iov_base vec[2].iov_len = = = = = = (unsigned char *) &prio; 1; (void *) tag; strlen(tag) + 1; (void *) msg; strlen(msg) + 1; // 先写入日志级别,1 个字节 // 再写入日志 tag,"SimpleTest",11 字节 // 最后写入日志内容 // 注意这里已经将 tag 和 msg 的'\0'考虑进去了