转自: http://my.oschina.net/wolfcs/blog/164624

Android log系统。

在android Java code中输出log

android系统有4种类型、6个优先级的log,有一些常量用于标识这些信息,相关的定义在frameworks/base/core/Java/android/util/Log.java中可以看到:

01 /**
02  * Priority constant for the println method; use Log.v.
03  */
04 public static final int VERBOSE = 2;
05  
06 /**
07  * Priority constant for the println method; use Log.d.
08  */
09 public static final int DEBUG = 3;
10  
11 /**
12  * Priority constant for the println method; use Log.i.
13  */
14 public static final int INFO = 4;
15  
16 /**
17  * Priority constant for the println method; use Log.w.
18  */
19 public static final int WARN = 5;
20  
21 /**
22  * Priority constant for the println method; use Log.e.
23  */
24 public static final int ERROR = 6;
25  
26 /**
27  * Priority constant for the println method.
28  */
29 public static final int ASSERT = 7;
30  
31 /** @hide */ public static final int LOG_ID_MAIN = 0;
32 /** @hide */ public static final int LOG_ID_RADIO = 1;
33 /** @hide */ public static final int LOG_ID_EVENTS = 2;
34 /** @hide */ public static final int LOG_ID_SYSTEM = 3;

Java层可以通过三个class来输出其中三种类型的log,三种类型分别为MAIN、RADIO和SYSTEM,三个class分别为Log、Rlog和Slog,其package则分别为android.util、android.telephony和 android.util。这些用于打印log的classes,其构造函数都为private,因而都不能创建其对象,但它们都提供了静态方法来给用户打印log。各个log打印class的实现都大同小异,可以看一下Log这个class中的一些:

01 public static int v(String tag, String msg, Throwable tr) {
02     return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
03 }
04  
05 /**
06  * Send a {@link #DEBUG} log message.
07  * @param tag Used to identify the source of a log message.  It usually identifies
08  *        the class or activity where the log call occurs.
09  * @param msg The message you would like logged.
10  */
11 public static int d(String tag, String msg) {
12     return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
13 }

最终都会是调用Log.println_native()静态native方法来打印log,各个类中各个方法的不同之处也仅在于参数的差异。

Log.println_native()方法

这个方法的code在/frameworks/base/core/jni/android_util_Log.cpp,为:

01 static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
02         jint bufID, jint priority, jstring tagObj, jstring msgObj)
03 {
04     const char* tag = NULL;
05     const char* msg = NULL;
06  
07     if (msgObj == NULL) {
08         jniThrowNullPointerException(env, "println needs a message");
09         return -1;
10     }
11  
12     if (bufID < 0 || bufID >= LOG_ID_MAX) {
13         jniThrowNullPointerException(env, "bad bufID");
14         return -1;
15     }
16  
17     if (tagObj != NULL)
18         tag = env->GetStringUTFChars(tagObj, NULL);
19     msg = env->GetStringUTFChars(msgObj, NULL);
20  
21     int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
22  
23     if (tag != NULL)
24         env->ReleaseStringUTFChars(tagObj, tag);
25     env->ReleaseStringUTFChars(msgObj, msg);
26  
27     return res;
28 }
29  
30 /*
31  * JNI registration.
32  */
33 static JNINativeMethod gMethods[] = {
34     /* name, signature, funcPtr */
35     "isLoggable",      "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
36     "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
37 };

可以看到,干的都是转换参数的事情,最终再call到__android_log_buf_write()函数,这个函数的定义在system/core/liblog/logd_write.c,为:

01 int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
02 {
03     struct iovec vec[3];
04     char tmp_tag[32];
05  
06     if (!tag)
07         tag = "";
08  
09     /* XXX: This needs to go! */
10     if ((bufID != LOG_ID_RADIO) &&
11          (!strcmp(tag, "HTC_RIL") ||
12         !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
13         !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
14         !strcmp(tag, "AT") ||
15         !strcmp(tag, "GSM") ||
16         !strcmp(tag, "STK") ||
17         !strcmp(tag, "CDMA") ||
18         !strcmp(tag, "PHONE") ||
19         !strcmp(tag, "SMS"))) {
20             bufID = LOG_ID_RADIO;
21             // Inform third party apps/ril/radio.. to use Rlog or RLOG
22             snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
23             tag = tmp_tag;
24     }
25  
26     vec[0].iov_base   = (unsigned char *) &prio;
27     vec[0].iov_len    = 1;
28     vec[1].iov_base   = (void *) tag;
29     vec[1].iov_len    = strlen(tag) + 1;
30     vec[2].iov_base   = (void *) msg;
31     vec[2].iov_len    = strlen(msg) + 1;
32  
33     return write_to_log(bufID, vec, 3);
34 }

做了三件事情,一是根据log的tag,转换bufID,二是用传进来的参数构造一个struct iovec数组,三是将前一步构造的数组作为参数调用write_to_log()。write_to_log()是一个函数指针,在开始时,它指向了__write_to_log_init():

1 static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;

__write_to_log_init()的实现如下:

01 static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
02 {
03 #ifdef HAVE_PTHREADS
04     pthread_mutex_lock(&log_init_lock);
05 #endif
06  
07     if (write_to_log == __write_to_log_init) {
08         log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
09         log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
10         log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
11         log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
12  
13         write_to_log = __write_to_log_kernel;
14  
15         if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
16                 log_fds[LOG_ID_EVENTS] < 0) {
17             log_close(log_fds[LOG_ID_MAIN]);
18             log_close(log_fds[LOG_ID_RADIO]);
19             log_close(log_fds[LOG_ID_EVENTS]);
20             log_fds[LOG_ID_MAIN] = -1;
21             log_fds[LOG_ID_RADIO] = -1;
22             log_fds[LOG_ID_EVENTS] = -1;
23             write_to_log = __write_to_log_null;
24         }
25  
26         if (log_fds[LOG_ID_SYSTEM] < 0) {
27             log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
28         }
29     }
30  
31 #ifdef HAVE_PTHREADS
32     pthread_mutex_unlock(&log_init_lock);
33 #endif
34  
35     return write_to_log(log_id, vec, nr);
36 }

这个地方,会检查write_to_log是否指向了__write_to_log_init,也就是是否是第一次打印log,如果是,则打开几个用于输出log的设备文件,然后使write_to_log函数指针指向__write_to_log_kernel,或者在打开输出log设备文件出现异常时,使write_to_log指向__write_to_log_null,最后再次调用经过了重定向的write_to_log,也就是__write_to_log_kernel或者__write_to_log_null函数。我们可以看一下那几个设备文件究竟是什麽(在system/core/include/cutils/logger.h):

1 #define LOGGER_LOG_MAIN     "log/main"
2 #define LOGGER_LOG_RADIO    "log/radio"
3 #define LOGGER_LOG_EVENTS   "log/events"
4 #define LOGGER_LOG_SYSTEM   "log/system"

接着继续来看__write_to_log_kernel或者__write_to_log_null函数:

01 static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
02 {
03     return -1;
04 }
05  
06 static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
07 {
08     ssize_t ret;
09     int log_fd;
10  
11     if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
12         log_fd = log_fds[(int)log_id];
13     else {
14         return EBADF;
15     }
16  
17     do {
18         ret = log_writev(log_fd, vec, nr);
19     while (ret < 0 && errno == EINTR);
20  
21     return ret;
22 }

由log_id获取到对应的log_fd,然后调用log_writev()打印log。可以看一下log_writev()的定义,它是一个宏:

01 #if FAKE_LOG_DEVICE
02 // This will be defined when building for the host.
03 #define log_open(pathname, flags) fakeLogOpen(pathname, flags)
04 #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
05 #define log_close(filedes) fakeLogClose(filedes)
06 #else
07 #define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
08 #define log_writev(filedes, vector, count) writev(filedes, vector, count)
09 #define log_close(filedes) close(filedes)
10 #endif

这些就都是标准的unix系统调用了。

本地层代码Log输出

以一些比较典型的native代码打印log的case为例。先来看一下,在JNI的code中打印log的方法。在JNI中,比较常见到用ALOGx这一组宏来打印log,比如在frameworks/base/core/jni/android/graphics/TextLayoutCache.cpp这个文件中的dumpCacheStats()函数:

01 void TextLayoutCache::dumpCacheStats() {
02     float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
03     float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
04  
05     size_t cacheSize = mCache.size();
06  
07     ALOGD("------------------------------------------------");
08     ALOGD("Cache stats");
09     ALOGD("------------------------------------------------");
10     ALOGD("pid       : %d", getpid());
11     ALOGD("running   : %.0f seconds", timeRunningInSec);
12     ALOGD("entries   : %d", cacheSize);
13     ALOGD("max size  : %d bytes", mMaxSize);
14     ALOGD("used      : %d bytes according to mSize", mSize);
15     ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);
16     ALOGD("hits      : %d", mCacheHitCount);
17     ALOGD("saved     : %0.6f ms", mNanosecondsSaved * 0.000001f);
18     ALOGD("------------------------------------------------");
19 }

使用这组宏,需要定义另外一个宏来作为所打印log的tag:

1 #define LOG_TAG "TextLayoutCache"

此外,还要include头文件<cutils/log.h>。来看一下这些宏中的一些的定义:

01 /*
02  * Simplified macro to send a debug log message using the current LOG_TAG.
03  */
04 #ifndef ALOGD
05 #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
06 #endif
07  
08 /*
09  * Simplified macro to send a warning log message using the current LOG_TAG.
10  */
11 #ifndef ALOGW
12 #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
13 #endif
14  
15 /*
16  * Basic log message macro.
17  *
18  * Example:
19  *  ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
20  *
21  * The second argument may be NULL or "" to indicate the "global" tag.
22  */
23 #ifndef ALOG
24 #define ALOG(priority, tag, ...) \
25     LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
26 #endif
27  
28 /*
29  * Log macro that allows you to specify a number for the priority.
30  */
31 #ifndef LOG_PRI
32 #define LOG_PRI(priority, tag, ...) \
33     android_printLog(priority, tag, __VA_ARGS__)
34 #endif
35  
36 #define android_printLog(prio, tag, fmt...) \
37     __android_log_print(prio, tag, fmt)

先来看一下,在native层中定义的priority(在system/core/include/android/log.h中):

01 /*
02  * Android log priority values, in ascending priority order.
03  */
04 typedef enum android_LogPriority {
05     ANDROID_LOG_UNKNOWN = 0,
06     ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
07     ANDROID_LOG_VERBOSE,
08     ANDROID_LOG_DEBUG,
09     ANDROID_LOG_INFO,
10     ANDROID_LOG_WARN,
11     ANDROID_LOG_ERROR,
12     ANDROID_LOG_FATAL,
13     ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
14 } android_LogPriority;

另外,这些宏最终都会call到__android_log_print(),也是在system/core/liblog/logd_write.c中:

01 int __android_log_print(int prio, const char *tag, const char *fmt, ...)
02 {
03     va_list ap;
04     char buf[LOG_BUF_SIZE];
05  
06     va_start(ap, fmt);
07     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
08     va_end(ap);
09  
10     return __android_log_write(prio, tag, buf);
11 }

先是格式化参数,然后就是调用__android_log_write()函数。这个函数的code如下:

01 int __android_log_write(int prio, const char *tag, const char *msg)
02 {
03     struct iovec vec[3];
04     log_id_t log_id = LOG_ID_MAIN;
05     char tmp_tag[32];
06  
07     if (!tag)
08         tag = "";
09  
10     /* XXX: This needs to go! */
11     if (!strcmp(tag, "HTC_RIL") ||
12         !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
13         !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
14         !strcmp(tag, "AT") ||
15         !strcmp(tag, "GSM") ||
16         !strcmp(tag, "STK") ||
17         !strcmp(tag, "CDMA") ||
18         !strcmp(tag, "PHONE") ||
19         !strcmp(tag, "SMS")) {
20             log_id = LOG_ID_RADIO;
21             // Inform third party apps/ril/radio.. to use Rlog or RLOG
22             snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
23             tag = tmp_tag;
24     }
25  
26     vec[0].iov_base   = (unsigned char *) &prio;
27     vec[0].iov_len    = 1;
28     vec[1].iov_base   = (void *) tag;
29     vec[1].iov_len    = strlen(tag) + 1;
30     vec[2].iov_base   = (void *) msg;
31     vec[2].iov_len    = strlen(msg) + 1;
32  
33     return write_to_log(log_id, vec, 3);
34 }

这个函数与我们前面看到的__android_log_buf_write()非常相似。所不同的就是这个函数没有log_id参数,因而它默认是输出MAIN log,当log的TAG为某些特殊字串时,则输出RADIO log。最后同样是调用write_to_log这个函数指针来输出log。

我们再来看一个skia里面打log的SkDebugf()函数的实现:

1 #include <android/log.h>
2  
3 void SkDebugf(const char format[], ...) {
4     va_list args;
5     va_start(args, format);
6     __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, format, args);
7     va_end(args);
8 }

call到了__android_log_vprint()来输出log,__android_log_vprint()的定义也在system/core/liblog/logd_write.c中:

1 int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
2 {
3     char buf[LOG_BUF_SIZE];
4  
5     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
6  
7     return __android_log_write(prio, tag, buf);
8 }

一样是__android_log_write()函数。

Done.

android log机制——输出log【转】的更多相关文章

  1. 我的手机华为荣耀7,运行android程序不输出Log

    解决方法:1 进入手机拨号界面2 输入*#*#2846579#*#*3 输入完毕后自动跳转到<工程菜单>界面4 依次选择后台设置-->LOG设置-->在此可以看见一些列关于LO ...

  2. 解读Android LOG机制的实现【转】

    转自:http://www.cnblogs.com/hoys/archive/2011/09/30/2196199.html http://armboard.taobao.com/ Android提供 ...

  3. 实现Android Studio JNI开发C/C++使用__android_log_print输出Log

    相信很多人在刚开始学习Android JNI编程的时候,需要输出Log,在百度Google搜索的时候都是说需要在Android.mk中加入LOCAL_LDLIBS+= -L$(SYSROOT)/usr ...

  4. [转]Android输出Log到文件

    前言:开发中遇到mx4这款机型Eclipse联调不上,logcat看不了,需要输出生成文件查看调试信息.网上搜了下,功能很完善了.startService和过滤输出信息需要自己添加设置,另外注意添加权 ...

  5. adb logcat命令查看并过滤android输出log

    cmd命令行中使用adb logcat命令查看android系统和应用的log,dos窗口按ctrl+c中断输出log记录. logcat日志中的优先级/tag标记: android输出的每一条日志都 ...

  6. Android学习笔记——log无法输出的解决方法和命令行查看log日志

    本人邮箱:JohnTsai.Work@gmail.com,欢迎交流讨论. 欢迎转载,转载请注明网址:http://www.cnblogs.com/JohnTsai/p/3983936.html. 知识 ...

  7. 【android】 adb logcat命令查看并过滤android输出log

    cmd命令行中使用adb logcat命令查看android系统和应用的log,dos窗口按ctrl+c中断输出log记录. logcat日志中的优先级/tag标记: android输出的每一条日志都 ...

  8. Android中Log机制详解

    Android中Log的输出有如下几种: Log.v(String tag, String msg);        //VERBOSELog.d(String tag, String msg);   ...

  9. Android关于log日志,华为不输出log.v,log.d(zz)

    [java] view plain copy 我用的是mate8,本来虚拟机测试一点日志一点问题没有 [java] view plain copy 但是真机测试发现log.d一直不输出,正好又试了lo ...

随机推荐

  1. SQLdiag-初识

    SQLdiag是一个命令行实用工具,默认情况下,在C:\Program Files\Microsoft SQL Server\100\Tools\Binn目录下可用.首先我们打开SQLdiag.exe ...

  2. [转载]ArcGIS Engine 中的多线程使用

    ArcGIS Engine 中的多线程使用 原文链接 http://anshien.blog.163.com/blog/static/169966308201082441114173/   一直都想写 ...

  3. Linux 安装php

    安装libxml2 下载解压 libxml2-2.6.32.tar.gz 安装 ./configure --prefix=/usr/local/libxml2makesudo make install ...

  4. iOS 归档

    #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> @pr ...

  5. 获取git的最后一次提交的commit id

    git log --pretty=format:"%h" | head -1  | awk '{print $1}' 可以放到xcode  build setting  post ...

  6. tomcat 6.0.44 “has failed to stop it. This is very likely to create a memory leak” 问题调查

    1. 问题起因 我们项目中缓存模块某个实现采用了ehcache(2.4.3),当项目部署到tomcat中后,对tomcat做停止服务操作(点击eclipse的console红色的停止按钮,奇怪的是有小 ...

  7. Radius session

    1,EAP 中继 client start, NAS require identity, client sent username, NAS sent username to sever, serve ...

  8. Java基础之写文件——使用Formatter对象加载缓冲区(UsingAFormatter)

    控制台程序,使用Formatter对象将写入文件的数据准备好. 使用Formatter对象的format()方法,将数据值格式化到视图缓冲区charBuf中. import static java.n ...

  9. Protostuff序列化

    前言: Java序列化是Java技术体系当中的一个重要议题,序列化的意义在于信息的交换和存储,通常会和io.持久化.rmi技术有关(eg:一些orm框架会要求持久化的对象类型实现Serializabl ...

  10. PostgreSQL Replication之第十一章 使用Skytools(4)

    11.4 使用 londiste 复制数据 pgq是一个叫做londiste的复制工具的核心.londiste 的核心是有一个比如比Slony 更加简单,容易使用的机制.如果您在一个大的安装中使用Sl ...