天天看点

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

转自:http://blog.csdn.net/tdstds/article/details/19084327

目录(?)[-]

  1. 在android Java code中输出log
    1. Logprintln_native方法
  2. 本地层代码Log输出

转自: 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

04

public

static

final

int

VERBOSE = 

2

;

05

06

09

public

static

final

int

DEBUG = 

3

;

10

11

14

public

static

final

int

INFO = 

4

;

15

16

19

public

static

final

int

WARN = 

5

;

20

21

24

public

static

final

int

ERROR = 

6

;

25

26

29

public

static

final

int

ASSERT = 

7

;

30

31

public

static

final

int

LOG_ID_MAIN = 

;

32

public

static

final

int

LOG_ID_RADIO = 

1

;

33

public

static

final

int

LOG_ID_EVENTS = 

2

;

34

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

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

33

static

JNINativeMethod gMethods[] = {

34

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

10

if

((bufID != LOG_ID_RADIO) &&

11

(!

strcmp

(tag, 

"HTC_RIL"

) ||

12

!

strncmp

(tag, 

"RIL"

, 3) || 

13

!

strncmp

(tag, 

"IMS"

, 3) || 

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 < (

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

04

#ifndef ALOGD

05

#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))

06

#endif

07

08

11

#ifndef ALOGW

12

#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))

13

#endif

14

15

23

#ifndef ALOG

24

#define ALOG(priority, tag, ...) \

25

LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)

26

#endif

27

28

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

04

typedef

enum

android_LogPriority {

05

ANDROID_LOG_UNKNOWN = 0,

06

ANDROID_LOG_DEFAULT,    

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,     

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

11

if

(!

strcmp

(tag, 

"HTC_RIL"

) ||

12

!

strncmp

(tag, 

"RIL"

, 3) || 

13

!

strncmp

(tag, 

"IMS"

, 3) || 

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.