android logcat 命令以及 Log机制

android logcat命令

       1. logcat -c 清除已有log信息

2.logcat -b main 显示主缓冲区的log

logcat -b radio 显示无线缓冲区的log

logcat -b events 显示事件缓冲区的log

3.logcat -f [filename] 将log保存到指定的文件中,例如 logcat -b radio -f /data/radio.log

4.logcat -v 设置logcat输出的格式

主要有7种输出格式:

1. brief — Display priority/tag and PID of originating process (the default format).

2. process — Display PID only.

3. tag — Display the priority/tag only.

4. thread — Display process:thread and priority/tag only.

5. raw — Display the raw log message, with no other metadata fields.

6. time — Display the date, invocation time, priority/tag, and PID of the originating process.

7. long — Display all metadata fields and separate messages with a blank lines.

比较常用的是显示时间:logcat -v time &

5.logcat -g 查看缓冲区的大小

logcat -g main

logcat -g radio

logcat -g events



Log机制的一些理解:


1. 系统结构

应用程序调用应用程序框架层的Java接口(android.util.Log)来使用日志系统,这个Java接口通过JNI方法和系统运行库最终调用内核驱动程序Logger把Log写到内核空间中。


2. 关键代码及理解

一. 应用程序框架层日志系统Java接口的实现

代码位置:frameworks/base/core/java/android/util/Log.java文件中,实现日志系统的Java接口:

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;

    ........................
  
    public static int v(String tag, String msg) {
        return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
    }

    public static int d(String tag, String msg) {
        return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
    }

    public static int i(String tag, String msg) {
        return println_native(LOG_ID_MAIN, INFO, tag, msg);
    }

    public static int w(String tag, String msg) {
        return println_native(LOG_ID_MAIN, WARN, tag, msg);
    }

    public static int e(String tag, String msg) {
        return println_native(LOG_ID_MAIN, ERROR, tag, msg);
    }

    public static int println(int priority, String tag, String msg) {
        return println_native(LOG_ID_MAIN, priority, 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);
}
其中  VERBOSE ,DEBUG, INFO,WARN,ERROR,ASSERT代表Log优先级,分别由对应的v,d,i,w,e,a方法实现写入。

在logcat中用 

logcat  ActivityManager:w  

命令显示ActivityManager标签中等于或高于WARN级别的Log


其中

LOG_ID_MAIN = 0  LOG_ID_RADIO = 1  LOG_ID_EVENTS = 2 LOG_ID_SYSTEM = 3;

为日志缓冲区,在底层定义了3个设备文件分别为:/dev/log/main、/dev/log/events和/dev/log/radio(),第4个日志缓冲区LOG_ID_SYSTEM并没有对应的设备文件,在这种情况下,它和LOG_ID_MAIN对应同一个缓冲区ID(为什么这样下面会提到)。

在整个Log接口中,最关键的地方声明了println_native本地方法,所有的Log接口都是通过调用这个本地方法来实现Log的注入。下面我们就继续分析这个本地方法println_native。

二. 应用程序框架层日志系统JNI方法的实现。

在frameworks/base/core/jni/android_util_Log.cpp文件中,实现JNI方法println_native:

#define LOG_NAMESPACE "log.tag."
#define LOG_TAG "Log_println"
 
#include <assert.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/String8.h>
 
#include "jni.h"
#include "utils/misc.h"
#include "android_runtime/AndroidRuntime.h"
 
#define MIN(a,b) ((a<b)?a:b)
 
namespace android {
 
struct levels_t {
    jint verbose;
    jint debug;
    jint info;
    jint warn;
    jint error;
    jint assert;
};
static levels_t levels;
 
static int toLevel(const char* value) 
{
    switch (value[0]) {
        case 'V': return levels.verbose;
        case 'D': return levels.debug;
        case 'I': return levels.info;
        case 'W': return levels.warn;
        case 'E': return levels.error;
        case 'A': return levels.assert;
        case 'S': return -1; // SUPPRESS
    }
    return levels.info;
}
 
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
#ifndef HAVE_ANDROID_OS
    return false;
#else /* HAVE_ANDROID_OS */
    int len;
    char key[PROPERTY_KEY_MAX];
    char buf[PROPERTY_VALUE_MAX];
 
    if (tag == NULL) {
        return false;
    }
 
    jboolean result = false;
 
    const char* chars = env->GetStringUTFChars(tag, NULL);
 
    if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
        jclass clazz = env->FindClass("java/lang/IllegalArgumentException");
        char buf2[200];
        snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",
                chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
 
        // release the chars!
        env->ReleaseStringUTFChars(tag, chars);
 
        env->ThrowNew(clazz, buf2);
        return false;
    } else {
        strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);
        strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars);
    }
 
    env->ReleaseStringUTFChars(tag, chars);
 
    len = property_get(key, buf, "");
    int logLevel = toLevel(buf);
    return (logLevel >= 0 && level >= logLevel) ? true : false;
#endif /* HAVE_ANDROID_OS */
}
 
/*
 * In class android.util.Log:
 *  public static native int println_native(int buffer, int priority, String tag, String msg)
 */
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) {
        jclass npeClazz;
 
        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);
 
        env->ThrowNew(npeClazz, "println needs a message");
        return -1;
    }
 
    if (bufID < 0 || bufID >= LOG_ID_MAX) {
        jclass npeClazz;
 
        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);
 
        env->ThrowNew(npeClazz, "bad bufID");
        return -1;
    }
 
    if (tagObj != NULL)
        tag = env->GetStringUTFChars(tagObj, NULL);
    msg = env->GetStringUTFChars(msgObj, NULL);
 
    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
 
    if (tag != NULL)
        env->ReleaseStringUTFChars(tagObj, tag);
    env->ReleaseStringUTFChars(msgObj, msg);
 
    return res;
}
 
/*
 * JNI registration.
 */
static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "isLoggable",      "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
    { "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
};
 
int register_android_util_Log(JNIEnv* env)
{
    jclass clazz = env->FindClass("android/util/Log");
 
    if (clazz == NULL) {
        LOGE("Can't find android/util/Log");
        return -1;
    }
 
    levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));
    levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));
    levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));
    levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));
    levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));
    levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));
 
    return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
}
 
}; // 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的实现。


三. 系统运行库层日志系统的实现。

在system/core/liblog/logd_write.c中,关注写入函数__android_log_buf_write的实现

int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
{
    struct iovec vec[3];
    if (!tag)
        tag = "";
    /* XXX: This needs to go! */
    if (!strcmp(tag, "HTC_RIL") ||
        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" 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;
 
    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(bufID, vec, 3);
}
函数进来后,首先把以HTC_RIL,RIL,AT,GSM,STK,CDMA,PHONE,SMS开头的标签的Log缓存区设为LOG_ID_RADIO,表示无线缓冲区。其他的就基本是写入LOG_ID_MAIN(主分区)中了。


就先写道这里吧,这主要是我想知道Log是怎么写入radio日志分区中看到的一些东西。大部分来自:

http://disanji.net/2011/08/18/android%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E6%A1%86%E6%9E%B6%E5%B1%82%E5%92%8C%E7%B3%BB%E7%BB%9F%E8%BF%90%E8%A1%8C%E5%BA%93%E5%B1%82%E6%97%A5%E5%BF%97%E7%B3%BB%E7%BB%9F%E6%BA%90%E4%BB%A3%E7%A0%81/

非常感谢



版权声明:本文为z10232原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。