转自: 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中可以看到:
02 |
* Priority constant for the println method; use Log.v. |
04 |
public static final int VERBOSE = 2; |
07 |
* Priority constant for the println method; use Log.d. |
09 |
public static final int DEBUG = 3; |
12 |
* Priority constant for the println method; use Log.i. |
14 |
public static final int INFO = 4; |
17 |
* Priority constant for the println method; use Log.w. |
19 |
public static final int WARN = 5; |
22 |
* Priority constant for the println method; use Log.e. |
24 |
public static final int ERROR = 6; |
27 |
* Priority constant for the println method. |
29 |
public static final int ASSERT = 7; |
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)); |
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. |
11 |
public static int d(String tag, String msg) { |
12 |
return println_native(LOG_ID_MAIN, DEBUG, tag, msg); |
最终都会是调用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) |
04 |
const char* tag = NULL; |
05 |
const char* msg = NULL; |
08 |
jniThrowNullPointerException(env, "println needs a message"); |
12 |
if (bufID < 0 || bufID >= LOG_ID_MAX) { |
13 |
jniThrowNullPointerException(env, "bad bufID"); |
18 |
tag = env->GetStringUTFChars(tagObj, NULL); |
19 |
msg = env->GetStringUTFChars(msgObj, NULL); |
21 |
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg); |
24 |
env->ReleaseStringUTFChars(tagObj, tag); |
25 |
env->ReleaseStringUTFChars(msgObj, msg); |
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 }, |
可以看到,干的都是转换参数的事情,最终再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) |
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 */ |
15 |
!strcmp(tag, "GSM") || |
16 |
!strcmp(tag, "STK") || |
17 |
!strcmp(tag, "CDMA") || |
18 |
!strcmp(tag, "PHONE") || |
19 |
!strcmp(tag, "SMS"))) { |
21 |
// Inform third party apps/ril/radio.. to use Rlog or RLOG |
22 |
snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); |
26 |
vec[0].iov_base = (unsigned char *) &prio; |
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; |
33 |
return write_to_log(bufID, vec, 3); |
做了三件事情,一是根据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) |
04 |
pthread_mutex_lock(&log_init_lock); |
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); |
13 |
write_to_log = __write_to_log_kernel; |
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; |
26 |
if (log_fds[LOG_ID_SYSTEM] < 0) { |
27 |
log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; |
32 |
pthread_mutex_unlock(&log_init_lock); |
35 |
return write_to_log(log_id, vec, nr); |
这个地方,会检查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) |
06 |
static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) |
11 |
if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { |
12 |
log_fd = log_fds[(int)log_id]; |
18 |
ret = log_writev(log_fd, vec, nr); |
19 |
} while (ret < 0 && errno == EINTR); |
由log_id获取到对应的log_fd,然后调用log_writev()打印log。可以看一下log_writev()的定义,它是一个宏:
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) |
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) |
这些就都是标准的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; |
05 |
size_t cacheSize = mCache.size(); |
07 |
ALOGD("------------------------------------------------"); |
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("------------------------------------------------"); |
使用这组宏,需要定义另外一个宏来作为所打印log的tag:
1 |
#define LOG_TAG "TextLayoutCache" |
此外,还要include头文件<cutils/log.h>。来看一下这些宏中的一些的定义:
02 |
* Simplified macro to send a debug log message using the current LOG_TAG. |
05 |
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) |
09 |
* Simplified macro to send a warning log message using the current LOG_TAG. |
12 |
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) |
16 |
* Basic log message macro. |
19 |
* ALOG(LOG_WARN, NULL, "Failed with error %d", errno); |
21 |
* The second argument may be NULL or "" to indicate the "global" tag. |
24 |
#define ALOG(priority, tag, ...) \ |
25 |
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) |
29 |
* Log macro that allows you to specify a number for the priority. |
32 |
#define LOG_PRI(priority, tag, ...) \ |
33 |
android_printLog(priority, tag, __VA_ARGS__) |
36 |
#define android_printLog(prio, tag, fmt...) \ |
37 |
__android_log_print(prio, tag, fmt) |
先来看一下,在native层中定义的priority(在system/core/include/android/log.h中):
02 |
* Android log priority values, in ascending priority order. |
04 |
typedef enum android_LogPriority { |
05 |
ANDROID_LOG_UNKNOWN = 0, |
06 |
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ |
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, ...) |
04 |
char buf[LOG_BUF_SIZE]; |
07 |
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); |
10 |
return __android_log_write(prio, tag, buf); |
先是格式化参数,然后就是调用__android_log_write()函数。这个函数的code如下:
01 |
int __android_log_write(int prio, const char *tag, const char *msg) |
04 |
log_id_t log_id = LOG_ID_MAIN; |
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 */ |
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); |
26 |
vec[0].iov_base = (unsigned char *) &prio; |
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; |
33 |
return write_to_log(log_id, vec, 3); |
这个函数与我们前面看到的__android_log_buf_write()非常相似。所不同的就是这个函数没有log_id参数,因而它默认是输出MAIN log,当log的TAG为某些特殊字串时,则输出RADIO log。最后同样是调用write_to_log这个函数指针来输出log。
我们再来看一个skia里面打log的SkDebugf()函数的实现:
1 |
#include <android/log.h> |
3 |
void SkDebugf(const char format[], ...) { |
5 |
va_start(args, format); |
6 |
__android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, format, args); |
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) |
3 |
char buf[LOG_BUF_SIZE]; |
5 |
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); |
7 |
return __android_log_write(prio, tag, buf); |
一样是__android_log_write()函数。
Done.
- 我的手机华为荣耀7,运行android程序不输出Log
解决方法:1 进入手机拨号界面2 输入*#*#2846579#*#*3 输入完毕后自动跳转到<工程菜单>界面4 依次选择后台设置-->LOG设置-->在此可以看见一些列关于LO ...
- 解读Android LOG机制的实现【转】
转自:http://www.cnblogs.com/hoys/archive/2011/09/30/2196199.html http://armboard.taobao.com/ Android提供 ...
- 实现Android Studio JNI开发C/C++使用__android_log_print输出Log
相信很多人在刚开始学习Android JNI编程的时候,需要输出Log,在百度Google搜索的时候都是说需要在Android.mk中加入LOCAL_LDLIBS+= -L$(SYSROOT)/usr ...
- [转]Android输出Log到文件
前言:开发中遇到mx4这款机型Eclipse联调不上,logcat看不了,需要输出生成文件查看调试信息.网上搜了下,功能很完善了.startService和过滤输出信息需要自己添加设置,另外注意添加权 ...
- adb logcat命令查看并过滤android输出log
cmd命令行中使用adb logcat命令查看android系统和应用的log,dos窗口按ctrl+c中断输出log记录. logcat日志中的优先级/tag标记: android输出的每一条日志都 ...
- Android学习笔记——log无法输出的解决方法和命令行查看log日志
本人邮箱:JohnTsai.Work@gmail.com,欢迎交流讨论. 欢迎转载,转载请注明网址:http://www.cnblogs.com/JohnTsai/p/3983936.html. 知识 ...
- 【android】 adb logcat命令查看并过滤android输出log
cmd命令行中使用adb logcat命令查看android系统和应用的log,dos窗口按ctrl+c中断输出log记录. logcat日志中的优先级/tag标记: android输出的每一条日志都 ...
- Android中Log机制详解
Android中Log的输出有如下几种: Log.v(String tag, String msg); //VERBOSELog.d(String tag, String msg); ...
- Android关于log日志,华为不输出log.v,log.d(zz)
[java] view plain copy 我用的是mate8,本来虚拟机测试一点日志一点问题没有 [java] view plain copy 但是真机测试发现log.d一直不输出,正好又试了lo ...
- 获取微信服务器IP地址
关键字:微信公众平台 微信服务器 IP地址 如果公众号基于安全等考虑,需要获知微信服务器的IP地址列表,以便进行相关限制,可以通过该接口获得微信服务器IP地址列表. 接口调用请求说明 http请求方式 ...
- Python线程指南
本文介绍了Python对于线程的支持,包括“学会”多线程编程需要掌握的基础以及Python两个线程标准库的完整介绍及使用示例. 注意:本文基于Python2.4完成,:如果看到不明白的词汇请记得百度谷 ...
- 分布式消息系统Kafka初步
终于可以写kafka的文章了,Mina的相关文章我已经做了索引,在我的博客中置顶了,大家可以方便的找到.从这一篇开始分布式消息系统的入门. 在我们大量使用分布式数据库.分布式计算集群的时候,是否会遇到 ...
- ThinkPHP 自动验证与自动填充无效可能的原因(转)
自动验证与自动填充是在使用ThinkPHP时经常用到的功能,但偶尔会遇到自动验证与自动填充无效的情况,本文就ThinkPHP 自动验证与自动填充无效可能的原因做一些分析. create() Think ...
- oracle恢复备份数据
sqlplus链接数据库: 1.sqlplus 用户名/密码@IP地址/数据库名称 2.sqlplus 用户名/密码@数据库名称 注:第二种方法要在tnsnames.oRA文件中配置数据库名称 链接断 ...
- java版本的sqlHelper
以下版本的sqlHelper可以支持普通的DDL,DML和查询语句,对于连接池,事务等的支持还有待改进 1)将数据库连接相关信息存储为属性文件,如database.properties,建立DataB ...
- iPhone尺寸规范
转载于:http://www.uigreat.com/page/guifan
- 如何修改DBSNMP和SYSMAN用户的密码
SYSMAN和DBSNMP用户密码过期后OEM无法使用,并报以下错误: SYSMAN用户的密码被加密后存放在不同的地方,这样database control(OMS和agent)可以不用每次访问数据库 ...
- SQL百分比
百分比=cast(cast(count(ProvinceName)*100./ (select count(Id) from dbo.TopicCurrent where boardId=316 ...
- nyist28大数阶乘
http://acm.nyist.net/JudgeOnline/problem.php?pid=28 大数阶乘 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 我们 ...