Android Logger日誌系統

來源:互聯網
上載者:User

Android Logger日誌系統
目錄

目錄 前言 執行階段程式庫層日誌庫liblog 源碼分析 CC日誌寫入介面 Java日誌寫入介面 logcat工具分析 基礎資料結構

執行階段程式庫層日誌庫——liblog

Android系統在執行階段程式庫層提供了一個用來和Logger日誌驅動程式進行互動的日誌庫liblog。通過日誌庫liblog提供的介面,應用程式就可以方便地往Logger日誌驅動程式中寫入日誌記錄。

位於執行階段程式庫層的C/C++日誌寫入介面和位於應用程式架構層的Java日誌寫入介面都是通過liblog庫提供的日誌寫入介面來往Logger日誌驅動程式中寫入日誌記錄的。

源碼分析

日誌庫liblog提供的日誌記錄寫入介面實現在logd_write.c檔案中,它的源碼位置為:/system/core/liblog/logd_write.c。

根據寫入的日誌記錄的類型不同,這些函數可以劃分為三個類別,其中:

函數__android_log_assert、__android_log_vprint和__android_log_print用來寫入類型為main的日誌記錄。 函數__android_log_btwrite和__android_log_bwrite用來寫入類型為events的日誌記錄。 函數__android_log_buf_print可以寫入任意一種類型的日誌記錄。

無論寫入的是什麼類型的日誌記錄,它們最終都是通過調用函數write_to_log寫入到Logger日誌驅動程式中的。write_to_log是一個函數指標,它開始時指向函數__write_to_log_init。因此,當函數write_to_log第一次被調用時,實際上執行的是函數__write_to_log_init。函數__write_to_log_init主要是進行一些日誌庫初始化操作,接著函數指標write_to_log重新導向到函數__write_to_log_kernel或者__write_to_log_null中,這取決於能否成功地將日誌裝置檔案開啟。

源碼分析如上,源碼實現如下:

// 先聲明,後引用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;// 一些定義在system/core/include/cutils/log.h中的宏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 LOGGER_LOG_RADIO log/radio#define LOGGER_LOG_EVENTS log/events#define LOGGER_LOG_SYSTEM log/system// 真正函數執行的地方static int __write_to_log_init(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);        // 修改write_to_log函數指標        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];        }    }    return write_to_log(log_id, vec, nr);}

通過上述代碼,我們在替換宏定義之後,是可以知道調用log_open開啟的分別是/dev/log/main、/dev/log/radio、/dev/log/events、/dev/log/system四個日誌裝置檔案。而宏log_open定義在system/core/liblog/logd_write.c中:

#if FAKE_LOG_DEVICE// 不需要care這裡,真正編譯的時候FAKE_LOG_DEVICE為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

從上面代碼可以看出,log_open的真正實現是open函數。

回到最開始的地方,如果log_open的檔案都是ok的,那接下來會調用__write_to_log_kernel函數,源碼實現如下:

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;}

函數__write_to_log_kernel會根據參數log_id在全域數組log_fds中找到對應的日誌裝置檔案描述符,然後調用宏log_writev,即函數writev,把日誌記錄寫入到Logger日誌驅動程式中。

如果裝置檔案開啟失敗的話,write_to_log函數指標會被賦值為__write_to_log_kernel,這個函數其實什麼都沒有做,只是返回了個-1。所以就不貼源碼了。

最後,我們在分析一下__android_log_buf_write函數。因為C/C++日誌寫入介面和Java日誌寫入介面最終都是調用了這個函數完成了日誌的寫入。源碼如下:

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);    }

在預設情況下,函數__android_log_write寫入的日誌記錄類型為main。然後,如果傳進來的日誌記錄的標請以”RIL”等標誌開頭,那麼它就會被認為是類型是radio的日誌記錄。

C/C++日誌寫入介面

Android系統提供了三組常用的C/C++宏來封裝日誌寫入介面。之所以這樣做,是為了方便開發同學進行日誌的開關控制,例如不在發布版本中開啟日誌。

三組宏定義分別為:

ALOGV,ALOGD,ALOGI,ALOGW和ALOGE。用來記錄類型為main的日誌。 SLOGV,SLOGD,SLOGI,SLOGW和SLOGE,用來寫入類型為system的日誌。 LOG_EVENT_INT,LOG_EVENT_LONG和LOG_EVENT_STRING,它們用來寫入類型為events的日誌記錄。

這些宏定義在system/core/include/log/log.h中,並且使用了一個LOG_NDEBUG的宏來作為日誌開關。

具體源碼如下:

// 日誌開關#ifndef LOG_NDEBUG#ifdef NDEBUG#define LOG_NDEBUG 1#else#define LOG_NDEBUG 0#endif#endif// 以ALOGE為例子#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# 回到了我們熟悉的__android_log_print函數#define android_printLog(prio, tag, fmt...)    __android_log_print(prio, tag, fmt)
Java日誌寫入介面

Android系統在應用程式架構中定義了三個Java日誌寫入介面,它們分別是android.util.Log、android.util.Slog和android.util.EventLog,寫入的日誌記錄類型分別為main、system和events。
這裡主要分析android.util.log的實現。源碼如下:

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);}

可以看到,JAVA應用程式層logger代碼是調用了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);    }    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;    // 真正日誌寫入的函數(liblog.so中的函數)    res = __android_log_buf_write(bufID, (android_LogPriority), tag, msg);    return res;}
logcat工具分析

前面分析的將日誌記錄寫入到Logger日誌中的目的就是通過logcat工具將它們讀出來,然後給開發人員進行分析。
Logcat的用法很多,但是這裡主要從源碼的角度出發,分析Logcat的四個部分:

基礎資料結構。 初始化過程。 日誌記錄的讀取過程。 日誌記錄的輸出過程。

logcat的源碼位於:system/core/logcat.cpp中。

基礎資料結構

首先是定義在system/core/include/log/logger.h中的logger_entry,定義如下:

struct logger_entry {    uint16_t len;    uint16_t __pad;    int32_t pid;    int32_t tid;    int32_t sec;    int32_t nsec;    char msg[0];};

結構體logger_entry用來描述一條日誌記錄。其中,char msg[0]指標用來記錄訊息實體內容。

然後,在看一下queued_entry_t結構體,源碼如下:

struct queued_entry_t {};

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.