android log系统。

在android Java code中输出log

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

    /**     * Priority constant for the println method; use Log.v.     */    public static final int VERBOSE = 2;    /**     * Priority constant for the println method; use Log.d.     */    public static final int DEBUG = 3;    /**     * Priority constant for the println method; use Log.i.     */    public static final int INFO = 4;    /**     * Priority constant for the println method; use Log.w.     */    public static final int WARN = 5;    /**     * Priority constant for the println method; use Log.e.     */    public static final int ERROR = 6;    /**     * Priority constant for the println method.     */    public static final int ASSERT = 7;    /** @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;

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中的一些:

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

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

Log.println_native()方法

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

static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,        jint bufID, jint priority, jstring tagObj, jstring msgObj){    const char* tag = NULL;    const char* msg = NULL;    if (msgObj == NULL) {        jniThrowNullPointerException(env, "println needs a message");        return -1;    }    if (bufID < 0 || bufID >= LOG_ID_MAX) {        jniThrowNullPointerException(env, "bad bufID");        return -1;    }    if (tagObj != NULL)        tag = env->GetStringUTFChars(tagObj, NULL);    msg = env->GetStringUTFChars(msgObj, NULL);    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);    if (tag != NULL)        env->ReleaseStringUTFChars(tagObj, tag);    env->ReleaseStringUTFChars(msgObj, msg);    return res;}/* * JNI registration. */static JNINativeMethod gMethods[] = {    /* name, signature, funcPtr */    { "isLoggable",      "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },    { "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },};

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

int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg){    struct iovec vec[3];    char tmp_tag[32];    if (!tag)        tag = "";    /* XXX: This needs to go! */    if ((bufID != LOG_ID_RADIO) &&         (!strcmp(tag, "HTC_RIL") ||        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" 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;            // Inform third party apps/ril/radio.. to use Rlog or RLOG            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);            tag = tmp_tag;    }    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);}

做了三件事情,一是根据log的tag,转换bufID,二是用传进来的参数构造一个struct iovec数组,三是将前一步构造的数组作为参数调用write_to_log()。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;

__write_to_log_init()的实现如下:

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_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) {            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);}

这个地方,会检查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):

#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_log_kernel或者__write_to_log_null函数:

static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr){    return -1;}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_fd,然后调用log_writev()打印log。可以看一下log_writev()的定义,它是一个宏:

#if FAKE_LOG_DEVICE// This will be defined when building for the host.#define log_open(pathname, flags) fakeLogOpen(pathname, flags)#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)#define log_close(filedes) fakeLogClose(filedes)#else#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)#define log_writev(filedes, vector, count) writev(filedes, vector, count)#define log_close(filedes) close(filedes)#endif

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

本地层代码Log输出

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

void TextLayoutCache::dumpCacheStats() {    float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));    float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;    size_t cacheSize = mCache.size();    ALOGD("------------------------------------------------");    ALOGD("Cache stats");    ALOGD("------------------------------------------------");    ALOGD("pid       : %d", getpid());    ALOGD("running   : %.0f seconds", timeRunningInSec);    ALOGD("entries   : %d", cacheSize);    ALOGD("max size  : %d bytes", mMaxSize);    ALOGD("used      : %d bytes according to mSize", mSize);    ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);    ALOGD("hits      : %d", mCacheHitCount);    ALOGD("saved     : %0.6f ms", mNanosecondsSaved * 0.000001f);    ALOGD("------------------------------------------------");}

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

#define LOG_TAG "TextLayoutCache"

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

/* * Simplified macro to send a debug log message using the current LOG_TAG. */#ifndef ALOGD#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))#endif/* * Simplified macro to send a warning log message using the current LOG_TAG. */#ifndef ALOGW#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))#endif/* * Basic log message macro. * * Example: *  ALOG(LOG_WARN, NULL, "Failed with error %d", errno); * * The second argument may be NULL or "" to indicate the "global" tag. */#ifndef ALOG#define ALOG(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)

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

/* * Android log priority values, in ascending priority order. */typedef enum android_LogPriority {    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 */} android_LogPriority;

另外,这些宏最终都会call到__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()函数。这个函数的code如下:

int __android_log_write(int prio, const char *tag, const char *msg){    struct iovec vec[3];    log_id_t log_id = LOG_ID_MAIN;    char tmp_tag[32];    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 */        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" 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;            // Inform third party apps/ril/radio.. to use Rlog or RLOG            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);            tag = tmp_tag;    }    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(log_id, vec, 3);}

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

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

#include <android/log.h>void SkDebugf(const char format[], ...) {    va_list args;    va_start(args, format);    __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, format, args);    va_end(args);}

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

int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap){    char buf[LOG_BUF_SIZE];    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);    return __android_log_write(prio, tag, buf);}

一样是__android_log_write()函数。

Done.

更多相关文章

  1. 关于android的animation的xml定义中的android:interpolator属性
  2. Android中自定义带图标和清空内容按钮的EditText控件
  3. Android 导航条效果实现(四) ViewPager+自定义导航条
  4. Android 更新升级下载 自定义Updates 兼容版
  5. 【android】checkedTextView形成自定义ListView
  6. android自定义滚动条(ScrollBar)样式
  7. android animation中的参数interpolator详解
  8. Android中自定义属性(attrs.xml,TypedArray的使用)

随机推荐

  1. Android新手轻松入门十问十答
  2. 刚进入Android终端即可使用busybox的命令
  3. JAVA和Android之较量
  4. 想抢先体验Android操作系统的魅力吗?那就
  5. 零打碎敲学Android(二)—做个拼图游戏吧
  6. Android应用程序的开发
  7. Android中SQLite应用详解
  8. Android应用程序框架层和系统运行库层日
  9. (搬运工)android makefile(android.mk)分
  10. Android广播详解