aosp16设置单个应用的日志输出级别

现象回顾

一次修改bug过程中发现,com.android.bluetooth这个app一开始能正常输出debug、verbose级别的日志,但运行一会儿之后就再也看不到debug、verbose级别的日志了,只能看见info级别的日志。然后我就写了一个demo app分别用Log.d和Log.v打印日志,发现logcat能正常输出demo的日志。

于是我就猜测,是不是有什么接口可以设置app的logcat日志输出级别。

原理分析

于是乎,循着打印log的源码分析:

一般打印日志调用Log.d

bash 复制代码
Log.d(TAG, "onCreate: ");

Log.d实现在frameworks/base/core/java/android/util/Log.java

bash 复制代码
	public static final int DEBUG = 3;
	  
    public static int d(@Nullable String tag, @NonNull String msg) {
        return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
    }

    public static native int println_native(int bufID, int priority, String tag, String msg);

Log.d接着调用println_native,println_native是一个jni方法,实现在frameworks/base/core/jni/android_util_Log.cpp

bash 复制代码
/*
 * 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) {
        jniThrowNullPointerException(env, "println needs a message");
        return -1;
    }

    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 = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);

    if (tag != NULL)
        env->ReleaseStringUTFChars(tagObj, tag);
    env->ReleaseStringUTFChars(msgObj, msg);

    return res;
}

android_util_Log_println_native调用__android_log_buf_write,__android_log_buf_write实现在system/logging/liblog/logger_write.cpp

bash 复制代码
int __android_log_buf_write(int log_id, int prio, const char* tag, const char* msg) {
  ErrnoRestorer errno_restorer;

  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
    return -EPERM;
  }

  __android_log_message log_message = {
      sizeof(__android_log_message), log_id, prio, tag, nullptr, 0, msg};
  __android_log_write_log_message(&log_message);
  return 1;
}

__android_log_is_loggable判断能否打印,实现在system/logging/liblog/properties.cpp

bash 复制代码
int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
  auto len = tag ? strlen(tag) : 0;
  return __android_log_is_loggable_len(prio, tag, len, default_prio);
}

int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
  return __android_log_is_loggable(prio, nullptr, def);
}

int __android_log_is_loggable(int prio, const char*, int) {
  int minimum_priority = __android_log_get_minimum_priority();
  if (minimum_priority == ANDROID_LOG_DEFAULT) {
    minimum_priority = ANDROID_LOG_INFO;
  }
  return prio >= minimum_priority;
}

经过层层调用,发现能否打印取决于是否prio >= minimum_priority,prio是调用Log.d时传过来的DEBUG=3,minimum_priority取自__android_log_get_minimum_priority方法,实现在system/logging/liblog/logger_write.cpp

bash 复制代码
static std::atomic_int32_t minimum_log_priority = ANDROID_LOG_DEFAULT;

int32_t __android_log_set_minimum_priority(int32_t priority) {
  return minimum_log_priority.exchange(priority, std::memory_order_relaxed);
}

int32_t __android_log_get_minimum_priority() {
  return minimum_log_priority;
}

ANDROID_LOG_DEFAULT 定义在system/logging/liblog/include/android/log.h

bash 复制代码
/**
 * Android log priority values, in increasing order of priority.
 */
typedef enum android_LogPriority {
  /** For internal use only.  */
  ANDROID_LOG_UNKNOWN = 0,
  /** The default priority, for internal use only.  */
  ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
  /** Verbose logging. Should typically be disabled for a release apk. */
  ANDROID_LOG_VERBOSE,
  /** Debug logging. Should typically be disabled for a release apk. */
  ANDROID_LOG_DEBUG,
  /** Informational logging. Should typically be disabled for a release apk. */
  ANDROID_LOG_INFO,
  /** Warning logging. For use with recoverable failures. */
  ANDROID_LOG_WARN,
  /** Error logging. For use with unrecoverable failures. */
  ANDROID_LOG_ERROR,
  /** Fatal logging. For use when aborting. */
  ANDROID_LOG_FATAL,
  /** For internal use only.  */
  ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
} android_LogPriority;

c语言的枚举值从第一个开始依次递增,所以ANDROID_LOG_DEFAULT=1,minimum_log_priority默认是ANDROID_LOG_DEFAULT,Log.d传过来的DEBUG=3>ANDROID_LOG_DEFAULT=1,所以默认Log.d是能打印的。

但有个接口__android_log_set_minimum_priority可以改变minimum_log_priority的值,看到这,是不是发现了什么。

猜想,这个就是设置单个应用的默认日志输出级别接口。

顺着猜想看看com.android.bluetooth是不是哪里调用了这个接口。

bluetooth里会启动AdapterService,实现在packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java,AdapterService的init方法会加载bluetooth_jni库

bash 复制代码
public class AdapterService extends Service {
    @SuppressLint("AndroidFrameworkRequiresPermission")
    private void init() {
        Log.d(TAG, "init()");
        ...
        if (Utils.isInstrumentationTestMode()) {
            Log.w(TAG, "This Bluetooth App is instrumented. ** Skip loading the native **");
        } else {
            Log.d(TAG, "Loading JNI Library");
            System.loadLibrary("bluetooth_jni");
        }

bluetooth_jni库的接口实现packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp

bash 复制代码
/*
 * JNI Initialization
 */
jint JNI_OnLoad(JavaVM* jvm, void* /* reserved */) {
  /* Set the default logging level for the process using the tag
   *  "log.tag.bluetooth" and/or "persist.log.tag.bluetooth" via the android
   * logging framework.
   */
  const char* stack_default_log_tag = "bluetooth";
  int default_prio = ANDROID_LOG_INFO;
  if (__android_log_is_loggable(ANDROID_LOG_VERBOSE, stack_default_log_tag, default_prio)) {
    __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
    log::info("Set stack default log level to 'VERBOSE'");
  } else if (__android_log_is_loggable(ANDROID_LOG_DEBUG, stack_default_log_tag, default_prio)) {
    __android_log_set_minimum_priority(ANDROID_LOG_DEBUG);
    log::info("Set stack default log level to 'DEBUG'");
  } else if (__android_log_is_loggable(ANDROID_LOG_INFO, stack_default_log_tag, default_prio)) {
    __android_log_set_minimum_priority(ANDROID_LOG_INFO);
    log::info("Set stack default log level to 'INFO'");
  } else if (__android_log_is_loggable(ANDROID_LOG_WARN, stack_default_log_tag, default_prio)) {
    __android_log_set_minimum_priority(ANDROID_LOG_WARN);
    log::info("Set stack default log level to 'WARN'");
  } else if (__android_log_is_loggable(ANDROID_LOG_ERROR, stack_default_log_tag, default_prio)) {
    __android_log_set_minimum_priority(ANDROID_LOG_ERROR);
    log::info("Set stack default log level to 'ERROR'");
  }

JNI_OnLoad里调用了上面提到的__android_log_set_minimum_priority方法,当加载so库的时候,会先调用JNI_OnLoad方法。观察bluetooth启动日志也发现了相关信息。

也就是说,com.android.bluetooth正是通过调用__android_log_set_minimum_priority改变了默认的日志输出级别,所以才出现了一开始能看到Log.d的日志后面却只能看到Log.i及以上的日志的现象。

设置单个应用日志输出级别

假设我们需要设置自己app的默认日志输出级别,参考com.android.bluetooth的做法:

  1. app启动时加载一个自定义so库
  2. 重写jni方法的JNI_OnLoad方法
  3. JNI_OnLoad里调用__android_log_set_minimum_priority改变默认日志级别

补充

  1. 设置系统默认日志输出级别:adb shell setprop log.tag v,重启后无效
  2. 设置系统默认日志输出级别:adb shell setprop persist.log.tag d,重启后也有效
  3. 设置单个tag(如hai)日志输出级别:adb shell setprop log.tag.hai v,重启后无效
  4. 设置单个tag(如hai)日志输出级别:adb shell setprop persist.log.tag.hai d,重启后也有效
相关推荐
此去正年少2 个月前
编写adb脚本工具对Android设备上的闪退问题进行监控分析
android·adb·logcat·ndk·日志监控
ClassOps5 个月前
Android Studio Logcat中 杀死应用
android·android studio·logcat
时丶光10 个月前
Android 查看 Logcat (可纯手机方式 无需电脑)
android·logcat
Flamesky2 年前
LogCat连接安卓手机拉取日志到本地(Unity开发版)
android·unity·logcat·crash·log
_Mr.Tree2 年前
Android-实现记录“异常闪退“日志
android·java·日志·logcat
深度安全实验室2 年前
adb shell logcat
adb·shell·logcat
Modu_MrLiu2 年前
AndroidStudio - 新版本 Logcat 使用详解
android·logcat·androidstudio·新版本logcat
勺城之客2 年前
logcat命令使用
logcat
TedSmile3 年前
Android Studio新版本logcat过滤说明
android studio·logcat·过滤