Android Logger Log System
Directory
Directory preface Runtime Library-layer logstore liblog source code analysis CC log writing interface Java log writing interface logcat tool Analysis Basic Data Structure
Runtime Library-liblog
In the Runtime Library layer, the Android system provides a logstore liblog that is used to interact with the Logger log driver. Through the interface provided by the logstore liblog, the application can easily write logs to the Logger log driver.
Both the C/C ++ log writing interface at the Runtime Library layer and the Java log writing interface at the application framework layer are written to and from the Logger log driver through the log writing interface provided by the liblog Library log recorded.
Source code analysis
The log writing interface provided by the logstore liblog is implemented in the logd_write.c file. Its source code is/system/core/liblog/logd_write.c.
Based on the types of log records written, these functions can be divided into three categories:
Functions _ android_log_assert, _ android_log_vprint, and _ android_log_print are used to write logs whose type is main. Functions _ android_log_btwrite and _ android_log_bwrite are used to write log records whose types are events. Function _ android_log_buf_print can write any type of log records.
No matter what type of log records are written, they are eventually written to the Logger log driver by calling the write_to_log function. Write_to_log is a function pointer that points to function _ write_to_log_init at the beginning. Therefore, when the function write_to_log is called for the first time, the function _ write_to_log_init is actually executed. Function _ write_to_log_init initializes logstores, and redirects the function pointer write_to_log to function _ write_to_log_kernel or _ write_to_log_null, depending on whether the log device file can be opened successfully.
The source code analysis is as follows. The source code implementation is as follows:
// Declare it first, and then reference static int _ write_to_log_init (log_id_t, struct iovec * vec, size_t nr); int (* write_to_log) (log_id_t, struct iovec * vec, size_t nr) = _ write_to_log_init; // some definitions are in system/core/include/cutils/log. macro typedef enum {LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_MAX} log_id_t; # define LOGGER_LOG_MAIN log/main # define logging log/radio # define LOGGER_LOG_EVENTS log/events # define LOGGER_LOG_SYSTEM log/system // static int _ counts (log_id_t log_id, struct iovec * vec, size_t nr) {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); // modify the write_to_log function pointer 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 [logs]); log_close (log_fds [logs]); log_fds [LOG_ID_MAIN] =-1; log_fds [logs] =-1; log_fds [LOG_ID_EVENTS] =-1; write_to_log = _ logs;} if (log_fds [LOG_ID_SYSTEM] <0) {log_fds [LOG_ID_SYSTEM] = log_fds [LOG_ID_MAIN] ;}return write_to_log (log_id, vec, nr );}
With the above Code, after replacing the macro definition, yes, you can know that the four log Device files opened by calling log_open are/dev/log/main,/dev/log/radio,/dev/log/events, And/dev/log/system.. Macro log_open is defined in system/core/liblog/logd_write.c:
# If FAKE_LOG_DEVICE // you do not need to care about it here. When compiling, The FAKE_LOG_DEVICE is 0 # 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
From the code above, we can see that the real implementation of log_open is the open function.
Return to the beginning. If the log_open files are all OK, the _ write_to_log_kernel function will be called. The source code is implemented as follows:
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 < (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;}
Function _ write_to_log_kernel finds the corresponding log device file descriptor in the Global Array log_fds Based on the log_id parameter, and then calls the macro log_writev, that is, the function writev, write logs to the Logger log driver.
If the device file fails to be opened, the write_to_log function pointer will be assigned as _ write_to_log_kernel. In fact, this function does nothing, but returns a-1. So we will not post the source code.
Finally, let's analyze the _ android_log_buf_write function. Because the C/C ++ log writing interface and Java log writing interface call this function to write logs. The source code is as follows:
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 = ; if ((bufID != LOG_ID_RADIO) && (!strcmp(tag, HTC_RIL) || (!strncmp(tag, RIL, 3)) || (!strncmp(tag, IMS, 3)) || !strcmp(tag, AT) || !strcmp(tag, GSM) || !strcmp(tag, STK) || !strcmp(tag, CDMA) || !strcmp(tag, PHONE) || !strcmp(tag, SMS))) { bufID = LOG_ID_RADIO; 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); }
By default, the log type written by function _ android_log_write is main. Then, if the uploaded log record starts with the "RIL" mark, it will be considered as a log record of the radio type.
C/C ++ log writing Interface
The Android system provides three groups of common C/C ++ macros to encapsulate log writing interfaces. The purpose of this operation is to enable log switch control for developers. For example, if the log is not enabled in the published version.
The three groups of macros are defined:
ALOGV, ALOGD, ALOGI, ALOGW, and ALOGE. Logs of the main type are recorded. SLOGV, SLOGD, SLOGI, SLOGW, and SLOGE are used to write logs of the system type. LOG_EVENT_INT, LOG_EVENT_LONG, and LOG_EVENT_STRING are used to write log records whose types are events.
These macros are defined in system/core/include/log. h, and a LOG_NDEBUG macro is used as the log switch.
The source code is as follows:
// Log switch # ifndef LOG_NDEBUG # ifdef NDEBUG # define LOG_NDEBUG 1 # else # define LOG_NDEBUG 0 # endif // take ALOGE as an example # ifnded ALOGE # define ALOGE (...) (void) ALOG (LOG_WARN, LOG_TAG, _ VA_ARGS _) # endif # ifndef ALOG # define ALOG (priority, tag ,...) LOG_PRI (ANDROID _ # priority, tag, _ VA_ARGS _) # endif # ifndef LOG_PRI # define LOG_PRI (priority, tag ,...) android_printLog (priority, tag, _ VA_ARGS _) # endif # returns to the familiar _ android_log_print function # define android_printLog (prio, tag, fmt ...) _ android_log_print (prio, tag, fmt)
Java log writing Interface
The Android system defines three Java log writing interfaces in the application framework, which are android. util. log, android. util. slog and android. util. eventLog. The types of log records written are main, system, and events.
Here we mainly analyze the implementation of android. util. log. The source code is as follows:
public final class Log { /** * 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; private Log() { } /** * Send a {@link #VERBOSE} 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 v(String tag, String msg) { return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); } /** * 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); } /** * Send an {@link #INFO} 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 i(String tag, String msg) { return println_native(LOG_ID_MAIN, INFO, tag, msg); } /** * Send a {@link #WARN} 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 w(String tag, String msg) { return println_native(LOG_ID_MAIN, WARN, tag, msg); } /** * Send an {@link #ERROR} 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 e(String tag, String msg) { return println_native(LOG_ID_MAIN, ERROR, tag, msg); } /** @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; /** @hide */ public static native int println_native(int bufID, int priority, String tag, String msg);}
As you can see, the JAVA application-layer logger code calls android_util_Log.cpp at the JNI layer. The source code is as follows:
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) {response (env, println needs a message);} 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 =-1; // The function (liblog. so function) res = _ android_log_buf_write (bufID, (android_LogPriority), tag, msg); return res ;}
Logcat tool Analysis
The purpose of writing log records to Logger logs in the previous analysis is to read them through the logcat tool and then analyze them for developers.
Logcat is used in many ways, but here we analyze the four parts of Logcat from the source code perspective:
Basic data structure. Initialization process. The process of reading logs. The log output process.
The source code of logcat is located in system/core/logcat. cpp.
Basic Data Structure
The logger_entry in system/core/include/log/logger. h is defined as follows:
struct logger_entry { uint16_t len; uint16_t __pad; int32_t pid; int32_t tid; int32_t sec; int32_t nsec; char msg[0];};
The struct logger_entry is used to describe a log record. The char msg [0] pointer is used to record the message entity content.
Then, let's take a look at the queued_entry_t struct. The source code is as follows:
struct queued_entry_t {};