Log.isLoggable之二源码解析
2017-06-05 20:11
363 查看
相关文章
Log.isLoggable之一正确的使用姿势
Log.isLoggable之二源码解析
frameworks/base/core/java/android/util/Log.java
frameworks/rs/rsCompatibilityLib.h
frameworks/base/core/jni/android_util_Log.cpp
在上面的代码中,我已列出了重点部分的注解,相信大部分朋友都能看懂。它最重要的部分就是static jboolean isLoggable(const char* tag, jint level)这个函数了。这个函数里面会去获取与tag相关property的Log级别,并与代码设置的Log级别比较返回相应的true还是false。
一种方法是adb shell setprop,这种方法灵活方便,但是生命周期有限,机器设备重启即无效。
另一种方法就是将属性写入prop文件中,这种方法就可以永久生效。下面我们通过源码来分析下这种方法都有哪些prop文件可以写入。
bionic/libc/include/sys/_system_properties.h
Android系统在加载init.rc文件后会去解析各个prop文件。
system/core/init/property_service.c
通过上面的源码我们知道系统会去加载各个prop文件,但通常上面加载的prop文件不一定全部都有,但是/system/build.prop一定会有。同时,如果我们去改/data/local.prop文件不一定会生效,因为系统不一定会去加载这个文件。因为它有两个判断条件需要满足,当然这只是我从5.1的系统分析,不同的系统会有差别。以实际系统为准。
Android 5.0 如何正确启用isLoggable(二)__原理分析
深入讲解Android Property机制
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机制
相关文章推荐
- FrameLayout measure过程源码Log全解析之二:修改framework代码,输出Log
- 【Cocos2d-x】Cocos2d-x参考案例源码解析之二:TestCpp万变不离其宗Main入口点
- log::Writer-levelDB源码解析
- android Log.isLoggable步骤的使用
- android源码解析之(六)-->Log
- kafka源码解析之二kafka内部的专业术语
- OpenStack Swift源码分析(5)----swift-ring-builder源代码解析之二
- jeeCmsV7-src 源码解析之二(application-context.xml)
- android Log.isLoggable步骤的使用
- rails 源码解析之log notification/subscribe
- EventBus3 源码解析(个人理解) 之二。
- log format-levelDB源码解析
- FrameLayout measure过程源码Log全解析之四:onMeasure第一部分之ViewGroup对view的管理
- OpenStack建立实例完整过程源码详细分析(13)----依据AMQP通信架构实现消息发送机制解析之二
- android Log.isLoggable方法的使用
- android:分享 一个很强大的LOG开关---Log.isLoggable
- cocos2d-x:参考案例源码解析之二:TestCpp万变不离其宗Main入口点
- FrameLayout measure过程源码Log全解析之六:onMeasure第一部分之MeasureSpec类mode和size
- android Log.isLoggable方法的使用
- Spark-Sql源码解析之二 Sqlparser:sql –> unresolved logical plan