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.

  • 相关阅读:
    RAC连接时的2种方式Connect Time Failver和taf
    ElasticSearch Root身份运行
    sql in按照指定顺序排序
    JAVA字符串格式化-String.format()的使用
    java.lang.IllegalArgumentException: An invalid domain [.test.com] was specified for this cookie
    全文搜索引擎 Elasticsearch 入门教程
    监控页面后退前进,浏览器文档加载事件之pageshow、pagehide
    ios 上浏览器返回上一页不会刷新页面问题,页面初始化的方法不执行
    SQL之case when then用法
    MySQL表的四种分区类型
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5799854.html
Copyright © 2011-2022 走看看