Log.isLoggable之二源码解析

相关文章
Log.isLoggable之一正确的使用姿势
Log.isLoggable之二源码解析

简介

上一篇文章Log.isLoggable之一正确的使用姿势讲了Log.isLoggable使用,本文就来讲讲isLoggable的源码实现。

Log.isLoggable源码分析

首先,来看看Log.java中isLoggable的实现。通过源码我们可以知道isLoggable是一个JNI方法是通过CPP实现的。但是这里也有比较详细的注释。如果英文比较好的同学可以看下英文注解,这里就不细说了,直接看CPP源码。
frameworks/base/core/java/android/util/Log.java

    /**
     * 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;

    /**
     * Checks to see whether or not a log for the specified tag is loggable at the specified level.
     *
     *  The default level of any tag is set to INFO. This means that any level above and including
     *  INFO will be logged. Before you make any calls to a logging method you should check to see
     *  if your tag should be logged. You can change the default level by setting a system property:
     *      'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'
     *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
     *  turn off all logging for your tag. You can also create a local.prop file that with the
     *  following in it:
     *      'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
     *  and place that in /data/local.prop.
     *
     * @param tag The tag to check.
     * @param level The level to check.
     * @return Whether or not that this is allowed to be logged.
     * @throws IllegalArgumentException is thrown if the tag.length() > 23.
     */
    public static native boolean isLoggable(String tag, int level);

frameworks/rs/rsCompatibilityLib.h

#define PROPERTY_KEY_MAX 32

frameworks/base/core/jni/android_util_Log.cpp

#define LOG_NAMESPACE "log.tag."

/*
 * 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 },
};

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)
{
    //根据首字符的值设置相应的log级别,注意首字符要大写
    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
    }

    //如果都没有配备到上面的字符,就返回一个默认的info级别
    return levels.info;
}

static jboolean isLoggable(const char* tag, jint level) {
    String8 key;
    //将log.tag.<tag>连接起来成一个字符串
    key.append(LOG_NAMESPACE);
    key.append(tag);

    char buf[PROPERTY_VALUE_MAX];
    //获取这个字符串属性的值,如果没有获取到值就给其赋空值
    if (property_get(key.string(), buf, "") <= 0) {
        buf[0] = '\0';
    }

    int logLevel = toLevel(buf);
    //这个里可以看出,代码设置的级别不小于通过属性获取的级别就会返回true了。
    return logLevel >= 0 && level >= logLevel;
}

static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
    if (tag == NULL) {
        return false;
    }

    const char* chars = env->GetStringUTFChars(tag, NULL);
    if (!chars) {
        return false;
    }

    jboolean result = false;
    //判断log.tag.<tag>的长度是否大于32,如果大于32就报异常,所以我们给tag设置字符串的时候不要过长
    if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
        char buf2[200];
        snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %zu characters\n",
                chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));

        jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
    } else {
        //真正处理逻辑的地方
        result = isLoggable(chars, level);
    }

    env->ReleaseStringUTFChars(tag, chars);
    return result;
}

int register_android_util_Log(JNIEnv* env)
{
    jclass clazz = env->FindClass("android/util/Log");

    if (clazz == NULL) {
        ALOGE("Can't find android/util/Log");
        return -1;
    }

    //通过反射获取Log.java上对应的值
    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));
}

在上面的代码中,我已列出了重点部分的注解,相信大部分朋友都能看懂。它最重要的部分就是static jboolean isLoggable(const char* tag, jint level)这个函数了。这个函数里面会去获取与tag相关property的Log级别,并与代码设置的Log级别比较返回相应的true还是false。

property的加载源码分析

下面重点分析下property_get方法获取对应的属性值时,我们可以设置property值的方法。
一种方法是adb shell setprop,这种方法灵活方便,但是生命周期有限,机器设备重启即无效。
另一种方法就是将属性写入prop文件中,这种方法就可以永久生效。下面我们通过源码来分析下这种方法都有哪些prop文件可以写入。

bionic/libc/include/sys/_system_properties.h

#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
#define PROP_PATH_VENDOR_BUILD     "/vendor/build.prop"
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"
#define PROP_PATH_FACTORY          "/factory/factory.prop"

Android系统在加载init.rc文件后会去解析各个prop文件。
system/core/init/property_service.c

void load_all_props(void)
{
    //加载/system/build.prop属性文件
    load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
    //加载/system/default.prop属性文件
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL);
    //加载/vendor/build.prop属性文件
    load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
    //加载/factory/factory.prop属性文件
    load_properties_from_file(PROP_PATH_FACTORY, "ro.*");

    //加载/data/local.prop属性文件
    load_override_properties();

    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();
}

static void load_override_properties() {
#ifdef ALLOW_LOCAL_PROP_OVERRIDE
    char debuggable[PROP_VALUE_MAX];
    int ret;

    ret = property_get("ro.debuggable", debuggable);
    //加载/data/local.prop属性文件有两个判断条件。1.定义了ALLOW_LOCAL_PROP_OVERRIDE,2.ro.debuggable=1
    //只有满足这两个条件才会去加载/data/local.prop属性文件
    if (ret && (strcmp(debuggable, "1") == 0)) {
        load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
    }
#endif /* ALLOW_LOCAL_PROP_OVERRIDE */
}

通过上面的源码我们知道系统会去加载各个prop文件,但通常上面加载的prop文件不一定全部都有,但是/system/build.prop一定会有。同时,如果我们去改/data/local.prop文件不一定会生效,因为系统不一定会去加载这个文件。因为它有两个判断条件需要满足,当然这只是我从5.1的系统分析,不同的系统会有差别。以实际系统为准。

参考文章

Android 5.0 如何正确启用isLoggable(一)__使用详解
Android 5.0 如何正确启用isLoggable(二)__原理分析
深入讲解Android Property机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值