当前位置:   article > 正文

Android中LOG机制详解_android dslog

android dslog
一、简介
Android 中LOG的实现架构如下图所示,这基本上也是Android的某个模块实现各个层次的经典架构。
图1
Android中LOG机制详解(上) - hubingforever - 民主与科学
 

 Android应用程序通过Framework提供的Log类来进行日志的输出;而Log类其实是通过JNI函数来进行日志输出;JNI函数则是调用底层库函数进行日志的输出;然后库函数通过操作映射的设备文件操作设备。
在Linux中,系统其实把LOG作为一种设备处理,并把它映射为文件。因此库函数操作映射的LOG文件, 其实是通过kernel中的 LOG的Driver 完成相应的操作
关于Android中LOG实现架构更详细的流程图请参考《 Android LOG机制流程图
二、类android.util.Log
Android的Java程序通过android.util.Log类来输出Log,下图2.1列出了我们常用的Log的静态方法。
图2.1
Android中LOG机制详解(上) - hubingforever - 民主与科学
 
   一般,要输出Log信息,可直接调用 Log.v()/Log.d()/Log.i()/Log.w()/Log.e() 等类方法。这里之所以有这么多有区分的方法,这也是Log的分类。Log的分类就如同Log的静态常量成员定义的那样,而Log的优先级按照数字大小排列,数字大的优先级高。 Log.wtf() 一般用于输出的非常致命的FAULT信息(What  a Terrible Failure),报这个错误,不光是在Log里记录,还要在界面上有提示,并可能杀死当前的进程。
它在输出日志的同时,它会把此处代码此时的执行路径(调用栈)打印出来。在调试时,我们可以使用它来打印当前代码的执行栈。
isLoggable() 用于判断是否需要输出日志, 如果日志的tag太长或如果要输出的LOG优先级低于当前设置的优先级,则返回false,表示不用输出该日志 。在Java程序中,用Log的方法打印Log之前,可以先用 isLoggable() 判断一下是否需要,才进行输出日志。
另外, Log.println() 能达到与 Log.v()/Log.d()/… 等方法同样的输出效果,只是在用它时,要指定对应的优先级。
2.1、类android.util.Log的java实现
      类 android.util.Log 的java实现是比较简单的。类 android.util.Log 的构造函数是私有的,并不会被实例化,只是提供了静态的属性和方法。
android.util.Log 的各种Log记录方法的实现都依赖于native的实现 println_native() Log.v()/Log.d()/Log.i()/Log.w()/Log.e() 最终都是调用了 println_native() 。如Log.d()的实现:
    public static int d(String tag, String msg) {
         return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
    }
2.2、类android.util.Log的JNI实现
类android.util.Log有两个Native方法,它们通过JNI用c/c++中实现。
public static native boolean  isLoggable(String tag,  int  level);
public static native int  println_native( int  bufID,int priority, String tag, String msg);
这两个方法是在 frameworks/base/core/jni/android_util_log.cpp 中实现的。这两个方法分别对应下列两个c/c++函数。
static jboolean  android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag,  jint  level)
static jint  android_util_Log_println_native(JNIEnv* env, jobject clazz,jint bufID, jint priority, jstring tagObj, jstring msgObj)
isLoggable() 的实现是比较<level>(来自参数)与当前property里设定的“log.tag.<tag>”(<tag>来自参数)的level值,大于或等于都是可记录的。程序实现片断如下
    // LOG_NAMESPACE : “log.tag.”
    // chars: convert from param<tag>
    strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);
    strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars);
    //略
     len = property_get(key, buf, "");
    i nt logLevel = toLevel(buf);
     //略
     return (logLevel >= 0 && level >= logLevel) ? true : false;
println_native() 的实现要负责些。关于此最好对比着 Android LOG机制流程图 来看。
android_util.Log.cpp 中, 函数 android_util_Log_println_native()  调用了 Android_log_buf_write() 函数,而 Android_log_buf_write() 又直接调用了 system/core/liblog/logd_write.c 中的 __android_log_buf_write() 。在 文件 system/core/liblog/logd_write.c 中, __android_log_buf_write() 组织了参数,又调用了 write_to_log 这个函数指针。
write_to_log 这个函数指针是实现的关键。
write_to_log 的定义
static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
write_to_log 初始是指向 __write_to_log_init() 这个函数的。所以第一次执行write_to_log的时候是执行了 __write_to_log_init() 。而如果 write_to_log 不是第一次被执行,它已经在 __write_to_log_init() 里被修改指向了 __write_to_log_kernel()
先看 __write_to_log_init() 的实现:
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
{
#ifdef HAVE_PTHREADS
    pthread_mutex_lock(&log_init_lock);
#endif
 
     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 = __write_to_log_kernel;
 
         if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
                log_fds[LOG_ID_EVENTS] < 0) {
            log_close(log_fds[LOG_ID_MAIN]);
            log_close(log_fds[LOG_ID_RADIO]);
            log_close(log_fds[LOG_ID_EVENTS]);
            log_fds[LOG_ID_MAIN] = -1;
            log_fds[LOG_ID_RADIO] = -1;
            log_fds[LOG_ID_EVENTS] = -1;
            write_to_log = __write_to_log_null;
        }
 
         if (log_fds[LOG_ID_SYSTEM] < 0) {
            log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
        }
    }
 
#ifdef HAVE_PTHREADS
    pthread_mutex_unlock(&log_init_lock);
#endif
 
      return  write_to_log(log_id, vec, nr);
}
如果是第一次调用( write_to_log 还指向 __write_to_log_init() ),就打开相应的设备文件,获取描述符,并把 write_to_log 指向 __write_to_log_kernel() 。再在 __write_to_log_kernel() 中具体执行写入文件操作。
再看 __write_to_kernel() 的实现,基本就是写操作
static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
{
    ssize_t ret;
     int log_fd;
 
     if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
        log_fd = log_fds[(int)log_id];
    }  else {
         return EBADF;
    }
 
     do {
         ret = log_writev(log_fd, vec, nr);
    }  while (ret < 0 && errno == EINTR);
 
     return ret;
}

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/684730
推荐阅读
相关标签
  

闽ICP备14008679号