<자바클래스 android.util.Log>
안드로이드에서 로그를 사용하고 싶은 경우에 자바 레이어에서는 android.util.Log 클래스를 사용한다.
android.util.Log 클래스는 frameworks/base/core/java/android/util/Log.java 에 구현되어 있다.
Log 클래스안에 구현된 메소드는 v(), d(), i(), w(), e() 등이 있다. 각각은 VERVOSE, DEBUG, INFO, WARN, ERROR 를 나타낸다. 이는 vervosity(자세함? 장황함)의 순서이다. Verbose 는 개발하는 동안에만 컴파일되고, Debug 는 컴파일되지만, runtime 시에 strip 된다. Error / warning / info 는 항상 동작한다.
보통은 본인이 작성하는 클래스에 TAG 라는 상수를 선언하는 것이 좋다. 왜냐면 Log 클래스의 메소드들의 첫번째 파라미터가 tag 를 나타내는 String 타입이기 때문에, 보통 Log 함수를 호출할 경우에 TAG 를 선언한 후에 Log.v(TAG, "statements") 와 같이 사용한다.
Log.v() 함수의 구현부를 살펴보자.
위 코드에서 보다시피 Log.v() 함수는 native 함수인 println_native 함수를 호출하도록 되어있다. println_native() 함수는 frameworks/base/core/jni/android_util_Log.cpp 에 구현되어 있다.
관련부분을 뜯어보면 위 코드와 같이 구현되어 있다. 즉 println_native() 함수는 jni 의 매핑을 통해서 실제로는 android_util_Log_println_native() 함수를 호출하도록 되어있다. JNI 로 두 함수가 연결되는 내용은 다음을 참조하면 된다. (JNI 네이티브 함수에 직접 등록방법) 이에 대해서는 따로 정리할 예정이다.
android_util_Log_println_native() 함수는 최종적으로 __android_log_buf_write() 함수를 호출한다. 이 함수는 system/core/liblog/logd_write.c 에 구현되어 있고, write_to_log 라는 함수 포인터를 호출하는 데, 처음에 __write_to_log_init 으로 세팅되어 있고, 결과적으로 __write_to_log_kernel 함수를 호출하게 된다.
__write_to_log_kernel 은 각각 관련된 character device 에 해당 로그를 write 하게 된다. 안드로이드의 log 는 총 3가지 카테고리로 나뉘는데, GSM/CDMA/RIL/SMS 등 radio 관련된 내용(/dev/radio)과 시스템/하드웨어 관련된 내용(/dev/system), 이벤트 관련 내용(/dev/events) 등으로 나뉜다.
device read/write 관련 자세한 것은 kernel/drivers/staging/android/logger.c 파일에 구현되어 있다.
파일을 열어보면 Robert Love 가 구현한 것으로 되어있다.(Linux Kernel Development 의 저자인 Robert Love 가 구글에 입사했군요.)
각각 character device 는 초기화 시점(init_logger())에 misc device 로 등록(register_misc())하고, 각각의 file operation 은 logger_fops 로 등록한다.(DEFINE_LOGGER_DEVICE)
각 디바이스는 log_main(64K) / log_events(128K) / log_radio(64K) / log_system(64k) 의 버퍼 크기를 갖는다.
logger_fops 는 다음과 같이 정의되었기 때문에, 해당 character device 에 write 를 하게 되면, logger_aio_write() 함수가 호출된다.
logger_aio_write() 는 최종적으로 do_write_log_from_user() 를 호출하고, 해당 함수는 디바이스에 해당하는 버퍼에 파라미터로 넘어온 user 영역 데이터를 write offset 부분에 기록하고, write offset 을 증가시킨다.
반대로 read 는 logger_read() 함수를 호출하고, 최종적으로 do_read_log_to_user() 를 호출한다. 해당 함수는 읽을 데이터가 없는 경우 TASK_INTERRUPTIBLE 상태로 sleep 한다. 데이터가 있는 경우 해당 버퍼에서 user 영역 메모리로 데이터를 복사해주고 read offset 을 수정해준다.
참고 : http://blog.csdn.net/fuyajun01/article/details/6787729
안드로이드에서 로그를 사용하고 싶은 경우에 자바 레이어에서는 android.util.Log 클래스를 사용한다.
android.util.Log 클래스는 frameworks/base/core/java/android/util/Log.java 에 구현되어 있다.
Log 클래스안에 구현된 메소드는 v(), d(), i(), w(), e() 등이 있다. 각각은 VERVOSE, DEBUG, INFO, WARN, ERROR 를 나타낸다. 이는 vervosity(자세함? 장황함)의 순서이다. Verbose 는 개발하는 동안에만 컴파일되고, Debug 는 컴파일되지만, runtime 시에 strip 된다. Error / warning / info 는 항상 동작한다.
보통은 본인이 작성하는 클래스에 TAG 라는 상수를 선언하는 것이 좋다. 왜냐면 Log 클래스의 메소드들의 첫번째 파라미터가 tag 를 나타내는 String 타입이기 때문에, 보통 Log 함수를 호출할 경우에 TAG 를 선언한 후에 Log.v(TAG, "statements") 와 같이 사용한다.
Log.v() 함수의 구현부를 살펴보자.
public static int v(String tag, String msg) {
return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
}
public static native int println_native(int bufID, int priority, String tag, String msg);
return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
}
public static native int println_native(int bufID, int priority, String tag, String msg);
위 코드에서 보다시피 Log.v() 함수는 native 함수인 println_native 함수를 호출하도록 되어있다. println_native() 함수는 frameworks/base/core/jni/android_util_Log.cpp 에 구현되어 있다.
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
const char* tag = NULL;
const char* msg = NULL;
if (msgObj == NULL) {
jclass npeClazz;
npeClazz = env->FindClass("java/lang/NullPointerException");
assert(npeClazz != NULL);
env->ThrowNew(npeClazz, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
jclass npeClazz;
npeClazz = env->FindClass("java/lang/NullPointerException");
assert(npeClazz != NULL);
env->ThrowNew(npeClazz, "bad bufID");
return -1;
}
if (tagObj != NULL)
tag = env->GetStringUTFChars(tagObj, NULL);
msg = env->GetStringUTFChars(msgObj, NULL);
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
if (tag != NULL)
env->ReleaseStringUTFChars(tagObj, tag);
env->ReleaseStringUTFChars(msgObj, msg);
return res;
}
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
{ "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
};
jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
const char* tag = NULL;
const char* msg = NULL;
if (msgObj == NULL) {
jclass npeClazz;
npeClazz = env->FindClass("java/lang/NullPointerException");
assert(npeClazz != NULL);
env->ThrowNew(npeClazz, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
jclass npeClazz;
npeClazz = env->FindClass("java/lang/NullPointerException");
assert(npeClazz != NULL);
env->ThrowNew(npeClazz, "bad bufID");
return -1;
}
if (tagObj != NULL)
tag = env->GetStringUTFChars(tagObj, NULL);
msg = env->GetStringUTFChars(msgObj, NULL);
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
if (tag != NULL)
env->ReleaseStringUTFChars(tagObj, tag);
env->ReleaseStringUTFChars(msgObj, msg);
return res;
}
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
{ "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
};
관련부분을 뜯어보면 위 코드와 같이 구현되어 있다. 즉 println_native() 함수는 jni 의 매핑을 통해서 실제로는 android_util_Log_println_native() 함수를 호출하도록 되어있다. JNI 로 두 함수가 연결되는 내용은 다음을 참조하면 된다. (JNI 네이티브 함수에 직접 등록방법) 이에 대해서는 따로 정리할 예정이다.
android_util_Log_println_native() 함수는 최종적으로 __android_log_buf_write() 함수를 호출한다. 이 함수는 system/core/liblog/logd_write.c 에 구현되어 있고, write_to_log 라는 함수 포인터를 호출하는 데, 처음에 __write_to_log_init 으로 세팅되어 있고, 결과적으로 __write_to_log_kernel 함수를 호출하게 된다.
__write_to_log_kernel 은 각각 관련된 character device 에 해당 로그를 write 하게 된다. 안드로이드의 log 는 총 3가지 카테고리로 나뉘는데, GSM/CDMA/RIL/SMS 등 radio 관련된 내용(/dev/radio)과 시스템/하드웨어 관련된 내용(/dev/system), 이벤트 관련 내용(/dev/events) 등으로 나뉜다.
device read/write 관련 자세한 것은 kernel/drivers/staging/android/logger.c 파일에 구현되어 있다.
파일을 열어보면 Robert Love 가 구현한 것으로 되어있다.(Linux Kernel Development 의 저자인 Robert Love 가 구글에 입사했군요.)
각각 character device 는 초기화 시점(init_logger())에 misc device 로 등록(register_misc())하고, 각각의 file operation 은 logger_fops 로 등록한다.(DEFINE_LOGGER_DEVICE)
각 디바이스는 log_main(64K) / log_events(128K) / log_radio(64K) / log_system(64k) 의 버퍼 크기를 갖는다.
logger_fops 는 다음과 같이 정의되었기 때문에, 해당 character device 에 write 를 하게 되면, logger_aio_write() 함수가 호출된다.
static const struct file_operations logger_fops = {
.owner = THIS_MODULE,
.read = logger_read,
.aio_write = logger_aio_write,
.poll = logger_poll,
.unlocked_ioctl = logger_ioctl,
.compat_ioctl = logger_ioctl,
.open = logger_open,
.release = logger_release,
};
.owner = THIS_MODULE,
.read = logger_read,
.aio_write = logger_aio_write,
.poll = logger_poll,
.unlocked_ioctl = logger_ioctl,
.compat_ioctl = logger_ioctl,
.open = logger_open,
.release = logger_release,
};
logger_aio_write() 는 최종적으로 do_write_log_from_user() 를 호출하고, 해당 함수는 디바이스에 해당하는 버퍼에 파라미터로 넘어온 user 영역 데이터를 write offset 부분에 기록하고, write offset 을 증가시킨다.
반대로 read 는 logger_read() 함수를 호출하고, 최종적으로 do_read_log_to_user() 를 호출한다. 해당 함수는 읽을 데이터가 없는 경우 TASK_INTERRUPTIBLE 상태로 sleep 한다. 데이터가 있는 경우 해당 버퍼에서 user 영역 메모리로 데이터를 복사해주고 read offset 을 수정해준다.
참고 : http://blog.csdn.net/fuyajun01/article/details/6787729
'IT 기술 > Android' 카테고리의 다른 글
Android Framework 에 Java 라이브러리 및 JNI 추가하기 (2) | 2012.01.03 |
---|---|
JNI 를 사용한 간단한 Android Project (0) | 2011.12.22 |
[Android] static library를 사용하여 shared library 만들기 (0) | 2011.08.10 |