您的位置:首页 > 移动开发 > Android开发

android log机制——输出log【转】

2016-08-23 16:17 267 查看
转自:http://blog.csdn.net/tdstds/article/details/19084327

目录(?)[-]

在androidJavacode中输出log

Logprintln_native方法

本地层代码Log输出

转自:http://my.oschina.net/wolfcs/blog/164624
Androidlog系统。

在androidJavacode中输出log

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

01
/**
02
*Priorityconstantfortheprintlnmethod;useLog.v.
03
*/
04
public
static
final
int
VERBOSE=
2
;
05
06
/**
07
*Priorityconstantfortheprintlnmethod;useLog.d.
08
*/
09
public
static
final
int
DEBUG=
3
;
10
11
/**
12
*Priorityconstantfortheprintlnmethod;useLog.i.
13
*/
14
public
static
final
int
INFO=
4
;
15
16
/**
17
*Priorityconstantfortheprintlnmethod;useLog.w.
18
*/
19
public
static
final
int
WARN=
5
;
20
21
/**
22
*Priorityconstantfortheprintlnmethod;useLog.e.
23
*/
24
public
static
final
int
ERROR=
6
;
25
26
/**
27
*Priorityconstantfortheprintlnmethod.
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(Stringtag,Stringmsg,Throwabletr){
02
return
println_native(LOG_ID_MAIN,VERBOSE,tag,msg+
'\n'
+getStackTraceString(tr));
03
}
04
05
/**
06
*Senda{@link#DEBUG}logmessage.
07
*@paramtagUsedtoidentifythesourceofalogmessage.Itusuallyidentifies
08
*theclassoractivitywherethelogcalloccurs.
09
*@parammsgThemessageyouwouldlikelogged.
10
*/
11
public
static
int
d(Stringtag,Stringmsg){
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
jintandroid_util_Log_println_native(JNIEnv*env,jobjectclazz,
02
jintbufID,jintpriority,jstringtagObj,jstringmsgObj)
03
{
04
const
char
*tag=NULL;
05
const
char
*msg=NULL;
06
07
if
(msgObj==NULL){
08
jniThrowNullPointerException(env,
"printlnneedsamessage"
);
09
return
-1;
10
}
11
12
if
(bufID<0||bufID>=LOG_ID_MAX){
13
jniThrowNullPointerException(env,
"badbufID"
);
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
*JNIregistration.
32
*/
33
static
JNINativeMethodgMethods[]={
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
iovecvec[3];
04
char
tmp_tag[32];
05
06
if
(!tag)
07
tag=
""
;
08
09
/*XXX:Thisneedstogo!*/
10
if
((bufID!=LOG_ID_RADIO)&&
11
(!
strcmp
(tag,
"HTC_RIL"
)||
12
!
strncmp
(tag,
"RIL"
,3)||
/*Anylogtagwith"RIL"astheprefix*/
13
!
strncmp
(tag,
"IMS"
,3)||
/*Anylogtagwith"IMS"astheprefix*/
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
//Informthirdpartyapps/ril/radio..touseRlogorRLOG
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,二是用传进来的参数构造一个structiovec数组,三是将前一步构造的数组作为参数调用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_tlog_id,
struct
iovec*vec,
size_t
nr)
02
{
03
#ifdefHAVE_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
#ifdefHAVE_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
#defineLOGGER_LOG_MAIN"log/main"
2
#defineLOGGER_LOG_RADIO"log/radio"
3
#defineLOGGER_LOG_EVENTS"log/events"
4
#defineLOGGER_LOG_SYSTEM"log/system"
接着继续来看__write_to_log_kernel或者__write_to_log_null函数:

01
static
int
__write_to_log_null(log_id_tlog_fd,
struct
iovec*vec,
size_t
nr)
02
{
03
return
-1;
04
}
05
06
static
int
__write_to_log_kernel(log_id_tlog_id,
struct
iovec*vec,
size_t
nr)
07
{
08
ssize_tret;
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
#ifFAKE_LOG_DEVICE
02
//Thiswillbedefinedwhenbuildingforthehost.
03
#definelog_open(pathname,flags)fakeLogOpen(pathname,flags)
04
#definelog_writev(filedes,vector,count)fakeLogWritev(filedes,vector,count)
05
#definelog_close(filedes)fakeLogClose(filedes)
06
#else
07
#definelog_open(pathname,flags)open(pathname,(flags)|O_CLOEXEC)
08
#definelog_writev(filedes,vector,count)writev(filedes,vector,count)
09
#definelog_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(
"Cachestats"
);
09
ALOGD(
"------------------------------------------------"
);
10
ALOGD(
"pid:%d"
,getpid());
11
ALOGD(
"running:%.0fseconds"
,timeRunningInSec);
12
ALOGD(
"entries:%d"
,cacheSize);
13
ALOGD(
"maxsize:%dbytes"
,mMaxSize);
14
ALOGD(
"used:%dbytesaccordingtomSize"
,mSize);
15
ALOGD(
"remaining:%dbytesor%2.2fpercent"
,mMaxSize-mSize,remainingPercent);
16
ALOGD(
"hits:%d"
,mCacheHitCount);
17
ALOGD(
"saved:%0.6fms"
,mNanosecondsSaved*0.000001f);
18
ALOGD(
"------------------------------------------------"
);
19
}
使用这组宏,需要定义另外一个宏来作为所打印log的tag:

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

01
/*
02
*SimplifiedmacrotosendadebuglogmessageusingthecurrentLOG_TAG.
03
*/
04
#ifndefALOGD
05
#defineALOGD(...)((void)ALOG(LOG_DEBUG,LOG_TAG,__VA_ARGS__))
06
#endif
07
08
/*
09
*SimplifiedmacrotosendawarninglogmessageusingthecurrentLOG_TAG.
10
*/
11
#ifndefALOGW
12
#defineALOGW(...)((void)ALOG(LOG_WARN,LOG_TAG,__VA_ARGS__))
13
#endif
14
15
/*
16
*Basiclogmessagemacro.
17
*
18
*Example:
19
*ALOG(LOG_WARN,NULL,"Failedwitherror%d",errno);
20
*
21
*ThesecondargumentmaybeNULLor""toindicatethe"global"tag.
22
*/
23
#ifndefALOG
24
#defineALOG(priority,tag,...)\
25
LOG_PRI(ANDROID_##priority,tag,__VA_ARGS__)
26
#endif
27
28
/*
29
*Logmacrothatallowsyoutospecifyanumberforthepriority.
30
*/
31
#ifndefLOG_PRI
32
#defineLOG_PRI(priority,tag,...)\
33
android_printLog(priority,tag,__VA_ARGS__)
34
#endif
35
36
#defineandroid_printLog(prio,tag,fmt...)\
37
__android_log_print(prio,tag,fmt)
先来看一下,在native层中定义的priority(在system/core/include/android/log.h中):

01
/*
02
*Androidlogpriorityvalues,inascendingpriorityorder.
03
*/
04
typedef
enum
android_LogPriority{
05
ANDROID_LOG_UNKNOWN=0,
06
ANDROID_LOG_DEFAULT,
/*onlyforSetMinPriority()*/
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,
/*onlyforSetMinPriority();mustbelast*/
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
iovecvec[3];
04
log_id_tlog_id=LOG_ID_MAIN;
05
char
tmp_tag[32];
06
07
if
(!tag)
08
tag=
""
;
09
10
/*XXX:Thisneedstogo!*/
11
if
(!
strcmp
(tag,
"HTC_RIL"
)||
12
!
strncmp
(tag,
"RIL"
,3)||
/*Anylogtagwith"RIL"astheprefix*/
13
!
strncmp
(tag,
"IMS"
,3)||
/*Anylogtagwith"IMS"astheprefix*/
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
//Informthirdpartyapps/ril/radio..touseRlogorRLOG
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参数,因而它默认是输出MAINlog,当log的TAG为某些特殊字串时,则输出RADIOlog。最后同样是调用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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: