一 架构
1 应用层api:
在应用层,安卓系统封装了日志系统的Java接口,Log.java, Rlog.java, Slog.java EventLog.java。应用开发者比较熟悉Log.java,系统开发者对 Rlog,Slog,EventLog, 接口会比较熟悉。这几个接口作用类似,都是写入日志,差别是写入logd的日志节点不同。
Java 接口封装在android.jar 中,作为SDK提供给开发者使用,在运行时通过libandroid_runtime.so 中的JNI 接口调用系统native api。
对于C/C++ 的开发者,可以直接使用 log.h 中提供的ALOGD 等系列API 在native程序中打印日志。
2 native api
日志系统的核心服务的logd,一个native守护进程。为了访问logd提供的api,安卓系统封装了一层liblog,便于应用层方便访问logd 的socket api
3 核心logd
logd是日志系统的核心,开机时由init进程启动,在系统后台持续运行。logd维护了一个RAM buffer, 作为日志的缓存。各个进程的日志都会写入这个RAM buffer。如果日志写入过多,会删除最老的日志,删除算法类似于ring buffer的逻辑。
logd 对外维护了3个socket api:
-
dev/socket/logd 传输控制指令
-
dev/socket/logw 写日志
-
dev/socket/logr 读日志
4 native bin
在native 层,安卓提供了一个/system/bin/logcat native 命令行程序,作为一个统一的客户端通信进程。开发者通过 adb logcat 相关指令可以读日志或者给logd发送指令。
二 日志流程
详细过程如下:
1 客户端进程通过SDK 中提供的接口写入日志;
Log.d(TAG, “Message body”);
2 java代码通过JNI 调用liblog;
android_util_Log_println_native
3 liblog封装了logd访问的socket接口;liblog通过socket通信,完成客户端日志写入logd;
__android_log_buf_write
write_to_log
__write_to_log_init
__write_to_log_daemon
write_transport_for_each
logdWrite.write
logdWrite
4 logd中维护main / system / crash 等各个log buffer,各个模块的日志缓存在此buffer中。
LogListener::onDataAvailable
LogBuffer::log
=> stats.add(elem);
maybePrune(elem->getLogId());
三 日志总结
1 跨进程通信的消耗:日志信息通过socket 发送到 logd;
2 内存消耗:logd中要维护对应的buffer。RAM 资源的消耗;
3 CPU 资源的消耗:logd中ring buffer会经常进行pruneLogs 操作,删减日志,耗费CPU资源。
4 IO消耗:在某些产品上,log会在后台进程/线程中写入文件,这些更会大量消耗IO,导致应用或者整机卡顿,产生性能问题。