天天看點

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.