前言
logd 是Android L版本提出来的概念,其作用是保存Android运行期间的log(日志)。在Android L之前,log由kernel的ring buffer 保存,在Android L之后,log保存在用户空间。
代码基于版本:Android P
架构
log系统大概分三个部分:
- 上层接口。例如ALOGD、log.d、Slog.d等
- liblog
- logd
在Android framework中有Log.java、Slog.java等文件提供了上层应用使用的接口,在native或jni代码中会使用liblog/include/log/log.h中定义的宏接口ALOGD、ALOGE等接口,但是最终都是通过liblog中logger_write.cpp下的__android_log_write接口。
先来看下__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);
-
}
调用__android_log_buf_write():
-
LIBLOG_ABI_PUBLIC
int __android_log_buf_write(
int bufID,
int prio,
-
const
char* tag,
const
char* msg) {
-
struct
iovec vec[
3];
-
char tmp_tag[
32];
-
-
...
-
...
-
-
vec[
0].iov_base = (
unsigned
char*)&prio;
-
vec[
0].iov_len =
1;
-
vec[
1].iov_base = (
void*)tag;
-
vec[
1].iov_len =
strlen(tag) +
1;
-
vec[
2].iov_base = (
void*)msg;
-
vec[
2].iov_len =
strlen(msg) +
1;
-
-
return
write_to_log(bufID, vec,
3);
-
}
调用到的是write_to_log(),而在初始化的时候write_log_log指向的是__write_to_log_init():
-
static int (*write_to_log)(log_id_t, struct iovec* vec,
-
size_t nr) = __write_to_log_init;
logger_write.c 中这个函数很重要,所有的源头都是从这里开始的,最终会调用__write_to_log_daemon:
-
static
int __write_to_log_init(
log_id_t log_id,
struct iovec* vec,
size_t nr) {
-
int ret, save_errno = errno;
-
-
__android_log_lock();
-
-
if (write_to_log == __write_to_log_init) {
-
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);
-
}
-
errno = save_errno;
-
return ret;
-
}
-
-
write_to_log = __write_to_log_daemon;
-
}
-
-
__android_log_unlock();
-
-
ret =
write_to_log(log_id, vec, nr);
-
errno = save_errno;
-
return ret;
-
}
在__write_to_log_daemon中进行一些初始化、过滤、判断等,最终会调用到transport中的write:
-
write_transport_for_each(node, &__android_log_transport_write) {
-
if (node->logMask & i) {
-
ssize_t retval;
-
retval = (*node->write)(log_id, &ts, vec, nr);
-
if (ret >=
0) {
-
ret = retval;
-
}
-
}
-
}
这里的write函数最终调用我们先不看,下面会解释。
这里transport在初始化的时候形成了一个list,详细看__write_to_log_initialize()中调用__android_log_config_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
-
}
-
...
-
...
对于使用LOGD来说,最终使用到的是logdLoggerWrite:
-
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,
-
};
所以对于logd来说__write_to_log_daemon最终调用的write就是这里的logdwrite,具体请查看logd_writer.c源码。
logd 在initrc中
可以看到logd会通过3个socket进行通信、管理。而且指启动一次。
logd.main()
在system/core/lodgd/main.cpp文件的main函数中,创建了LogBuffer、LogReader、LogListener和CommandListener四个对象:
- LogBuffer用于管理log;
- LogReader用于将log传递给logcat;
- LogListener用于监听是否有log写入logd;
- CommandListener用于监听是否有命令发送给logd。
Logd-reinit 进程
Logd-reinit 进程在启动时,会给logd进程发送reinit命令,logd在收到命令后,会重新初始化LogBuffer。
在system/core/lodgd/main.cpp文件的main函数中会创建一个线程用于监测是否有reinit请求
if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
在reinit_thread_start函数中,会重新初始化各个log区的大小,以及其他参数的初始化,但不会重新生成LogBuffer对象。相关代码如下:
-
// Anything that reads persist.<property>
-
if (logBuf) {
-
logBuf->
init();
-
logBuf->
initPrune(
nullptr);
-
}
保存log
- 创建LogBuffer对象,在classLogBuffer类中,定义了一个list容器,保存了指向LogBufferElement对象的指针,创建LogBuffer对象时在其构造函数中会调用LogBuffer::init()函数初始化各log域(如main/system/kernel/crash等)的大小。
- 创建LogListener对象并开始监听
-
LogListener *swl =
newLogListener(logBuf, reader);
-
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
-
if(swl->
startListener(
600)) {
-
exit(
1);
-
}
- 在startListener函数中创建线程,线程注册函数为SocketListener::threadStart;
- 执行runListener函数,如果socket监听到数据,则执行onDataAvailable函数进行处理;
- 调用logbuf->log(LogBuffer::log),这个函数很重要,新建一个LogBufferElement对象(用于保存log),调用mLogElements.insert将LogBufferElement加入list容器,实现log的保存。
读取log
可通过logcat工具获取logd log,logcat 相关代码所在路径:system/core/logcat
通过logcat获取的log,并不完全是按照log分类来打印的,如在KERNEL log中可能存在MAIN log。
logcat实现的大部分函数都在logcat/logcat.cpp文件中,其中__logcat函数是最重要的函数,其负责logcat 输入参数的解析以及log的处理。
logcat 最终读取log通过liblog/logd_reader.c 中的logdRead函数实现。此函数负责打开/dev/logdr,并通过socket获取log。
至于代码中的log级别等信息这里就暂不做解释了,以liblog下log.h为准。