Android日志打印(11)

內(nèi)核空間日志打印

日志保存

在linux內(nèi)核中使用printk來實現(xiàn)日志打印輸出且保存到/proc/kmsg,通過cat /proc/kmsg 或dmesg查看。
具體的打印級別如下:

#linux/kernel.h

#define KERN_EMERG  "<0>"    /* system is unusable          */  
#define KERN_ALERT  "<1>"    /* action must be taken immediately */  
#define KERN_CRIT  "<2>"    /* critical conditions          */  
#deinfe KERN_ERR    "<3>"    /* error conditions        */  
#deinfe KERN_WARNING    "<4>"    /* warning conditions          */  
#deinfe KERN_NOTICE "<5>"    /* normal but significant condition */  
#deinfe KERN_INFO  "<6>"    /* informational            */  
#deinfe KERN_DEBUG  "<7>"    /* debug-level messages        */ 
控制日志打印

在/proc/sys/kernel/printk保存了4個數(shù)字,分別表示當(dāng)前控制臺日志級別、未明確指定日志級別的默認(rèn)消息日志級別、最高允許設(shè)置的控制臺日志級別、引導(dǎo)時默認(rèn)的日志級別。
當(dāng)printk的日志級別高于當(dāng)前控制臺日志級別時,信息就會在控制臺上顯示。我們可以通過echo 0 > /proc/sys/kernel/printk或者類似于echo 0 1 4 7 > /proc/sys/kernel/printk來關(guān)閉日志打印

用戶空間日志打印

在Android用戶空間中,提供了一個輕量級的日志系統(tǒng),這個日志系統(tǒng)是以驅(qū)動程序的形式實現(xiàn)在內(nèi)核空間的,在用戶空間分別提供了Java接口和C/C++接口來使用這個日志系統(tǒng)。


8090047.jpg
內(nèi)核層面定義日志文件

在kernel的Logger驅(qū)動程序模塊中(android\vendor\mstar\kernel\linaro\drivers\android\logger.c),定義了log_main、log_events、log_system和log_radio日志緩沖區(qū),分別對應(yīng)設(shè)備文件/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system。

static int __init logger_init(void)
{
    ...
    ret = create_log(LOGGER_LOG_MAIN, 256*1024);
    ret = create_log(LOGGER_LOG_EVENTS, 256*1024);
    ret = create_log(LOGGER_LOG_RADIO, 256*1024);
    ret = create_log(LOGGER_LOG_SYSTEM, 256*1024);
    ...
}

Logger可以讀寫/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system等文件。

C/C++層面讀寫日志文件

1.在C/C++層定義了ALOG來實現(xiàn)日志打印,我們跟著ALOG來往下追溯源碼:

system/core/include/log/log.h

#ifndef ALOGV
#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))

#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

#define android_printLog(prio, tag, fmt...) \
    __android_log_print(prio, tag, fmt)

2.__android_log_print()在logd_write.c中定義

#system/core/liblog/logd_write.c

int __android_log_print(int prio, const char *tag, const char *fmt, ...)
{
    return __android_log_write(prio, tag, buf);
}

int __android_log_write(int prio, const char *tag, const char *msg)
{
    return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}

3.又調(diào)用logd_write.c中的__android_log_buf_write()函數(shù):

#system\core\liblog\logd_write.c
...
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
...
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
{
    ...
    /* XXX: This needs to go! */
    if ((bufID != LOG_ID_RADIO) &&
         (!strcmp(tag, "HTC_RIL") ||
        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
        !strcmp(tag, "AT") ||
        !strcmp(tag, "GSM") ||
        !strcmp(tag, "STK") ||
        !strcmp(tag, "CDMA") ||
        !strcmp(tag, "PHONE") ||
        !strcmp(tag, "SMS"))) {
            bufID = LOG_ID_RADIO;
            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
            tag = tmp_tag;
    }
    ...
    return write_to_log(bufID, vec, 3);
}

5.再看write_to_log函數(shù)所指向的__write_to_log_init函數(shù):

#\system\core\liblog\logd_write_kern.c

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_kernel函數(shù)
        write_to_log = __write_to_log_kernel;
        ...
    }
    return write_to_log(log_id, vec, nr);
}

6.__write_to_log_kernel函數(shù)調(diào)用log_writev

#\system\core\liblog\logd_write_kern.c

#define log_writev(filedes, vector, count) writev(filedes, vector, count)
static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
{
    do {
        ret = log_writev(log_fd, vec, nr);
        if (ret < 0) {
            ret = -errno;
        }
    } while (ret == -EINTR);
}

7.最后log_writev()映射到具體的驅(qū)動層的writev()函數(shù).把日志寫到內(nèi)核層面定義日志文件:/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system。

8.關(guān)于C/C++層面如何打印日志:

# 方法一
1.定義TAG,引入log.h
#define LOG_TAG "lights"
#include <cutils/log.h>

2.直接用LOGV/LOGD/LOGI/LOGW/LOGE

#方法二
1.在Android.mk文件中加入LOCAL_LDLIBS := -llog 或 LOCAL_SHARED_LIBRARIES := \ libutils \ liblog \ libcutils

2.在CPP文件中加入
#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ProjectName", __VA_ARGS__);

3.直接用LOGD("XXXXX")
Java層面

1.Java層面通過Log類來實現(xiàn)日志的打印,具體的調(diào)用如下:

#frameworks/base/core/java/android/util/Log.java

 public static int e(String tag, String msg) {
        return println_native(LOG_ID_MAIN, ERROR, tag, msg);
    }
    // 這里定義的LOG_ID_MAIN和kernel中定義的對應(yīng)
    /** @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 final int LOG_ID_CRASH = 4;   

2.println_native函數(shù)在frameworks/base/core/jni/android_util_Log.cpp定義的

#frameworks/base/core/jni/android_util_Log.cpp

static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
        jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
    return res;
}

3.回到C/C++層面的第3步,發(fā)現(xiàn)Java層面和C/C++層面走到了一起

logcat 命令行工具

Logcat工具源代碼位于system/core/logcat目錄下,只有一個源代碼文件logcat.cpp,編譯后生成的可執(zhí)行文件位于out/target/product/generic/system/bin目錄下。
Logcat是一個命令行工具,可以把上面存儲到/dev/log/main、/dev/log/events、/dev/log/radio、/dev/log/system的日志讀取出來。
-c 清除(刷新)整個日志并退出。
-d 將日志轉(zhuǎn)儲到屏幕并退出。
-f <filename> 將日志消息輸出寫入 <filename>。默認(rèn)值為 stdout。
-g 打印指定日志緩沖區(qū)的大小并退出。
-n <count> 將已旋轉(zhuǎn)日志的最大數(shù)量設(shè)置為 <count>。默認(rèn)值為 4。 需要使用 -r 選項。
-r <kbytes> 每輸出 <kbytes> 時旋轉(zhuǎn)日志文件。默認(rèn)值為 16。需要使用 -f 選項。
-s 將默認(rèn)過濾器規(guī)則設(shè)為靜默式。
-v <format> 設(shè)置日志消息的輸出格式。默認(rèn)值為 brief 格式有關(guān)支持的格式列表

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom閱讀 3,232評論 0 3
  • 1:InputChannel提供函數(shù)創(chuàng)建底層的Pipe對象 2: 1)客戶端需要新建窗口 2)new ViewRo...
    自由人是工程師閱讀 5,734評論 0 18
  • make menuconfig過程解析作者 codercjg 在 28 九月 2015, 5:27 下午 make...
    codercjg閱讀 1,250評論 0 1
  • Linux print system linux中的調(diào)試方法有很多種,但我們最常用的也是最關(guān)鍵的調(diào)試工具應(yīng)該就是使...
    Creator_Ly閱讀 2,031評論 0 4
  • HTTP狀態(tài)碼分類 HTTP狀態(tài)碼由三個十進(jìn)制數(shù)字組成,第一個十進(jìn)制數(shù)字定義了狀態(tài)碼的類型,后兩個數(shù)字沒有分類的作...
    thorhill閱讀 369評論 1 5

友情鏈接更多精彩內(nèi)容