Log在android应用开发中是使用频率非常高的一个类,在没有了解这个类之前都是使用System.out.println(),这个 是Java中的一个方法。
Log类定义了六个静态变量:
public static final int VERBOSE = 2;
/** * Priority constant for the println method; use Log.d. */
public static final int DEBUG = 3;
/** * Priority constant for the println method; use Log.i. */
public static final int INFO = 4;
/** * Priority constant for the println method; use Log.w. */
public static final int WARN = 5;
/** * Priority constant for the println method; use Log.e. */
public static final int ERROR = 6;
/** * Priority constant for the println method. */
public static final int ASSERT = 7;
Log.v()、Log.d()、 Log.i()、 Log.w()、 Log.e() 首字母分别对应分VERBOSE(详细的),DEBUG(调试),INFO(信息), WARN(警告),ERROR(错误)。
从v到e,v总是会打印的,当发生错误时,e才会打印,v仍然会打印。ASSERT是为了让错误不要再报告而设置的,对应Log.wtf()方法。还有一个方法
是isLoggable(),通过这个方法可用日志的级别做设置,默认是Log.i(),当长度大于23时会抛出异常。这些方法是native方法。
在android源码的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) {
jniThrowNullPointerException(env, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
jniThrowNullPointerException(env, "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);
}
__android_log_buf_write位于Android源代码的system\core\liblog中的logd_write.c文件中。
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
{
struct iovec vec[3];
char tmp_tag[32];
if (!tag)
tag = "";
/* XXX: This needs to go! */
if ((bufID != LOG_ID_RADIO) &&
(!strcmp(tag, "HTC_RIL") ||
!strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
!strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
!strcmp(tag, "AT") ||
!strcmp(tag, "GSM") ||
!strcmp(tag, "STK") ||
!strcmp(tag, "CDMA") ||
!strcmp(tag, "PHONE") ||
!strcmp(tag, "SMS"))) {
bufID = LOG_ID_RADIO;
// Inform third party apps/ril/radio.. to use Rlog or RLOG
snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
tag = tmp_tag;
}
vec[0].iov_base = (unsigned char *) &prio;//优先级
vec[0].iov_len = 1;
vec[1].iov_base = (void *) tag; //tag标志
vec[1].iov_len = strlen(tag) + 1;
vec[2].iov_base = (void *) msg; //msg 日志内容
vec[2].iov_len = strlen(msg) + 1;
return write_to_log(bufID, vec, 3);
}
从代码上看就是我们要调用的Log是带缓冲区的,接下来它自己又调用了不带缓冲区的。
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;//初始化
__write_to_log_init接下来会调用内核提供的API
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
{
#ifdef HAVE_PTHREADS
pthread_mutex_lock(&log_init_lock);
#endif
if (write_to_log == __write_to_log_init) {
log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
write_to_log = __write_to_log_kernel;
if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
log_fds[LOG_ID_EVENTS] < 0) {
log_close(log_fds[LOG_ID_MAIN]);
log_close(log_fds[LOG_ID_RADIO]);
log_close(log_fds[LOG_ID_EVENTS]);
log_fds[LOG_ID_MAIN] = -1;
log_fds[LOG_ID_RADIO] = -1;
log_fds[LOG_ID_EVENTS] = -1;
write_to_log = __write_to_log_null;
}
if (log_fds[LOG_ID_SYSTEM] < 0) {
log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
}
}
#ifdef HAVE_PTHREADS
pthread_mutex_unlock(&log_init_lock);
#endif
return write_to_log(log_id, vec, nr);
}
这样就成了文件IO操作了,原来打个日志还这么麻烦。