android log机制——输出log
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.
更多相关文章
- 关于android的animation的xml定义中的android:interpolator属性
- Android中自定义带图标和清空内容按钮的EditText控件
- Android 导航条效果实现(四) ViewPager+自定义导航条
- Android 更新升级下载 自定义Updates 兼容版
- 【android】checkedTextView形成自定义ListView
- android自定义滚动条(ScrollBar)样式
- android animation中的参数interpolator详解
- Android中自定义属性(attrs.xml,TypedArray的使用)