赞
踩
提供了一些格式化输出、美观
// 添加依赖
implementation 'com.orhanobut:logger:2.2.0'
// 初始化
Logger.addLogAdapter(new AndroidLogAdapter());
// 使用
Logger.d("hello,Android");
基于原生Log
类的小型可扩展的log框架
使用注解形式的调试版本log框架
可扩展,支持多种数据格式,支持线程和调用栈信息
支持多种数据结构,支持系统对象,支持高性能写入文件(mmap)
基于mmap内存映射,最大化保证日志完整性
Android 系统 Java 层 Log 定义在 /frameworks/base/core/java/android/util/Log.java
Log 提供了六种日志级别,并定义了一系列静态方法:
public static int v/d/i/w/e/wtf(String tag, String msg) {
return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
}
public static int v/d/i/w/e/wtf(String tag, String msg, Throwable tr) {
return printlns(LOG_ID_MAIN, VERBOSE, tag, msg, tr);
}
下面这种调用带异常记录,会调用 Log 类内部类 ImediateLogWriter
来写入日志消息,最终也会调用 println_native
。
说明:Android 中不同的 log 会指定不同的缓冲区然后被写入到不同的设备中,包括 system(系统相关)、radio(无线/电话相关)、event(事件相关)、main(主缓冲区,默认)
本地实现定义在 /frameworks/base/core/jni/android_util_Log.cpp
static const JNINativeMethod gMethods[] = {
{
"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 },
{
"logger_entry_max_payload_native", "()I", (void*) android_util_Log_logger_entry_max_payload_native },
};
android_util_Log_isLoggable
isLoggable
判断
__android_log_is_loggable
获取 logLevel【/system/core/liblog/properties.c,编译成 liblog.so】android_util_Log_println_native
__android_log_buf_write
【/system/core/liblog/logger_write.c,编译到 liblog.so】Native 层通过定义一系列宏的方式提供 log 功能,全部是调用了 __android_log_print
LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char* tag,
const char* fmt, ...) {
va_list ap;
char buf[LOG_BUF_SIZE];
va_start(ap, fmt);
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
return __android_log_write(prio, tag, buf);
}
这个过程使用了可变参数,va 就是 variable-argument,相关宏定义在 stdarg.h 中
最后函数会调用 __android_log_write
LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag,
const char* msg) {
return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}
这里 Java/C++ 层就走到同一个函数,在这个函数中会实现写设备文件
liblog.so 会被所有需要日志操作的进程加载,负责处理打印和读取日志的流程
主要代码及逻辑都在 /system/core/liblog/logger_write.c 中,__android_log_buf_write
里面做了下面这几件事:
定义 iovec 结构数组 vec[3],三个元素分别存放 prio、tag、msg
struct iovec {
void* iov_base;
size_t iov_len;
};
iovec 结构体包含一个指向缓冲区的指针和读/写的长度
判断 bufID 修改 tag(bufID 这个变量表示不同的 log 缓冲区,或者理解成写到不同的文件里)
最后调用 write_to_log
write_to_log
是一个函数指针,初始设置指向 __write_to_log_init
,进入 __write_to_log_init
后,首先会去调用 __write_to_log_initialize
,然后将 write_to_log
设置为指向 __write_to_log_daemon
,然后又调用一次 write_to_log
static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init; static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) { __android_log_lock(); if (write_to_log == __write_to_log_init) { int ret; ret = __write_to_log_initialize(); if (ret < 0) { __android_log_unlock(); if (!list_empty(&__android_log_persist_write)) { __write_to_log_daemon(log_id, vec, nr); } return ret; } write_to_log = __write_to_log_daemon; } __android_log_unlock(); return write_to_log(log_id, vec, nr); }
__write_to_log_initialize
和 __write_to_log_daemon
实现太复杂了,总结下来就是 initialize 基于默认配置构造结构体链表,daemon 从链表中取出节点,就是 android_log_transport_write
,节点的结构体的定义在liblog/logger.h 中
struct android_log_transport_write {
struct listnode node;
const char* name; /* human name to describe the transport */
unsigned logMask; /* mask cache of available() success */
union android_log_context context; /* Initialized by static allocation */
int (*available)(log_id_t logId); /* Does not cause resources to be taken */
int (*open)(); /* can be called multiple times, reusing current resources */
void (*close)(); /* free up resources */
int (*write)(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr); /* write log to transport, returns number of bytes propagated, or -errno */
};
初始化 的时候会调用 __android_log_config_write
会基于不同的场景定义不同的结构去写日志,包括 localLoggerWrite、logdLoggerWrite、pmsgLoggerWrite、fakeLoggerWrite、stderrLoggerWrite,调用 retval = (*node->write)(log_id, &ts, vec, nr) 进行 write 操作
LIBLOG_HIDDEN void __android_log_config_write() { if (__android_log_transport & LOGGER_LOCAL) { extern struct android_log_transport_write localLoggerWrite; __android_log_add_transport(&__android_log_transport_write, &localLoggerWrite); } if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) { #if (FAKE_LOG_DEVICE == 0) extern struct android_log_transport_write logdLoggerWrite; extern struct android_log_transport_write pmsgLoggerWrite; __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite); __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite); #else extern struct android_log_transport_write fakeLoggerWrite; __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite); #endif } if (__android_log_transport & LOGGER_STDERR) { extern struct android_log_transport_write stderrLoggerWrite; if (list_empty(&__android_log_transport_write)) { __android_log_add_transport(&__android_log_transport_write, &stderrLoggerWrite); } else { struct android_log_transport_write* transp; write_transport_for_each(transp, &__android_log_transport_write) { if (transp == &stderrLoggerWrite) { return; } } __android_log_add_transport(&__android_log_persist_write, &stderrLoggerWrite); } } }
看其中比较重要的几个,logdLoggerWrite
的定义在 /system/core/liblog/logd_writer.c
LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
.node = {
&logdLoggerWrite.node, &logdLoggerWrite.node },
.context.sock = -EBADF,
.name = "logd",
.available = logdAvailable,
.open = logdOpen,
.close = logdClose,
.write = logdWrite,
};
其中,logdOpen 方法创建 sockaddr_un 结构体并将 “/dev/socket/logdw” 写入 sun_path 成员变量中,然后调用 connect 去建立连接,并将套接字标识放到 logdLoggerWrite.context.sock 中;write 函数指针指向 logdWrite
方法,会调用 writev
非阻塞地写入日志信息
同理,pmsgLoggerWrite 打开的是 “/dev/pmsg0” 的 socket
所以,liblog.so 的一个主要工作就是写入日志
使用 file 命令查看 /dev/socket/logdw 发现是一个 socket,这个 socket 是由 logd 创建的,见 /system/corelogd/logd.rc
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
writepid /dev/cpuset/system-background/tasks
logd 是 C 层的守护进程,由 init 进程创建(创建 servicemanager 时同时创建),可以看到启动 logd 后会创建三个 socket,分别为 logd 用来监听命令、logdr 用于读日志、logdw 用于写日志,还打开了两个文件,修改了 user id 和 group id,并把 pid 写文件
logd 启动后会从 main 函数开始执行,见 /system/core/logd/main.cpp
第一步、打开 /dev/kmsg 用于写内核 log 的,在 logd 还未启动或出错时,只能写到内核日志中
int main(int argc, char* argv[]) {
......
static const char dev_kmsg[] = "/dev/kmsg";
fdDmesg = android_get_control_file(dev_kmsg);
if (fdDmesg < 0) {
fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
}
......
}
android_get_control_file 里面先调用 __android_get_control_from_env
拼接 Android 文件前缀 ANDROID_FILE_ 和路径 /dev/kmsg,然后做符号转换得到 ANDROID_FILE__dev_kmsg
,接下来通过 getenv 获取这个环境变量的值,最后通过 strtol 将这个值转换成 long 类型就是文件描述符,还要通过 fcntl 去验证下文件是不是开着
这里用了三种方法去验证:
__android_get_control_from_env
返回文件描述符后,还会调用 /proc/self/fd/fd_num 验证一次
第二步、打开 /proc/kmsg 用于读内核日志
int main(int argc, char* argv[]) { ...... bool klogd = __android_logger_property_get_bool( "logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE); if (klogd) { static const char proc_kmsg[] = "/proc/kmsg"; fdPmesg = android_get_control_file(proc_kmsg); if (fdPmesg < 0) { fdPmesg = TEMP_FAILURE_RETRY( open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC)); } if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg); } ...... }
第三步、启动 reinit 线程,处理 --reinit 命令,此外这个线程还会完成 uid 转 name
第四步、设置运行时优先级和权限【略】
第五步、启动 log 监听
看一下 logd 的线程,可以看到 logd 进程 pid = 574,ppid = 1,即 init 进程孵化,reinit_thread_start
函数启动线程“logd.daemon”,LogReader 启动线程“logd.reader”监听 /dev/socket/logdr,LogListener 启动线程“logd.writer”监听 /dev/socket/logdw,CommandListener 启动线程“logd.control”监听 /dev/socket/logd,LogAudit 启动线程“logd.auditd”,LogKlog 启动线程“logd.klogd”,LogTimeEntry 启动线程“logd.reader.per”
logd 574 574 1 32428 4912 SyS_rt_sigsuspend 753d7b1634 S logd
logd 574 577 1 32428 4912 futex_wait_queue_me 753d7644b0 S logd.daemon
logd 574 578 1 32428 4912 do_select 753d7b15a4 S logd.reader
logd 574 579 1 32428 4912 do_select 753d7b15a4 S logd.writer
logd 574 580 1 32428 4912 do_select 753d7b15a4 S logd.control
logd 574 582 1 32428 4912 do_select 753d7b15a4 S logd.auditd
logd 574 14402 1 32428 4912 futex_wait_queue_me 753d7644b0 S logd.reader.per
LogBuffer 继承自 LogBufferInterface,类内部定义了很多成员变量和一些函数,包括:
LogBufferElementCollection 是一个 LogBufferElement 指针类型的 list
LogBufferElement 中存放 LogBuffer 元素的相关信息,包括 uint32_t 类型的 uid/pid/tid,还有 realTime、消息内容 msg,logId 等;
LastLogTimes 是一个 LogTimeEntry 指针类型的 list,两者都定义在 LogTime 中,LogTimeEntry 里面包
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。