分享

android log机制——输出log

 jiffes 2014-03-12

android log机制——输出log

发表于5个月前(2013-09-27 15:27)   阅读(412) | 评论(00人收藏此文章, 我要收藏
0

android log系统。

在android Java code中输出log

android系统有4种类型、6个优先级的log,有一些常量用于标识这些信息,相关的定义在frameworks/base/core/java/android/util/Log.java中可以看到:

01/**
02 * Priority constant for the println method; use Log.v.
03 */
04public static final int VERBOSE = 2;
05 
06/**
07 * Priority constant for the println method; use Log.d.
08 */
09public static final int DEBUG = 3;
10 
11/**
12 * Priority constant for the println method; use Log.i.
13 */
14public static final int INFO = 4;
15 
16/**
17 * Priority constant for the println method; use Log.w.
18 */
19public static final int WARN = 5;
20 
21/**
22 * Priority constant for the println method; use Log.e.
23 */
24public static final int ERROR = 6;
25 
26/**
27 * Priority constant for the println method.
28 */
29public static final int ASSERT = 7;
30 
31/** @hide */ public static final int LOG_ID_MAIN = 0;
32/** @hide */ public static final int LOG_ID_RADIO = 1;
33/** @hide */ public static final int LOG_ID_EVENTS = 2;
34/** @hide */ public static final int LOG_ID_SYSTEM = 3;

Java层可以通过三个class来输出其中三种类型的log,三种类型分别为MAIN、RADIO和SYSTEM,三个class分别为Log、Rlog和Slog,其package则分别为android.util、android.telephony和 android.util。这些用于打印log的classes,其构造函数都为private,因而都不能创建其对象,但它们都提供了静态方法来给用户打印log。各个log打印class的实现都大同小异,可以看一下Log这个class中的一些:

01public static int v(String tag, String msg, Throwable tr) {
02    return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
03}
04 
05/**
06 * Send a {@link #DEBUG} log message.
07 * @param tag Used to identify the source of a log message.  It usually identifies
08 *        the class or activity where the log call occurs.
09 * @param msg The message you would like logged.
10 */
11public static int d(String tag, String msg) {
12    return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
13}

最终都会是调用Log.println_native()静态native方法来打印log,各个类中各个方法的不同之处也仅在于参数的差异。

Log.println_native()方法

这个方法的code在/frameworks/base/core/jni/android_util_Log.cpp,为:

01static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
02        jint bufID, jint priority, jstring tagObj, jstring msgObj)
03{
04    const char* tag = NULL;
05    const char* msg = NULL;
06 
07    if (msgObj == NULL) {
08        jniThrowNullPointerException(env, "println needs a message");
09        return -1;
10    }
11 
12    if (bufID < 0 || bufID >= LOG_ID_MAX) {
13        jniThrowNullPointerException(env, "bad bufID");
14        return -1;
15    }
16 
17    if (tagObj != NULL)
18        tag = env->GetStringUTFChars(tagObj, NULL);
19    msg = env->GetStringUTFChars(msgObj, NULL);
20 
21    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
22 
23    if (tag != NULL)
24        env->ReleaseStringUTFChars(tagObj, tag);
25    env->ReleaseStringUTFChars(msgObj, msg);
26 
27    return res;
28}
29 
30/*
31 * JNI registration.
32 */
33static JNINativeMethod gMethods[] = {
34    /* name, signature, funcPtr */
35    { "isLoggable",      "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
36    { "println_native""(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
37};

可以看到,干的都是转换参数的事情,最终再call到__android_log_buf_write()函数,这个函数的定义在system/core/liblog/logd_write.c,为:

01int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
02{
03    struct iovec vec[3];
04    char tmp_tag[32];
05 
06    if (!tag)
07        tag = "";
08 
09    /* XXX: This needs to go! */
10    if ((bufID != LOG_ID_RADIO) &&
11         (!strcmp(tag, "HTC_RIL") ||
12        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
13        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
14        !strcmp(tag, "AT") ||
15        !strcmp(tag, "GSM") ||
16        !strcmp(tag, "STK") ||
17        !strcmp(tag, "CDMA") ||
18        !strcmp(tag, "PHONE") ||
19        !strcmp(tag, "SMS"))) {
20            bufID = LOG_ID_RADIO;
21            // Inform third party apps/ril/radio.. to use Rlog or RLOG
22            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
23            tag = tmp_tag;
24    }
25 
26    vec[0].iov_base   = (unsigned char *) &prio;
27    vec[0].iov_len    = 1;
28    vec[1].iov_base   = (void *) tag;
29    vec[1].iov_len    = strlen(tag) + 1;
30    vec[2].iov_base   = (void *) msg;
31    vec[2].iov_len    = strlen(msg) + 1;
32 
33    return write_to_log(bufID, vec, 3);
34}

做了三件事情,一是根据log的tag,转换bufID,二是用传进来的参数构造一个struct iovec数组,三是将前一步构造的数组作为参数调用write_to_log()。write_to_log()是一个函数指针,在开始时,它指向了__write_to_log_init():

1static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;

__write_to_log_init()的实现如下:

01static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
02{
03#ifdef HAVE_PTHREADS
04    pthread_mutex_lock(&log_init_lock);
05#endif
06 
07    if (write_to_log == __write_to_log_init) {
08        log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
09        log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
10        log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
11        log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
12 
13        write_to_log = __write_to_log_kernel;
14 
15        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
16                log_fds[LOG_ID_EVENTS] < 0) {
17            log_close(log_fds[LOG_ID_MAIN]);
18            log_close(log_fds[LOG_ID_RADIO]);
19            log_close(log_fds[LOG_ID_EVENTS]);
20            log_fds[LOG_ID_MAIN] = -1;
21            log_fds[LOG_ID_RADIO] = -1;
22            log_fds[LOG_ID_EVENTS] = -1;
23            write_to_log = __write_to_log_null;
24        }
25 
26        if (log_fds[LOG_ID_SYSTEM] < 0) {
27            log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
28        }
29    }
30 
31#ifdef HAVE_PTHREADS
32    pthread_mutex_unlock(&log_init_lock);
33#endif
34 
35    return write_to_log(log_id, vec, nr);
36}

这个地方,会检查write_to_log是否指向了__write_to_log_init,也就是是否是第一次打印log,如果是,则打开几个用于输出log的设备文件,然后使write_to_log函数指针指向__write_to_log_kernel,或者在打开输出log设备文件出现异常时,使write_to_log指向__write_to_log_null,最后再次调用经过了重定向的write_to_log,也就是__write_to_log_kernel或者__write_to_log_null函数。我们可以看一下那几个设备文件究竟是什麽(在system/core/include/cutils/logger.h):

1#define LOGGER_LOG_MAIN     "log/main"
2#define LOGGER_LOG_RADIO    "log/radio"
3#define LOGGER_LOG_EVENTS   "log/events"
4#define LOGGER_LOG_SYSTEM   "log/system"

接着继续来看__write_to_log_kernel或者__write_to_log_null函数:

01static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
02{
03    return -1;
04}
05 
06static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
07{
08    ssize_t ret;
09    int log_fd;
10 
11    if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
12        log_fd = log_fds[(int)log_id];
13    } else {
14        return EBADF;
15    }
16 
17    do {
18        ret = log_writev(log_fd, vec, nr);
19    } while (ret < 0 && errno == EINTR);
20 
21    return ret;
22}

由log_id获取到对应的log_fd,然后调用log_writev()打印log。可以看一下log_writev()的定义,它是一个宏:

01#if FAKE_LOG_DEVICE
02// This will be defined when building for the host.
03#define log_open(pathname, flags) fakeLogOpen(pathname, flags)
04#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
05#define log_close(filedes) fakeLogClose(filedes)
06#else
07#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
08#define log_writev(filedes, vector, count) writev(filedes, vector, count)
09#define log_close(filedes) close(filedes)
10#endif

这些就都是标准的unix系统调用了。

本地层代码Log输出

以一些比较典型的native代码打印log的case为例。先来看一下,在JNI的code中打印log的方法。在JNI中,比较常见到用ALOGx这一组宏来打印log,比如在frameworks/base/core/jni/android/graphics/TextLayoutCache.cpp这个文件中的dumpCacheStats()函数:

01void TextLayoutCache::dumpCacheStats() {
02    float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
03    float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
04 
05    size_t cacheSize = mCache.size();
06 
07    ALOGD("------------------------------------------------");
08    ALOGD("Cache stats");
09    ALOGD("------------------------------------------------");
10    ALOGD("pid       : %d", getpid());
11    ALOGD("running   : %.0f seconds", timeRunningInSec);
12    ALOGD("entries   : %d", cacheSize);
13    ALOGD("max size  : %d bytes", mMaxSize);
14    ALOGD("used      : %d bytes according to mSize", mSize);
15    ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);
16    ALOGD("hits      : %d", mCacheHitCount);
17    ALOGD("saved     : %0.6f ms", mNanosecondsSaved * 0.000001f);
18    ALOGD("------------------------------------------------");
19}

使用这组宏,需要定义另外一个宏来作为所打印log的tag:

1#define LOG_TAG "TextLayoutCache"

此外,还要include头文件<cutils/log.h>。来看一下这些宏中的一些的定义:

01/*
02 * Simplified macro to send a debug log message using the current LOG_TAG.
03 */
04#ifndef ALOGD
05#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
06#endif
07 
08/*
09 * Simplified macro to send a warning log message using the current LOG_TAG.
10 */
11#ifndef ALOGW
12#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
13#endif
14 
15/*
16 * Basic log message macro.
17 *
18 * Example:
19 *  ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
20 *
21 * The second argument may be NULL or "" to indicate the "global" tag.
22 */
23#ifndef ALOG
24#define ALOG(priority, tag, ...) \
25    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
26#endif
27 
28/*
29 * Log macro that allows you to specify a number for the priority.
30 */
31#ifndef LOG_PRI
32#define LOG_PRI(priority, tag, ...) \
33    android_printLog(priority, tag, __VA_ARGS__)
34#endif
35 
36#define android_printLog(prio, tag, fmt...) \
37    __android_log_print(prio, tag, fmt)

先来看一下,在native层中定义的priority(在system/core/include/android/log.h中):

01/*
02 * Android log priority values, in ascending priority order.
03 */
04typedef enum android_LogPriority {
05    ANDROID_LOG_UNKNOWN = 0,
06    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
07    ANDROID_LOG_VERBOSE,
08    ANDROID_LOG_DEBUG,
09    ANDROID_LOG_INFO,
10    ANDROID_LOG_WARN,
11    ANDROID_LOG_ERROR,
12    ANDROID_LOG_FATAL,
13    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
14} android_LogPriority;

另外,这些宏最终都会call到__android_log_print(),也是在system/core/liblog/logd_write.c中:

01int __android_log_print(int prio, const char *tag, const char *fmt, ...)
02{
03    va_list ap;
04    char buf[LOG_BUF_SIZE];
05 
06    va_start(ap, fmt);
07    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
08    va_end(ap);
09 
10    return __android_log_write(prio, tag, buf);
11}

先是格式化参数,然后就是调用__android_log_write()函数。这个函数的code如下:

01int __android_log_write(int prio, const char *tag, const char *msg)
02{
03    struct iovec vec[3];
04    log_id_t log_id = LOG_ID_MAIN;
05    char tmp_tag[32];
06 
07    if (!tag)
08        tag = "";
09 
10    /* XXX: This needs to go! */
11    if (!strcmp(tag, "HTC_RIL") ||
12        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
13        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
14        !strcmp(tag, "AT") ||
15        !strcmp(tag, "GSM") ||
16        !strcmp(tag, "STK") ||
17        !strcmp(tag, "CDMA") ||
18        !strcmp(tag, "PHONE") ||
19        !strcmp(tag, "SMS")) {
20            log_id = LOG_ID_RADIO;
21            // Inform third party apps/ril/radio.. to use Rlog or RLOG
22            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
23            tag = tmp_tag;
24    }
25 
26    vec[0].iov_base   = (unsigned char *) &prio;
27    vec[0].iov_len    = 1;
28    vec[1].iov_base   = (void *) tag;
29    vec[1].iov_len    = strlen(tag) + 1;
30    vec[2].iov_base   = (void *) msg;
31    vec[2].iov_len    = strlen(msg) + 1;
32 
33    return write_to_log(log_id, vec, 3);
34}

这个函数与我们前面看到的__android_log_buf_write()非常相似。所不同的就是这个函数没有log_id参数,因而它默认是输出MAIN log,当log的TAG为某些特殊字串时,则输出RADIO log。最后同样是调用write_to_log这个函数指针来输出log。

我们再来看一个skia里面打log的SkDebugf()函数的实现:

1#include <android/log.h>
2 
3void SkDebugf(const char format[], ...) {
4    va_list args;
5    va_start(args, format);
6    __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, format, args);
7    va_end(args);
8}

call到了__android_log_vprint()来输出log,__android_log_vprint()的定义也在system/core/liblog/logd_write.c中:

1int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
2{
3    char buf[LOG_BUF_SIZE];
4 
5    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
6 
7    return __android_log_write(prio, tag, buf);
8}

一样是__android_log_write()函数。

Done.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多