Android provides a user-level lightweight LOG Mechanism. Its implementation runs through Java, JNI, local c/c ++ implementation, Linux kernel driver, and Other Android layers, it is simple and clear enough to explain the case. This series of articles explains the internal implementation mechanism of the LOG mechanism. This article is part 3 of this series, interpreting android. util. the JNI Implementation of Log, and how to operate the device file to write Log information in the local implementation of c/c ++.
JNI Implementation of Log-like
As mentioned above, android. util. Log has two Native methods, which must be implemented in c/c ++ through JNI.
<Pre class = "java" name = "code"> public static native boolean isLoggable (String tag, int level );
Public static native int println_native (int bufID,
Int priority, String tag, String msg );
These two methods are implemented in frameworks/base/core/jni/android_util_log.cpp. How to Implement JNI is not described here. However, these two methods are transferred to the call of the following two c/c ++ functions.
Static jboolean android_util_Log_isLoggable (JNIEnv * env, jobject clazz, jstring tag, jint level)
Static jint android_util_Log_println_native (JNIEnv * env, jobject clazz,
Jint bufID, jint priority, jstring tagObj, jstring msgObj)
Implementation of isLoggable ()
The implementation of isLoggable is a comparison between <level> (from the parameter) and the "log. tag. the value of <tag> "(<tag> from parameter) is record if it is greater than or equal. The program implementation snippets are as follows:
// LOG_NAMESPACE: "log. tag ."
// Chars: convert from param <tag>
Strncpy (key, LOG_NAMESPACE, sizeof (LOG_NAMESPACE)-1 );
Strcpy (key + sizeof (LOG_NAMESPACE)-1, chars );
Len = property_get (key, buf ,"");
Int logLevel = toLevel (buf );
Return (logLevel> = 0 & level> = logLevel )? True: false;
Println_native () Implementation
The function android_util_Log_println_native () [in the file android_util.Log.cpp] calls _ android_log_buf_write () [File system/core/liblog/logd_write.c]. _ Android_log_buf_write () organizes parameters and calls the write_to_log function pointer.
The write_to_log function pointer is the key to implementation.
See the definition of write_to_log:
Static int _ write_to_log_init (log_id_t, struct iovec * vec, size_t nr );
Static int (* write_to_log) (log_id_t, struct iovec * vec, size_t nr) = _ write_to_log_init;
Write_to_log initially points to the _ write_to_log_init () function. Therefore, when write_to_log is executed for the first time, _ write_to_log_init () is executed (). If write_to_log is not executed for the first time, it has been modified in _ write_to_log_init () to point to _ write_to_log_kernel ().
First look at the implementation of _ 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 );
}
Basically, mutual access protection is implemented. If it is the first call (write_to_log also points to _ write_to_log_init (), the corresponding device file is opened to obtain the descriptor, and point write_to_log to _ write_to_log_kernel (). Then, write the file in _ write_to_log_kernel.
Look at the implementation of _ write_to_kernel (), which is basically a write operation:
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;
}
To sum up, the println_native () operation is to open the device file (if it has not been opened) and then write data. But how to write it depends on the implementation of the Logger driver of the Log device.