zoukankan      html  css  js  c++  java
  • android log机制——输出log【转】

     

    转自: http://my.oschina.net/wolfcs/blog/164624

    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  */
    04 public static final int VERBOSE = 2;
    05  
    06 /**
    07  * Priority constant for the println method; use Log.d.
    08  */
    09 public static final int DEBUG = 3;
    10  
    11 /**
    12  * Priority constant for the println method; use Log.i.
    13  */
    14 public static final int INFO = 4;
    15  
    16 /**
    17  * Priority constant for the println method; use Log.w.
    18  */
    19 public static final int WARN = 5;
    20  
    21 /**
    22  * Priority constant for the println method; use Log.e.
    23  */
    24 public static final int ERROR = 6;
    25  
    26 /**
    27  * Priority constant for the println method.
    28  */
    29 public 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中的一些:

    01 public static int v(String tag, String msg, Throwable tr) {
    02     return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + ' ' + 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  */
    11 public 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,为:

    01 static 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  */
    33 static 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,为:

    01 int __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():

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

    __write_to_log_init()的实现如下:

    01 static 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函数:

    01 static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
    02 {
    03     return -1;
    04 }
    05  
    06 static 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()函数:

    01 void 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  */
    04 typedef 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中:

    01 int __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如下:

    01 int __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  
    3 void 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中:

    1 int __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.

  • 相关阅读:
    个人冲刺二(7)
    个人冲刺二(6)
    个人冲刺二(5)
    个人冲刺二(4)
    对称二叉树 · symmetric binary tree
    108 Convert Sorted Array to Binary Search Tree数组变成高度平衡的二叉树
    530.Minimum Absolute Difference in BST 二叉搜索树中的最小差的绝对值
    pp 集成工程师 mism师兄问一问
    17. Merge Two Binary Trees 融合二叉树
    270. Closest Binary Search Tree Value 二叉搜索树中,距离目标值最近的节点
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5799854.html
Copyright © 2011-2022 走看看