1. 程式人生 > >android log機制——輸出log

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 + '\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 */
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