最近看了一点android关于log的代码,小结一下。
对开发者来说,Log读主要调用工具logcat,后面附带一些参数,
写的话JAVA或者C/C++都有相应的接口。
代码位置:
frameworks/base/core/java/android/util/Log.java
system/core/liblog
system/core/logcat
frameworks/base/core/jni/android_util_Log.cpp
写过程:
JAVA层的frameworks/base/core/java/android/util/Log.java通过JNI掉本地方法__android_log_buf_write,
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
{
struct iovec vec[3];
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"))
bufID = LOG_ID_RADIO;
vec[0].iov_base = (unsigned char *) &prio;
vec[0].iov_len = 1;
vec[1].iov_base = (void *) tag;
vec[1].iov_len = strlen(tag) + 1;
vec[2].iov_base = (void *) msg;
vec[2].iov_len = strlen(msg) + 1;
return write_to_log(bufID, vec, 3);
}
write_to_log是函数指针,初始化为__write_to_log_init
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
#define LOGGER_LOG_MAIN "log/main"
#define LOGGER_LOG_RADIO "log/radio"
#define LOGGER_LOG_EVENTS "log/events"
#define LOGGER_LOG_SYSTEM "log/system"
初始化设备后,write_to_lo指向__write_to_log_kernel
static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
{
ssize_t ret;
int log_fd;
if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
log_fd = log_fds[(int)log_id];
} else {
return EBADF;
}
do {
ret = log_writev(log_fd, vec, nr);
} while (ret < 0 && errno == EINTR);
return ret;
}
log_id指向对应打开的log设备,然后获取文件描述符,将log信息写入文件,id在Log.java中有定义
/** @hide */ public static final int LOG_ID_MAIN = 0;
/** @hide */ public static final int LOG_ID_RADIO = 1;
/** @hide */ public static final int LOG_ID_EVENTS = 2;
/** @hide */ public static final int LOG_ID_SYSTEM = 3;
对于C/C++,只需引用<utils/Log.h>头文件,并且定义LOG_TAG宏,在log.h中有如下宏定义
#ifndef LOG_TAG
#define LOG_TAG NULL
#endif
因此你注意避免LOG_TAG的定义有冲突
LOG的宏定义如下:
/*
* Simplified macro to send a debug log message using the current LOG_TAG.
*/
#ifndef LOGD
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
/*
* 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.
*/
#ifndef LOG
#define LOG(priority, tag, ...) \
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to specify a number for the priority.
*/
#ifndef LOG_PRI
#define LOG_PRI(priority, tag, ...) \
android_printLog(priority, tag, __VA_ARGS__)
#endif
#define android_printLog(prio, tag, fmt...) \
__android_log_print(prio, tag, fmt)
__android_log_print最后会调用到__android_log_write函数,完成写入过程。
读过程:
可想而知,读的过程也是打开对应的设备文件,读取里面的log信息。
读log一般都是在minicom或adb shell中执行logcat命令
logcat.cpp里的main函数是该命令的入口
其中-b参数选择对应buffer,也就是对应的设备文件,-c清空buffer,-g获取buffer大小,-f设置log输出设备。
获取命令行里的设备列表之后,打开设备,调用readLogLines从设备中读取log信息到queued_entry_t队列中
static void readLogLines(log_device_t* devices)
for (dev=devices; dev; dev = dev->next) {
FD_SET(dev->fd, &readset);
}
result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);
android_log_printLogLine负责将queued_entry_t队列中的log打印到g_outFD描述符的设备里,如果-f没有设置,则打印到标准输出。