Android应用程序框架层和系统运行库层日志系统源代码分析
在开开Android开用程序开~少不了使用Log来开控和开开程序的开行。在上一篇文章 Android 日志系开开开程序 Logger 源代开分析中~我开分析了开开程序Logger的源代开~在前面的文章 浅 开 Android 系开开开中 Log 的使用 一文~我开也开开介开在开用程序中使Log的方法~在开篇文章中~我开开开介开将Android开用程序架开和系开行开存开日志系开框运
的源代开~使得我开可以更好地理解Android的日志系开的开开。
我开在Android开用程序~一般是开用开用程序架开的框Java接口;android.util.Log,使用日志系开~开来个Java接口通开JNI方法和系开行开最开开用运内核开开程序Logger把Log写内个到核空开中。按照开开用开程~我开一步步介开Android开用程序架开日志系开的源代开。开完开开程之后~我开可以好地理解框学个很Android系开的架~开用程序开;构即Application,的接口是如何一步一步地开用到核空开的。内
一. 开用程序架开日志系开框Java接口的开开。
在 浅 开 Android 系开开开中 Log 的使用 一文中~我开曾开介开开Android开用程序框架开日志系开的源代开接口。开里~开了描述方便和文章的完整性~我开重新开一下开部份的代开~在frameworks/base/core/java/android/util/Log.java文件中~开开日志系开的Java接口,
view plain
1.................................................
2.
3.public final class Log {
4.
5.................................................
6.
7. /**
8. * Priority constant for the println method; use Log.v.
9. */
10. public static final int VERBOSE = 2;
11.
12. /**
13. * Priority constant for the println method; use Log.d.
14. */
15. public static final int DEBUG = 3;
16.
17. /**
18. * Priority constant for the println method; use Log.i.
19. */
20. public static final int INFO = 4;
21.
22. /**
23. * Priority constant for the println method; use Log.w.
24. */
25. public static final int WARN = 5;
26.
27. /**
28. * Priority constant for the println method; use Log.e.
29. */
30. public static final int ERROR = 6;
31.
32. /**
33. * Priority constant for the println method.
34. */
35. public static final int ASSERT = 7;
36.
37...................................................... 38.
39. public static int v(String tag, String msg) {
40. return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); 41. }
42.
43. public static int v(String tag, String msg, Throwable tr) { 44. return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr)); 45. }
46.
47. public static int d(String tag, String msg) {
48. return println_native(LOG_ID_MAIN, DEBUG, tag, msg); 49. }
50.
51. public static int d(String tag, String msg, Throwable tr) { 52. return println_native(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr)); 53. }
54.
55. public static int i(String tag, String msg) {
56. return println_native(LOG_ID_MAIN, INFO, tag, msg); 57. }
58.
59. public static int i(String tag, String msg, Throwable tr) { 60. return println_native(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr)); 61. }
62.
63. public static int w(String tag, String msg) {
64. return println_native(LOG_ID_MAIN, WARN, tag, msg); 65. }
66.
67. public static int w(String tag, String msg, Throwable tr) { 68. return println_native(LOG_ID_MAIN, WARN, tag, msg + '\n' + getStackTraceString(tr)); 69. }
70.
71. public static int w(String tag, Throwable tr) {
72. return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
73. }
74.
75. public static int e(String tag, String msg) {
76. return println_native(LOG_ID_MAIN, ERROR, tag, msg);
77. }
78.
79. public static int e(String tag, String msg, Throwable tr) {
80. return println_native(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
81. }
82.
83...................................................................
84. /** @hide */ public static native int LOG_ID_MAIN = 0;
85. /** @hide */ public static native int LOG_ID_RADIO = 1;
86. /** @hide */ public static native int LOG_ID_EVENTS = 2;
87. /** @hide */ public static native int LOG_ID_SYSTEM = 3;
88.
89. /** @hide */ public static native int println_native(int bufID,
90. int priority, String tag, String msg);
91.}
定开了2~7一共6个日志开先开开ID和4个冲区日志开ID。回开一下Android
日志系开开开程序 Logger 源代开分析 一文~在Logger开开程序模开中~定开了log_main、log_events和log_radio三日志开~分开开开三开开文个冲区个
件/dev/log/main、/dev/log/events和/dev/log/radio。开里的4个冲区日志开的前面3个ID就是开开开三开开文件的文件描述符了~在下面的章开中~我开看到开三文件个将个
描述符是如何开建的。在下开下的来Android内核源代开中~第4个冲区日志开LOG_ID_SYSTEM并没况它有开开的开开文件~在开开情下~和LOG_ID_MAIN开开同一个冲区开开开ID~在下面的章开中~我开同开可以看到开两个ID是如何开开到同一开开文件个的。
在整个Log接口中~最开开的地方明了声println_native本地方法~所有的Log接口都是通开开用开本地方法开开个来Log的定入。下面我开就开开分析开本地方法个println_native。
二. 开用程序架开日志系开框JNI方法的开开。
在frameworks/base/core/jni/android_util_Log.cpp文件中~开开JNI方法println_native,
view plain
1./* //device/libs/android_runtime/android_util_Log.cpp
2.**
3.** Copyright 2006, The Android Open Source Project
4.**
5.** Licensed under the Apache License, Version 2.0 (the "License");
6.** you may not use this file except in compliance with the License.
7.** You may obtain a copy of the License at
8.**
9.** **
11.** Unless required by applicable law or agreed to in writing, software 12.** distributed under the License is distributed on an "AS IS" BASIS, 13.** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14.** See the License for the specific language governing permissions and 15.** limitations under the License.
16.*/
17.
18.#define LOG_NAMESPACE "log.tag."
19.#define LOG_TAG "Log_println"
20.
21.#include
22.#include
23.#include
24.#include
25.
26.#include "jni.h"
27.#include "utils/misc.h"
28.#include "android_runtime/AndroidRuntime.h" 29.
30.#define MIN(a,b) ((aGetStringUTFChars(tag, NULL);
74.
75. if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
76. jclass clazz = env->FindClass("java/lang/IllegalArgumentException"); 77. char buf2[200];
78. snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n", 79. chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE)); 80.
81. // release the chars!
82. env->ReleaseStringUTFChars(tag, chars); 83.
84. env->ThrowNew(clazz, buf2); 85. return false;
86. } else {
87. strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1); 88. strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars); 89. }
90.
91. env->ReleaseStringUTFChars(tag, chars); 92.
93. len = property_get(key, buf, "");
94. int logLevel = toLevel(buf);
95. return (logLevel >= 0 && level >= logLevel) ? true : false;
96.#endif /* HAVE_ANDROID_OS */
97.}
98.
99./*
100. * In class android.util.Log:
101. * public static native int println_native(int buffer, int priority, String tag, String msg) 102. */
103.static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
104. jint bufID, jint priority, jstring tagObj, jstring msgObj)
105.{
106. const char* tag = NULL;
107. const char* msg = NULL;
108.
109. if (msgObj == NULL) {
110. jclass npeClazz;
111.
112. npeClazz = env->FindClass("java/lang/NullPointerException"); 113. assert(npeClazz != NULL);
114.
115. env->ThrowNew(npeClazz, "println needs a message");
116. return -1;
117. }
118.
119. if (bufID < 0 || bufID >= LOG_ID_MAX) { 120. jclass npeClazz;
121.
122. npeClazz = env->FindClass("java/lang/NullPointerException"); 123. assert(npeClazz != NULL);
124.
125. env->ThrowNew(npeClazz, "bad bufID");
126. return -1;
127. }
128.
129. if (tagObj != NULL)
130. tag = env->GetStringUTFChars(tagObj, NULL); 131. msg = env->GetStringUTFChars(msgObj, NULL); 132.
133. int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
134.
135. if (tag != NULL)
136. env->ReleaseStringUTFChars(tagObj, tag); 137. env->ReleaseStringUTFChars(msgObj, msg); 138.
139. return res;
140.}
141.
142./*
143. * JNI registration.
144. */
145.static JNINativeMethod gMethods[] = {
146. /* name, signature, funcPtr */
147. { "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
148. { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_
println_native },
149.};
150.
151.int register_android_util_Log(JNIEnv* env)
152.{
153. jclass clazz = env->FindClass("android/util/Log");
154.
155. if (clazz == NULL) {
156. LOGE("Can't find android/util/Log");
157. return -1;
158. }
159.
160. levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE
", "I"));
161. levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I
"));
162. levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));
163. levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));
164. levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I
"));
165. levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I
"));
166.
167. return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NEL
EM(gMethods));
168.}
169.
170.}; // namespace android
在gMethods开量中~定开了println_native本地方法开开的函开用是数android_util_Log_println_native。在android_util_Log_println_native函中~通开数了各开开开正后~就开用行开开函参数确运数__android_log_buf_write来开开Log的入操写作。__android_log_buf_write函开开开在liblog开中~有它4个参数冲区~分开开ID、开先开开ID、Tag字符串和Msg字符串。下面行开开运liblog中的
__android_log_buf_write的开开。
三. 系开行开开日志系开的开开。运
在系开行开开运liblog开的开开中~容比开多~开里~我开只开注日志入操作内写
__android_log_buf_write的相开开开,
view plain
1.int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
2.{
3. struct iovec vec[3];
4.
5. if (!tag)
6. tag = "";
7.
8. /* XXX: This needs to go! */
9. if (!strcmp(tag, "HTC_RIL") ||
10. !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ 11. !strcmp(tag, "AT") ||
12. !strcmp(tag, "GSM") ||
13. !strcmp(tag, "STK") ||
14. !strcmp(tag, "CDMA") ||
15. !strcmp(tag, "PHONE") ||
16. !strcmp(tag, "SMS"))
17. bufID = LOG_ID_RADIO;
18.
19. vec[0].iov_base = (unsigned char *) &prio;
20. vec[0].iov_len = 1;
21. vec[1].iov_base = (void *) tag;
22. vec[1].iov_len = strlen(tag) + 1; 23. vec[2].iov_base = (void *) msg;
24. vec[2].iov_len = strlen(msg) + 1; 25.
26. return write_to_log(bufID, vec, 3); 27.}
函首先是开开开开的数来tag参数是否是开
HTC_RIL、RIL、AT、GSM、STK、CDMA、PHONE和SMS中的一~如果是个~
就无件地使用条ID开LOG_ID_RADIO的日志开作开入开~接着~把开开的冲区写冲区来
参数prio、tag和msg分开存放在一向量开中~开用个数write_to_log函开入下一数来
步操作。write_to_log是一函指开~定开在文件开始的位置上,个数
view plain
1.static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); 2.static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
且初始化开并__write_to_log_init函,数view plain
1.static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) 2.{
3.#ifdef HAVE_PTHREADS
4. pthread_mutex_lock(&log_init_lock); 5.#endif
6.
7. if (write_to_log == __write_to_log_init) { 8. log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
9. 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 == __write_to_log_init判开句就断会true~于是开行log_open函打开开开文件~把文数并
件描述符保存在log_fds数开中。如果打开/dev/LOGGER_LOG_SYSTEM文件失开~即log_fds[LOG_ID_SYSTEM] < 0~就把log_fds[LOG_ID_SYSTEM]开置开log_fds[LOG_ID_MAIN]~开就是我开上面描述的如果不存在ID开LOG_ID_SYSTEM的日志开~就把冲区LOG_ID_SYSTEM开置开和LOG_ID_MAIN开开的日志开了冲区。LOGGER_LOG_MAIN、LOGGER_LOG_RADIO、LOGGER_LOG_EVENTS和LOGGER_LOG_SYSTEM四宏定开在个system/core/include/cutils/logger.h文件中,view plain
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函指开指向数__write_to_log_kernel函,数view plain
1.static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
2.{
3. ssize_t ret;
4. int log_fd;
5.
6. if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
7. log_fd = log_fds[(int)log_id];
8. } else {
9. return EBADF;
10. }
11.
12. do {
13. ret = log_writev(log_fd, vec, nr);
14. } while (ret < 0 && errno == EINTR);
15.
16. return ret;
17.}
函开用数log_writev来开开Log的入~注意~开里通开一循开入写个来写Log~直到入成功开止。开里写log_writev是一宏~在文件开始的地方定开开,个view plain
1.#if FAKE_LOG_DEVICE
2.// This will be defined when building for the host.
3.#define log_open(pathname, flags) fakeLogOpen(pathname, flags)
4.#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
5.#define log_close(filedes) fakeLogClose(filedes)
6.#else
7.#define log_open(pathname, flags) open(pathname, flags)
8.#define log_writev(filedes, vector, count) writev(filedes, vector, count)
9.#define log_close(filedes) close(filedes)
10.#endif
开里~我开看到~一般情下~况log_writev就是writev了~开是常开的批量文个件入函~就不多开了。写数
至些~整开用开程就开束了。开开一下~首先是开用程序开开用开用程序架开的个从框
Java接口~开用程序架开的框Java接口通开开用本开的JNI方法开入到系开行开开的运C接口~系开行开开的运C接口通开开开文件开开开核空开开的来内Logger开开程序。开是一典个型的开用开程~好地开开很Android的系开架~希望开者好好开。构会