Android高级——Logger日志系统

Logger日志系统

  • Logger日志系统是基于内核中的Logger日志驱动程序实现
  • 将日志记录保存在内核空间中
  • 使用一个环形缓冲区来保存日志,满了之后,新的日志就会覆盖旧的日志

日志类型

  • main,记录应用程序级别
  • system,记录系统级别
  • radio,记录无线设备相关
  • events,用于诊断系统问题,开发人员不应使用

日志驱动程序

4种类型的日志通过下面4个设备文件来访问

  • /dev/log/main
  • /dev/log/system
  • /dev/log/radio
  • /dev/log/events

运行时库

无论什么类型,最终调用write_to_log写入Logger日志驱动程序

C/C++写入日志

  • 宏ALOGV、ALOGD、ALOGI、ALOGW和ALOGE写入main
  • 宏SLOGV、SLOGD、SLOGI、SLOGW和SLOGE写入system
  • 宏RLOGV、RLOGD、RLOGI、RLOGW和RLOGE写入radio
  • 宏LOG_EVENT_INT、LOG_EVENT_LONG、LOG_EVENT_FLOAT和LOG_EVENT_STRING写入events

Java写入日志

  • android.util.Log 写入main
  • android.util.Slog 写入system
  • android.util.Rlog 写入radio
  • android.util.EventLog 写入event

整体架构

在这里插入图片描述

Logger日志格式

main、system和radio

  • priority:优先级,整数,VERBOSE、DEBUG、INFO、WARN、ERROR和FATAL
  • tag:标签,字符串
  • msg:内容,字符串

在这里插入图片描述

events

  • tag:标签,整数
  • msg:内容,二进制数据,由一个或多个值组成,每个值前面都有一个字段描述它的类型

在这里插入图片描述

tag

tag为整数,根据/system/etc/event-log-tags转为字符串

/system/etc/event-log-tags还用来描述events类型的日志内容的格式

  • tag number:标签值,范围为0~2147483648
  • tag name:标签值对应的字符串描述,字母[A-Z][a-z]、数字[0-9]或者下画线“_”组成
  • 第三个字段:日志内容的值

在这里插入图片描述

值格式为

  • name:名称
  • data type:数据类型,int(1)、long(2)、string(3)、list(4)、float(5)
  • data unit:数据单位,范围是1~6,分别表示对象数量(number of objects)、字节数(Number of bytes)、毫秒数(Number of milliseconds)、分配额(Number of allocations)、标志(ID)和百分比(Percent)

在这里插入图片描述

2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1)

如上为/system/etc/event-log-tags的内容

  • tag number:2722
  • tag name:battery_level
  • 由三个值组成,level/voltage/temperature,数据类型为1/1/1,数据单位为6/1/1

msg

msg格式为

  • 类型
  • 值,int(1)、long(2)、string(3)、list(4)、float(5)

在这里插入图片描述

Logger日志驱动程序(能力不够暂时分析不了)

基础数据结构

./system/logging/liblog/include/log/log_read.h(Android13)

logger_entry

logger_entry描述一个日志记录,最大长度为4K,其有效负载长度最大等于4K减去结构体logger_entry的大小

  • len:实际log的有效负载长度
  • hdr_size:logger_entry大小
  • pid/itd:进程pid/tid
  • sec/nsec:写入时间
  • lid:实际log id
  • uid:进程uid
struct logger_entry {uint16_t len;      /* length of the payload */uint16_t hdr_size; /* sizeof(struct logger_entry) */int32_t pid;       /* generating process's pid */uint32_t tid;      /* generating process's tid */uint32_t sec;      /* seconds since Epoch */uint32_t nsec;     /* nanoseconds */uint32_t lid;      /* log id of the payload, bottom 4 bits currently */uint32_t uid;      /* generating process's uid */
};

log_msg

  • 缓冲区5M
  • 包含logger_entry
  • 若为C++,则新增函数返回nsec、lid、msg、len
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)struct log_msg {union {unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];struct logger_entry entry;} __attribute__((aligned(4)));
#ifdef __cplusplusuint64_t nsec() const {return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;}log_id_t id() {return static_cast<log_id_t>(entry.lid);}char* msg() {unsigned short hdr_size = entry.hdr_size;if (hdr_size >= sizeof(struct log_msg) - sizeof(entry)) {return nullptr;}return reinterpret_cast<char*>(buf) + hdr_size;}unsigned int len() { return entry.hdr_size + entry.len; }
#endif
};

main

system/logging/logd/main.cpp,进行初始化操作

  • 默认log时区为utc
  • 缓冲区类型有SerializedLogBuffer(默认)和SimpleLogBuffer,可通过logd.buffer_type属性修改
  • LogReader监听/dev/socket/logdr
  • LogListener监听/dev/socket/logdw
  • CommandListener监听/dev/socket/logd
int main(int argc, char* argv[]) {// We want EPIPE when a reader disconnects, not to terminate logd.signal(SIGPIPE, SIG_IGN);// logd is written under the assumption that the timezone is UTC.// If TZ is not set, persist.sys.timezone is looked up in some time utility// libc functions, including mktime. It confuses the logd time handling,// so here explicitly set TZ to UTC, which overrides the property.setenv("TZ", "UTC", 1);// issue reinit command. KISS argument parsing.if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {return issueReinit();}android::base::InitLogging(argv, [](android::base::LogId log_id, android::base::LogSeverity severity,const char* tag, const char* file, unsigned int line, const char* message) {if (tag && strcmp(tag, "logd") != 0) {auto prefixed_message = android::base::StringPrintf("%s: %s", tag, message);android::base::KernelLogger(log_id, severity, "logd", file, line,prefixed_message.c_str());} else {android::base::KernelLogger(log_id, severity, "logd", file, line, message);}});static const char dev_kmsg[] = "/dev/kmsg";int fdDmesg = android_get_control_file(dev_kmsg);if (fdDmesg < 0) {fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));}int fdPmesg = -1;bool klogd = GetBoolPropertyEngSvelteDefault("ro.logd.kernel");if (klogd) {SetProperty("ro.logd.kernel", "true");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) PLOG(ERROR) << "Failed to open " << proc_kmsg;}bool auditd = GetBoolProperty("ro.logd.auditd", true);DropPrivs(klogd, auditd);// A cache of event log tagsLogTags log_tags;// Pruning configuration.PruneList prune_list;std::string buffer_type = GetProperty("logd.buffer_type", "serialized");LogStatistics log_statistics(GetBoolPropertyEngSvelteDefault("logd.statistics"),buffer_type == "serialized");// Serves the purpose of managing the last logs times read on a socket connection, and as a// reader lock on a range of log entries.LogReaderList reader_list;// LogBuffer is the object which is responsible for holding all log entries.LogBuffer* log_buffer = nullptr;if (buffer_type == "serialized") {log_buffer = new SerializedLogBuffer(&reader_list, &log_tags, &log_statistics);} else if (buffer_type == "simple") {log_buffer = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);} else {LOG(FATAL) << "buffer_type must be one of 'serialized' or 'simple'";}// LogReader listens on /dev/socket/logdr. When a client// connects, log entries in the LogBuffer are written to the client.LogReader* reader = new LogReader(log_buffer, &reader_list);if (reader->startListener()) {return EXIT_FAILURE;}// LogListener listens on /dev/socket/logdw for client// initiated log messages. New log entries are added to LogBuffer// and LogReader is notified to send updates to connected clients.LogListener* swl = new LogListener(log_buffer);if (!swl->StartListener()) {return EXIT_FAILURE;}// Command listener listens on /dev/socket/logd for incoming logd// administrative commands.CommandListener* cl = new CommandListener(log_buffer, &log_tags, &prune_list, &log_statistics);if (cl->startListener()) {return EXIT_FAILURE;}// Notify that others can now interact with logdSetProperty("logd.ready", "true");// LogAudit listens on NETLINK_AUDIT socket for selinux// initiated log messages. New log entries are added to LogBuffer// and LogReader is notified to send updates to connected clients.LogAudit* al = nullptr;if (auditd) {int dmesg_fd = GetBoolProperty("ro.logd.auditd.dmesg", true) ? fdDmesg : -1;al = new LogAudit(log_buffer, dmesg_fd, &log_statistics);}LogKlog* kl = nullptr;if (klogd) {kl = new LogKlog(log_buffer, fdDmesg, fdPmesg, al != nullptr, &log_statistics);}readDmesg(al, kl);// failure is an option ... messages are in dmesg (required by standard)if (kl && kl->startListener()) {delete kl;}if (al && al->startListener()) {delete al;}TrustyLog::create(log_buffer);TEMP_FAILURE_RETRY(pause());return EXIT_SUCCESS;
}

SerializedLogBuffer

  • Log将日志封装成SerializedLogEntry、LogStatisticsElement添加到stats_
SerializedLogBuffer::SerializedLogBuffer(LogReaderList* reader_list, LogTags* tags,LogStatistics* stats): reader_list_(reader_list), tags_(tags), stats_(stats) {Init();
}void SerializedLogBuffer::Init() {log_id_for_each(i) {if (!SetSize(i, GetBufferSizeFromProperties(i))) {SetSize(i, kLogBufferMinSize);}}// Release any sleeping reader threads to dump their current content.auto lock = std::lock_guard{logd_lock};for (const auto& reader_thread : reader_list_->running_reader_threads()) {reader_thread->TriggerReader();}
}int SerializedLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,const char* msg, uint16_t len) {if (log_id >= LOG_ID_MAX || len == 0) {return -EINVAL;}if (len > LOGGER_ENTRY_MAX_PAYLOAD) {len = LOGGER_ENTRY_MAX_PAYLOAD;}if (!ShouldLog(log_id, msg, len)) {stats_->AddTotal(log_id, len);return -EACCES;}auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);auto lock = std::lock_guard{logd_lock};auto entry = LogToLogBuffer(logs_[log_id], max_size_[log_id], sequence, realtime, uid, pid, tid,msg, len);stats_->Add(entry->ToLogStatisticsElement(log_id));MaybePrune(log_id);reader_list_->NotifyNewLog(1 << log_id);return len;
}static SerializedLogEntry* LogToLogBuffer(std::list<SerializedLogChunk>& log_buffer,size_t max_size, uint64_t sequence, log_time realtime,uid_t uid, pid_t pid, pid_t tid, const char* msg,uint16_t len) {if (log_buffer.empty()) {log_buffer.push_back(SerializedLogChunk(max_size / SerializedLogBuffer::kChunkSizeDivisor));}auto total_len = sizeof(SerializedLogEntry) + len;if (!log_buffer.back().CanLog(total_len)) {log_buffer.back().FinishWriting();log_buffer.push_back(SerializedLogChunk(max_size / SerializedLogBuffer::kChunkSizeDivisor));}return log_buffer.back().Log(sequence, realtime, uid, pid, tid, msg, len);
}

LogReader

LogListener

StartListener开启线程LogListener,循环调用HandleData,通过LogBuffer的Log方法写入日志

LogListener::LogListener(LogBuffer* buf) : socket_(GetLogSocket()), logbuf_(buf) {}bool LogListener::StartListener() {if (socket_ <= 0) {return false;}auto thread = std::thread(&LogListener::ThreadFunction, this);thread.detach();return true;
}void LogListener::ThreadFunction() {prctl(PR_SET_NAME, "logd.writer");while (true) {HandleData();}
}void LogListener::HandleData() {// + 1 to ensure null terminator if MAX_PAYLOAD buffer is received__attribute__((uninitialized)) charbuffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];struct iovec iov = {buffer, sizeof(buffer) - 1};alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];struct msghdr hdr = {nullptr, 0, &iov, 1, control, sizeof(control), 0,};ssize_t n = recvmsg(socket_, &hdr, 0);if (n <= (ssize_t)(sizeof(android_log_header_t))) {return;}// To clear the entire buffer would be safe, but this contributes to 1.68%// overhead under logging load. We are safe because we check counts, but// still need to clear null terminatorbuffer[n] = 0;struct ucred* cred = nullptr;struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);while (cmsg != nullptr) {if (cmsg->cmsg_level == SOL_SOCKET &&cmsg->cmsg_type == SCM_CREDENTIALS) {cred = (struct ucred*)CMSG_DATA(cmsg);break;}cmsg = CMSG_NXTHDR(&hdr, cmsg);}if (cred == nullptr) {return;}if (cred->uid == AID_LOGD) {// ignore log messages we send to ourself.// Such log messages are often generated by libraries we depend on// which use standard Android logging.return;}android_log_header_t* header =reinterpret_cast<android_log_header_t*>(buffer);log_id_t logId = static_cast<log_id_t>(header->id);if (/* logId < LOG_ID_MIN || */ logId >= LOG_ID_MAX ||logId == LOG_ID_KERNEL) {return;}if (logId == LOG_ID_SECURITY) {if (!__android_log_security()) {return;}if (!clientCanWriteSecurityLog(cred->uid, cred->gid, cred->pid)) {return;}}char* msg = ((char*)buffer) + sizeof(android_log_header_t);n -= sizeof(android_log_header_t);// NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a// truncated message to the logs.logbuf_->Log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
}int LogListener::GetLogSocket() {static const char socketName[] = "logdw";int sock = android_get_control_socket(socketName);if (sock < 0) {  // logd started up in init.shsock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);int on = 1;if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {return -1;}}return sock;
}

CommandListener

运行时库

write_to_log

system/logging/liblog/logger_write.cpp

调用LogdWrite

#ifdef __ANDROID__
static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {int ret;struct timespec ts;if (log_id == LOG_ID_KERNEL) {return -EINVAL;}clock_gettime(CLOCK_REALTIME, &ts);if (log_id == LOG_ID_SECURITY) {if (vec[0].iov_len < 4) {return -EINVAL;}ret = check_log_uid_permissions();if (ret < 0) {return ret;}if (!__android_log_security()) {/* If only we could reset downstream logd counter */return -EPERM;}} else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {if (vec[0].iov_len < 4) {return -EINVAL;}}ret = LogdWrite(log_id, &ts, vec, nr);PmsgWrite(log_id, &ts, vec, nr);return ret;
}
#else
static int write_to_log(log_id_t, struct iovec*, size_t) {// Non-Android text logs should go to __android_log_stderr_logger, not here.// Non-Android binary logs are always dropped.return 1;
}
#endif

LogdWrite

system/logging/liblog/logd_writer.cpp

  • 若logId == LOG_ID_SECURITY,获取LogdSocket::BlockingSocket(),否则获取LogdSocket::NonBlockingSocket()
  • 调用sock打开设备/dev/socket/logdw,通过writev写入struct iovec
  • 返回值小于0且错误码不等于EAGAIN需要重新连接再次写入
int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {ssize_t ret;static const unsigned headerLength = 1;struct iovec newVec[nr + headerLength];android_log_header_t header;size_t i, payloadSize;static atomic_int dropped;LogdSocket& logd_socket =logId == LOG_ID_SECURITY ? LogdSocket::BlockingSocket() : LogdSocket::NonBlockingSocket();if (logd_socket.sock() < 0) {return -EBADF;}/* logd, after initialization and priv drop */if (getuid() == AID_LOGD) {/** ignore log messages we send to ourself (logd).* Such log messages are often generated by libraries we depend on* which use standard Android logging.*/return 0;}header.tid = gettid();header.realtime.tv_sec = ts->tv_sec;header.realtime.tv_nsec = ts->tv_nsec;newVec[0].iov_base = (unsigned char*)&header;newVec[0].iov_len = sizeof(header);int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),ANDROID_LOG_VERBOSE)) {android_log_event_int_t buffer;header.id = LOG_ID_EVENTS;buffer.header.tag = LIBLOG_LOG_TAG;buffer.payload.type = EVENT_TYPE_INT;buffer.payload.data = snapshot;newVec[headerLength].iov_base = &buffer;newVec[headerLength].iov_len = sizeof(buffer);ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, 2));if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);}}header.id = logId;for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {newVec[i].iov_base = vec[i - headerLength].iov_base;payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;if (newVec[i].iov_len) {++i;}break;}}// EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with// the connection, so we reset it and try again.ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));if (ret < 0 && errno != EAGAIN) {logd_socket.Reconnect();ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));}if (ret < 0) {ret = -errno;}if (ret > (ssize_t)sizeof(header)) {ret -= sizeof(header);} else if (ret < 0) {atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);}return ret;
}

LogdSocket

sock、GetSocket、LogdConnect打开设备/dev/socket/logdw

class LogdSocket {public:static LogdSocket& BlockingSocket() {static LogdSocket logd_socket(true);return logd_socket;}static LogdSocket& NonBlockingSocket() {static LogdSocket logd_socket(false);return logd_socket;}void Reconnect() { LogdConnect(sock_); }// Zygote uses this to clean up open FD's after fork() and before specialization.  It is single// threaded at this point and therefore this function is explicitly not thread safe.  It sets// sock_ to kUninitialized, so future logs will be safely initialized whenever they happen.void Close() {if (sock_ != kUninitialized) {close(sock_);}sock_ = kUninitialized;}int sock() {GetSocket();return sock_;}private:LogdSocket(bool blocking) : blocking_(blocking) {}// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this// function is used to reconnect to logd without requiring a new socket.static void LogdConnect(int sock) {sockaddr_un un = {};un.sun_family = AF_UNIX;strcpy(un.sun_path, "/dev/socket/logdw");TEMP_FAILURE_RETRY(connect(sock, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));}// sock_ should only be opened once.  If we see that sock_ is uninitialized, we// create a new socket and attempt to exchange it into the atomic sock_.  If the// compare/exchange was successful, then that will be the socket used for the duration of the// program, otherwise a different thread has already opened and written the socket to the atomic,// so close the new socket and return.void GetSocket() {if (sock_ != kUninitialized) {return;}int flags = SOCK_DGRAM | SOCK_CLOEXEC;if (!blocking_) {flags |= SOCK_NONBLOCK;}int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));if (new_socket < 0) {return;}LogdConnect(new_socket);int uninitialized_value = kUninitialized;if (!sock_.compare_exchange_strong(uninitialized_value, new_socket)) {close(new_socket);return;}}static const int kUninitialized = -1;atomic_int sock_ = kUninitialized;bool blocking_;
};

__android_log_print

system/logging/liblog/logger_write.cpp

int __android_log_print(int prio, const char* tag, const char* fmt, ...) {ErrnoRestorer errno_restorer;if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {return -EPERM;}va_list ap;__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];va_start(ap, fmt);vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);va_end(ap);__android_log_message log_message = {sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};__android_log_write_log_message(&log_message);return 1;
}

__android_log_write_log_message

void __android_log_write_log_message(__android_log_message* log_message) {ErrnoRestorer errno_restorer;if (log_message->buffer_id != LOG_ID_DEFAULT && log_message->buffer_id != LOG_ID_MAIN &&log_message->buffer_id != LOG_ID_SYSTEM && log_message->buffer_id != LOG_ID_RADIO &&log_message->buffer_id != LOG_ID_CRASH) {return;}if (log_message->tag == nullptr) {log_message->tag = GetDefaultTag().c_str();}#if __BIONIC__if (log_message->priority == ANDROID_LOG_FATAL) {android_set_abort_message(log_message->message);}
#endifget_logger_function()(log_message);
}

调用get_logger_function,get_file_logger_path判断是否有定义ro.log.file_logger.path指定log文件路径,如果没有则调用__android_log_logd_logger

static __android_logger_function get_logger_function() {if (user_set_logger_function != nullptr) {return user_set_logger_function;}static __android_logger_function default_logger_function = []() {
#if __ANDROID__if (get_file_logger_path() != nullptr) {return file_logger;} else {return __android_log_logd_logger;}
#elsereturn file_logger;
#endif}();return default_logger_function;
}#ifdef __ANDROID__
static const char* get_file_logger_path() {static const char* file_logger_path = []() {static char path[PROP_VALUE_MAX] = {};if (__system_property_get("ro.log.file_logger.path", path) > 0) {return path;}return (char*)nullptr;  // means file_logger should not be used}();return file_logger_path;
}
#endif

__android_log_logd_logger将优先级、标签、内容存在数字元素vec[0]、vec[1]和vec[2],最后调用write_to_log

+1是因为标签和内容后面跟着’\0’,用来区分和解析

void __android_log_logd_logger(const struct __android_log_message* log_message) {int buffer_id = log_message->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : log_message->buffer_id;struct iovec vec[3];vec[0].iov_base =const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&log_message->priority));vec[0].iov_len = 1;vec[1].iov_base = const_cast<void*>(static_cast<const void*>(log_message->tag));vec[1].iov_len = strlen(log_message->tag) + 1;vec[2].iov_base = const_cast<void*>(static_cast<const void*>(log_message->message));vec[2].iov_len = strlen(log_message->message) + 1;write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
}

__android_log_buf_print

也是调用__android_log_write_log_message,同上

int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {ErrnoRestorer errno_restorer;if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {return -EPERM;}va_list ap;__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];va_start(ap, fmt);vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);va_end(ap);__android_log_message log_message = {sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, buf};__android_log_write_log_message(&log_message);return 1;
}

__android_log_bwrite/__android_log_btwrite/__android_log_bswrite

  • __android_log_bwrite 的内容可由多个值组成
  • __android_log_btwrite 的内容只有一个值,类型为参数type
  • __android_log_btwrite 的内容为字符串
int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {ErrnoRestorer errno_restorer;struct iovec vec[2];vec[0].iov_base = &tag;vec[0].iov_len = sizeof(tag);vec[1].iov_base = (void*)payload;vec[1].iov_len = len;return write_to_log(LOG_ID_EVENTS, vec, 2);
}int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {ErrnoRestorer errno_restorer;struct iovec vec[3];vec[0].iov_base = &tag;vec[0].iov_len = sizeof(tag);vec[1].iov_base = &type;vec[1].iov_len = sizeof(type);vec[2].iov_base = (void*)payload;vec[2].iov_len = len;return write_to_log(LOG_ID_EVENTS, vec, 3);
}int __android_log_bswrite(int32_t tag, const char* payload) {ErrnoRestorer errno_restorer;struct iovec vec[4];char type = EVENT_TYPE_STRING;uint32_t len = strlen(payload);vec[0].iov_base = &tag;vec[0].iov_len = sizeof(tag);vec[1].iov_base = &type;vec[1].iov_len = sizeof(type);vec[2].iov_base = &len;vec[2].iov_len = sizeof(len);vec[3].iov_base = (void*)payload;vec[3].iov_len = len;return write_to_log(LOG_ID_EVENTS, vec, 4);
}

# C/C++写入日志

system/logging/liblog/include/log/log.h(Android13)

属性LOG_NDEBUG限制Log的输出(为0时相关函数定义为空)

#ifndef LOG_NDEBUG
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
#define LOG_NDEBUG 0
#endif
#endif

属性LOG_TAG定义了当前编译单元的日志TAG,默认为空

#ifndef LOG_TAG
#define LOG_TAG NULL
#endif

ALOGV 、ALOGD 、ALOGI 、ALOGW 和ALOGE

/system/logging/liblog/include/log/log_main.h,ALOGV只有当LOG_NDEBUG为0时才有效

#ifndef ALOGV
#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#if LOG_NDEBUG
#define ALOGV(...)                   \do {                               \__FAKE_USE_VA_ARGS(__VA_ARGS__); \if (false) {                     \__ALOGV(__VA_ARGS__);          \}                                \} while (false)
#else
#define ALOGV(...) __ALOGV(__VA_ARGS__)
#endif
#endif#ifndef ALOGD
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif#ifndef ALOGI
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif#ifndef ALOGW
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif#ifndef ALOGE
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif

调用ALOG、LOG_PRI、android_printLog,最后调用运行时库的__android_log_print

#ifndef ALOG
#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif#ifndef LOG_PRI
#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__)
#endif#define android_printLog(prio, tag, ...) \__android_log_print(prio, tag, __VA_ARGS__)

RLOGV、RLOGD、RLOGI、RLOGW和RLOGE

/system/logging/liblog/include/log/log_radio.h

同理,RLOGV只有在LOG_NDEBUG为0才有效,最后调用运行时库的__android_log_buf_print,传入LOG_ID_RADIO

#ifndef RLOGV
#define __RLOGV(...)                                                         \((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, \__VA_ARGS__))
#if LOG_NDEBUG
#define RLOGV(...)          \do {                      \if (0) {                \__RLOGV(__VA_ARGS__); \}                       \} while (0)
#else
#define RLOGV(...) __RLOGV(__VA_ARGS__)
#endif
#endif#ifndef RLOGD
#define RLOGD(...)                                                         \((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, \__VA_ARGS__))
#endif#ifndef RLOGI
#define RLOGI(...)                                                        \((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, \__VA_ARGS__))
#endif#ifndef RLOGW
#define RLOGW(...)                                                        \((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, \__VA_ARGS__))
#endif#ifndef RLOGE
#define RLOGE(...)                                                         \((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, \__VA_ARGS__))
#endif

SLOGV、SLOGD、SLOGI、SLOGW和SLOGE

/system/logging/liblog/include/log/log_system.h

同理,SLOGV只有在LOG_NDEBUG为0才有效,最后调用运行时库的__android_log_buf_print,传入LOG_ID_SYSTEM

#ifndef SLOGV
#define __SLOGV(...)                                                          \((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, \__VA_ARGS__))
#if LOG_NDEBUG
#define SLOGV(...)          \do {                      \if (0) {                \__SLOGV(__VA_ARGS__); \}                       \} while (0)
#else
#define SLOGV(...) __SLOGV(__VA_ARGS__)
#endif
#endif#ifndef SLOGD
#define SLOGD(...)                                                          \((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, \__VA_ARGS__))
#endif#ifndef SLOGI
#define SLOGI(...)                                                         \((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, \__VA_ARGS__))
#endif#ifndef SLOGW
#define SLOGW(...)                                                         \((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, \__VA_ARGS__))
#endif#ifndef SLOGE
#define SLOGE(...)                                                          \((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, \__VA_ARGS__))
#endif

LOG_EVENT_INT、LOG_EVENT_LONG、LOG_EVENT_FLOAT和LOG_EVENT_STRING

system/logging/liblog/include/log/log.h

  • LOG_EVENT_INT、LOG_EVENT_LONG和LOG_EVENT_LONG调用android_btWriteLog,最后调用运行时库的__android_log_btwrite
  • LOG_EVENT_STRING调用运行时库的__android_log_bswrite
#define android_btWriteLog(tag, type, payload, len) \__android_log_btwrite(tag, type, payload, len)typedef enum {/* Special markers for android_log_list_element type */EVENT_TYPE_LIST_STOP = '\n', /* declare end of list  */EVENT_TYPE_UNKNOWN = '?',    /* protocol error       *//* must match with declaration in java/android/android/util/EventLog.java */EVENT_TYPE_INT = 0,  /* int32_t */EVENT_TYPE_LONG = 1, /* int64_t */EVENT_TYPE_STRING = 2,EVENT_TYPE_LIST = 3,EVENT_TYPE_FLOAT = 4,
} AndroidEventLogType;#ifndef LOG_EVENT_INT
#define LOG_EVENT_INT(_tag, _value)                                          \{                                                                          \int intBuf = _value;                                                     \(void)android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)); \}
#endif#ifndef LOG_EVENT_LONG
#define LOG_EVENT_LONG(_tag, _value)                                            \{                                                                             \long long longBuf = _value;                                                 \(void)android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)); \}
#endif#ifndef LOG_EVENT_FLOAT
#define LOG_EVENT_FLOAT(_tag, _value)                           \{                                                             \float floatBuf = _value;                                    \(void)android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \sizeof(floatBuf));                 \}
#endif#ifndef LOG_EVENT_STRING
#define LOG_EVENT_STRING(_tag, _value) \(void)__android_log_bswrite(_tag, _value);
#endif

Java写入日志

android.util.Log

frameworks/base/core/java/android/util/Log.java

public final class Log {....../*** Priority constant for the println method; use Log.v.*/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;......public static int v(@Nullable String tag, @NonNull String msg) {return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);}public static int d(@Nullable String tag, @NonNull String msg) {return println_native(LOG_ID_MAIN, DEBUG, tag, msg);}public static int i(@Nullable String tag, @NonNull String msg) {return println_native(LOG_ID_MAIN, INFO, tag, msg);}public static int w(@Nullable String tag, @NonNull String msg) {return println_native(LOG_ID_MAIN, WARN, tag, msg);}public static int e(@Nullable String tag, @NonNull String msg) {return println_native(LOG_ID_MAIN, ERROR, tag, msg);}....../** @hide */ public static final int LOG_ID_MAIN = 0;/** @hide */ public static final int LOG_ID_RADIO = 1;/** @hide */ public static final int LOG_ID_EVENTS = 2;/** @hide */ public static final int LOG_ID_SYSTEM = 3;/** @hide */ public static final int LOG_ID_CRASH = 4;	......
}

调用println_native,传入LOG_ID_MAIN

println_native

根据frameworks/base/core/jni/android_util_Log.cpp

static const JNINativeMethod gMethods[] = {......{ "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },......
};

可知对应的调用函数,判断日志内容msgObj 是否为空,判断类型是否在[0, LOG_ID_MAX],最后调用运行时库的__android_log_buf_write写入log

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);return res;
}

android.util.Slog

frameworks/base/core/java/android/util/Slog.java

只可在系统内部使用,同上调用Log中的println_native传入LOG_ID_SYSTEM

public final class Slog {......@UnsupportedAppUsagepublic static int v(@Nullable String tag, @NonNull String msg) {return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg);}@UnsupportedAppUsagepublic static int d(@Nullable String tag, @NonNull String msg) {return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);}@UnsupportedAppUsagepublic static int i(@Nullable String tag, @NonNull String msg) {return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);}@UnsupportedAppUsagepublic static int w(@Nullable String tag, @NonNull String msg) {return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);}@UnsupportedAppUsagepublic static int e(@Nullable String tag, @NonNull String msg) {return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);}
}

android.util.Rlog

frameworks/base/core/java/android/util/Rlog.java

只可在系统内部使用,同上调用Log中的println_native传入LOG_ID_RADIO

public final class Rlog {.....@UnsupportedAppUsagepublic static int v(String tag, String msg) {return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg);}@UnsupportedAppUsagepublic static int d(String tag, String msg) {return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag, msg);}@UnsupportedAppUsagepublic static int i(String tag, String msg) {return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag, msg);}@UnsupportedAppUsagepublic static int w(String tag, String msg) {return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag, msg);}@UnsupportedAppUsagepublic static int e(String tag, String msg) {return Log.println_native(Log.LOG_ID_RADIO, Log.ERROR, tag, msg);}public static int println(int priority, String tag, String msg) {return Log.println_native(Log.LOG_ID_RADIO, priority, tag, msg);}
}

android.util.EventLog

frameworks/base/core/java/android/util/EventLog.java,重载了5个版本的writeEvent方法,日志内容分别为int、long、float、string、list

public class EventLog {......private static final byte INT_TYPE    = 0;private static final byte LONG_TYPE   = 1;private static final byte STRING_TYPE = 2;private static final byte LIST_TYPE   = 3;private static final byte FLOAT_TYPE = 4;public static native int writeEvent(int tag, int value);public static native int writeEvent(int tag, long value);public static native int writeEvent(int tag, float value);public static native int writeEvent(int tag, String str);public static native int writeEvent(int tag, Object... list);......
}

writeEvent

根据frameworks/base/core/jni/android_util_EventLog.cpp

static const JNINativeMethod gRegisterMethods[] = {/* name, signature, funcPtr */{ "writeEvent", "(II)I", (void*) ELog::writeEventInteger },{ "writeEvent", "(IJ)I", (void*) ELog::writeEventLong },{ "writeEvent", "(IF)I", (void*) ELog::writeEventFloat },{ "writeEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString },{ "writeEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray },......
};

可知调用

static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,jint tag, jint value) {android_log_event_list ctx(tag);ctx << (int32_t)value;return ctx.write(LogID);
}static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,jint tag, jlong value) {android_log_event_list ctx(tag);ctx << (int64_t)value;return ctx.write(LogID);
}static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,jint tag, jfloat value) {android_log_event_list ctx(tag);ctx << (float)value;return ctx.write(LogID);
}static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,jstring value) {android_log_event_list ctx(tag);// Don't throw NPE -- I feel like it's sort of mean for a logging function// to be all crashy if you pass in NULL -- but make the NULL value explicit.ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL");return ctx.write(LogID);
}static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,jobjectArray value) {android_log_event_list ctx(tag);if (value == nullptr) {ctx << "[NULL]";return ctx.write(LogID);}jsize copied = 0, num = env->GetArrayLength(value);for (; copied < num && copied < 255; ++copied) {if (ctx.status()) break;ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied));if (item == nullptr) {ctx << "NULL";} else if (env->IsInstanceOf(item.get(), gStringClass)) {ctx << ScopedUtfChars(env, (jstring) item.get()).c_str();} else if (env->IsInstanceOf(item.get(), gIntegerClass)) {ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID);} else if (env->IsInstanceOf(item.get(), gLongClass)) {ctx << (int64_t)env->GetLongField(item.get(), gLongValueID);} else if (env->IsInstanceOf(item.get(), gFloatClass)) {ctx << (float)env->GetFloatField(item.get(), gFloatValueID);} else {jniThrowException(env,"java/lang/IllegalArgumentException","Invalid payload item type");return -1;}}return ctx.write(LogID);
}

log_event_list

system/logging/liblog/include/log/log_event_list.h

int android_log_write_list(android_log_context ctx, log_id_t id);class android_log_event_list {......int write(log_id_t id = LOG_ID_EVENTS) {/* facilitate -EBUSY retry */if ((ret == -EBUSY) || (ret > 0)) ret = 0;int retval = android_log_write_list(ctx, id);/* existing errors trump transmission errors */if (!ret) ret = retval;return ret;}......
}

system/logging/liblog/log_event_list.cpp,根据id == LOG_ID_EVENTS调用__android_log_bwrite

int android_log_write_list(android_log_context context, log_id_t id) {const char* msg;ssize_t len;if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {return -EINVAL;}if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {return -EBADF;}if (context->list_nest_depth) {return -EIO;}/* NB: if there was overflow, then log is truncated. Nothing reported */context->storage[1] = context->count[0];len = context->len = context->pos;msg = (const char*)context->storage;/* it's not a list */if (context->count[0] <= 1) {len -= sizeof(uint8_t) + sizeof(uint8_t);if (len < 0) {len = 0;}msg += sizeof(uint8_t) + sizeof(uint8_t);}return (id == LOG_ID_EVENTS)? __android_log_bwrite(context->tag, msg, len): ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len): __android_log_security_bwrite(context->tag, msg, len));
}

Logcat工具分析

基础数据结构

logcat

system/logging/logcat/logcat.cpp

int main(int argc, char** argv) {Logcat logcat;return logcat.Run(argc, argv);
}

下面来分析其Run方法

命令参数

int Logcat::Run(int argc, char** argv) {......while (true) {......switch (c) {......case 'd':mode |= ANDROID_LOG_NONBLOCK;break;case 't':got_t = true;mode |= ANDROID_LOG_NONBLOCK;FALLTHROUGH_INTENDED;case 'T':if (strspn(optarg, "0123456789") != strlen(optarg)) {char* cp = parseTime(tail_time, optarg);if (!cp) {error(EXIT_FAILURE, 0, "-%c '%s' not in time format.", c, optarg);}if (*cp) {char ch = *cp;*cp = '\0';fprintf(stderr, "WARNING: -%c '%s' '%c%s' time truncated\n", c, optarg, ch,cp + 1);*cp = ch;}} else {if (!ParseUint(optarg, &tail_lines) || tail_lines < 1) {fprintf(stderr, "WARNING: -%c %s invalid, setting to 1\n", c, optarg);tail_lines = 1;}}break;case 'D':print_dividers_ = true;break;case 'e':regex_.reset(new std::regex(optarg));break;case 'm': {if (!ParseUint(optarg, &max_count_) || max_count_ < 1) {error(EXIT_FAILURE, 0, "-%c '%s' isn't an integer greater than zero.", c,optarg);}} break;case 'g':if (!optarg) {getLogSize = true;break;}FALLTHROUGH_INTENDED;case 'G': {if (!ParseByteCount(optarg, &setLogSize) || setLogSize < 1) {error(EXIT_FAILURE, 0, "-G must be specified as <num><multiplier>.");}} break;case 'p':if (!optarg) {getPruneList = true;break;}FALLTHROUGH_INTENDED;case 'P':setPruneList = optarg;break;case 'b':for (const auto& buffer : Split(optarg, delimiters)) {if (buffer == "default") {id_mask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH);} else if (buffer == "all") {id_mask = -1;} else {log_id_t log_id = android_name_to_log_id(buffer.c_str());if (log_id >= LOG_ID_MAX) {error(EXIT_FAILURE, 0, "Unknown buffer '%s' listed for -b.",buffer.c_str());}if (log_id == LOG_ID_SECURITY) {security_buffer_selected = true;}id_mask |= (1 << log_id);}}break;case 'B':print_binary_ = 1;break;case 'f':if ((tail_time == log_time::EPOCH) && !tail_lines) {tail_time = lastLogTime(optarg);}// redirect output to a fileoutput_file_name_ = optarg;break;case 'r':if (!ParseUint(optarg, &log_rotate_size_kb_) || log_rotate_size_kb_ < 1) {error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -r.", optarg);}break;case 'n':if (!ParseUint(optarg, &max_rotated_logs_) || max_rotated_logs_ < 1) {error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -n.", optarg);}break;case 'v':for (const auto& arg : Split(optarg, delimiters)) {int err = SetLogFormat(arg.c_str());if (err < 0) {error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -v.", arg.c_str());}if (err) hasSetLogFormat = true;}break;case 'S':printStatistics = true;break;case ':':error(EXIT_FAILURE, 0, "Option '%s' needs an argument.", argv[optind - 1]);break;case 'h':show_help();return EXIT_SUCCESS;case '?':error(EXIT_FAILURE, 0, "Unknown option '%s'.", argv[optind]);break;default:error(EXIT_FAILURE, 0, "Unknown getopt_long() result '%c'.", c);}}
  • d 把 mode设为ANDROID_LOG_NONBLOCK,表示没有日志记录可读时logcat直接退出
  • t 将got_t 设为true,表示只输出最新的日志
  • b 将参数分割出来,通过id_mask设置读取的设备
  • B print_binary_ = 1,表示以二进制输出日志
  • f 指定输出文件output_file_name_
  • r 指定输出文件的大小log_rotate_size_kb_(默认0无限制)
  • n 指定输出文件的个数max_rotated_logs_(默认4),若输出日志时,已超过-r指定大小,则建立新的日志文件,格式为xxx.1/xxx.2/xxx.n
  • v 调用SetLogFormat设置日志输出格式,将参数转为AndroidLogPrintFormat并设置到p_format->format
int Logcat::SetLogFormat(const char* format_string) {AndroidLogPrintFormat format = android_log_formatFromString(format_string);// invalid string?if (format == FORMAT_OFF) return -1;return android_log_setPrintFormat(logformat_.get(), format);
}

system/logging/liblog/logprint.cpp

AndroidLogPrintFormat android_log_formatFromString(const char* formatString) {/* clang-format off */if (!strcmp(formatString, "brief")) return FORMAT_BRIEF;if (!strcmp(formatString, "process")) return FORMAT_PROCESS;if (!strcmp(formatString, "tag")) return FORMAT_TAG;if (!strcmp(formatString, "thread")) return FORMAT_THREAD;if (!strcmp(formatString, "raw")) return FORMAT_RAW;if (!strcmp(formatString, "time")) return FORMAT_TIME;if (!strcmp(formatString, "threadtime")) return FORMAT_THREADTIME;if (!strcmp(formatString, "long")) return FORMAT_LONG;if (!strcmp(formatString, "color")) return FORMAT_MODIFIER_COLOR;if (!strcmp(formatString, "colour")) return FORMAT_MODIFIER_COLOR;if (!strcmp(formatString, "usec")) return FORMAT_MODIFIER_TIME_USEC;if (!strcmp(formatString, "nsec")) return FORMAT_MODIFIER_TIME_NSEC;if (!strcmp(formatString, "printable")) return FORMAT_MODIFIER_PRINTABLE;if (!strcmp(formatString, "year")) return FORMAT_MODIFIER_YEAR;if (!strcmp(formatString, "zone")) return FORMAT_MODIFIER_ZONE;if (!strcmp(formatString, "epoch")) return FORMAT_MODIFIER_EPOCH;if (!strcmp(formatString, "monotonic")) return FORMAT_MODIFIER_MONOTONIC;if (!strcmp(formatString, "uid")) return FORMAT_MODIFIER_UID;if (!strcmp(formatString, "descriptive")) return FORMAT_MODIFIER_DESCRIPT;/* clang-format on */#if !defined(__MINGW32__)// Check whether the format string is actually a time zone. If tzname[0]// is the empty string, that's tzset() signalling that it doesn't know// the requested timezone.TzSetter tz(formatString);if (!*tzname[0]) {tz.Reset();} else {// We keep the new time zone as a side effect!return FORMAT_MODIFIER_ZONE;}
#endifreturn FORMAT_OFF;
}int android_log_setPrintFormat(AndroidLogFormat* p_format, AndroidLogPrintFormat format) {switch (format) {case FORMAT_MODIFIER_COLOR:p_format->colored_output = true;return 0;case FORMAT_MODIFIER_TIME_USEC:p_format->usec_time_output = true;return 0;case FORMAT_MODIFIER_TIME_NSEC:p_format->nsec_time_output = true;return 0;case FORMAT_MODIFIER_PRINTABLE:p_format->printable_output = true;return 0;case FORMAT_MODIFIER_YEAR:p_format->year_output = true;return 0;case FORMAT_MODIFIER_ZONE:p_format->zone_output = !p_format->zone_output;return 0;case FORMAT_MODIFIER_EPOCH:p_format->epoch_output = true;return 0;case FORMAT_MODIFIER_MONOTONIC:p_format->monotonic_output = true;return 0;case FORMAT_MODIFIER_UID:p_format->uid_output = true;return 0;case FORMAT_MODIFIER_DESCRIPT:p_format->descriptive_output = true;descriptive_output = true;return 0;default:break;}p_format->format = format;return 1;
}

日志的格式为<PREFIX>+MESSAGE+<SUFFIX>,不同格式的<PREFIX>和<SUFFIX>不同

  • FORMAT_BRIEF:“<priority>/<tag>(<pid>):”和“\n”。

  • FORMAT_PROCESS:“<priority>(<pid>)”和“(<t a g>)\n”。

  • FORMAT_TAG:“<priority>/(<tag>):”和“\n”。

  • FORMAT_THREAD:“<priority>(<pid>:<tid>)”和“\n”。

  • FORMAT_RAW:空值和“\n”。

  • FORMAT_TIME:“<sec>.<nsec> <priority>/<tag>(<pid>):”和“\n”。

  • FORMAT_THREADTIME:“<sec>.<nsec><pid><tid><priority><tag>:”和“\n”。

  • FORMAT_LONG:“[<sec>.<nsec> <pid>:<tid><priority>/<tag>]”和“\n\n”

其他

上面解析完参数,继续往后走,未指定选项b时,默认输出MAIN、system、crash、kernel的log

    // If no buffers are specified, default to using these buffers.if (id_mask == 0) {id_mask = (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH) |(1 << LOG_ID_KERNEL);}

未设置选项v时,将环境变量ANDROID_PRINTF_LOG的值设置为当前格式,若无则设置为threadtime

    if (!hasSetLogFormat) {const char* logFormat = getenv("ANDROID_PRINTF_LOG");if (!!logFormat) {for (const auto& arg : Split(logFormat, delimiters)) {int err = SetLogFormat(arg.c_str());// environment should not cause crash of logcatif (err < 0) {fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg.c_str());}if (err > 0) hasSetLogFormat = true;}}if (!hasSetLogFormat) {SetLogFormat("threadtime");}}
  • forceFilters.size()不为0,表示通过选项Q让logcat读取/proc/cmdline中的过滤器
  • argc == optind表示命令没有其他参数,读取ANDROID_LOG_TAGS的值作为过滤器
  • 将命令参数设置为过滤器,格式为 tag:priority,如 *:E
    if (forceFilters.size()) {int err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());if (err < 0) {error(EXIT_FAILURE, 0, "Invalid filter expression in logcat args.");}} else if (argc == optind) {// Add from environment variableconst char* env_tags_orig = getenv("ANDROID_LOG_TAGS");if (!!env_tags_orig) {int err = android_log_addFilterString(logformat_.get(), env_tags_orig);if (err < 0) {error(EXIT_FAILURE, 0, "Invalid filter expression in ANDROID_LOG_TAGS.");}}} else {// Add from commandlinefor (int i = optind ; i < argc ; i++) {int err = android_log_addFilterString(logformat_.get(), argv[i]);if (err < 0) {error(EXIT_FAILURE, 0, "Invalid filter expression '%s'.", argv[i]);}}}

通过android_log_addFilterString设置过滤器,filterString可能以空格、tab或逗号分割

int android_log_addFilterString(AndroidLogFormat* p_format, const char* filterString) {char* filterStringCopy = strdup(filterString);char* p_cur = filterStringCopy;char* p_ret;int err;/* Yes, I'm using strsep */while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {/* ignore whitespace-only entries */if (p_ret[0] != '\0') {err = android_log_addFilterRule(p_format, p_ret);if (err < 0) {goto error;}}}free(filterStringCopy);return 0;
error:free(filterStringCopy);return -1;
}

通过android_log_addFilterRule添加解析过滤器,将冒号后面的字符转为android_LogPriority,通过tagName和pri创建FilterInfo

int android_log_addFilterRule(AndroidLogFormat* p_format, const char* filterExpression) {size_t tagNameLength;android_LogPriority pri = ANDROID_LOG_DEFAULT;tagNameLength = strcspn(filterExpression, ":");if (tagNameLength == 0) {goto error;}if (filterExpression[tagNameLength] == ':') {pri = filterCharToPri(filterExpression[tagNameLength + 1]);if (pri == ANDROID_LOG_UNKNOWN) {goto error;}}if (0 == strncmp("*", filterExpression, tagNameLength)) {/** This filter expression refers to the global filter* The default level for this is DEBUG if the priority* is unspecified*/if (pri == ANDROID_LOG_DEFAULT) {pri = ANDROID_LOG_DEBUG;}p_format->global_pri = pri;} else {/** for filter expressions that don't refer to the global* filter, the default is verbose if the priority is unspecified*/if (pri == ANDROID_LOG_DEFAULT) {pri = ANDROID_LOG_VERBOSE;}char* tagName;/** Presently HAVE_STRNDUP is never defined, so the second case is always taken* Darwin doesn't have strndup, everything else does*/
#ifdef HAVE_STRNDUPtagName = strndup(filterExpression, tagNameLength);
#else/* a few extra bytes copied... */tagName = strdup(filterExpression);tagName[tagNameLength] = '\0';
#endif /*HAVE_STRNDUP*/FilterInfo* p_fi = filterinfo_new(tagName, pri);free(tagName);p_fi->p_next = p_format->filters;p_format->filters = p_fi;}return 0;
error:return -1;
}static android_LogPriority filterCharToPri(char c) {android_LogPriority pri;c = tolower(c);if (c >= '0' && c <= '9') {if (c >= ('0' + ANDROID_LOG_SILENT)) {pri = ANDROID_LOG_VERBOSE;} else {pri = (android_LogPriority)(c - '0');}} else if (c == 'v') {pri = ANDROID_LOG_VERBOSE;} else if (c == 'd') {pri = ANDROID_LOG_DEBUG;} else if (c == 'i') {pri = ANDROID_LOG_INFO;} else if (c == 'w') {pri = ANDROID_LOG_WARN;} else if (c == 'e') {pri = ANDROID_LOG_ERROR;} else if (c == 'f') {pri = ANDROID_LOG_FATAL;} else if (c == 's') {pri = ANDROID_LOG_SILENT;} else if (c == '*') {pri = ANDROID_LOG_DEFAULT;} else {pri = ANDROID_LOG_UNKNOWN;}return pri;
}

若通过选项f指定输出文件,通过max_rotated_logs_设置个数,格式为xxx.1/…/xxx.n

    if (output_file_name_) {if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {error(EXIT_FAILURE, 0, "-f is incompatible with -g/-G, -S, and -p/-P.");}if (clearLog || setId) {int max_rotation_count_digits =max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;for (int i = max_rotated_logs_; i >= 0; --i) {std::string file;if (!i) {file = output_file_name_;} else {file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);}int err = unlink(file.c_str());if (err < 0 && errno != ENOENT) {fprintf(stderr, "failed to delete log file '%s': %s\n", file.c_str(),strerror(errno));}}}if (clearLog) {return EXIT_SUCCESS;}}

根据id打开log设备

    for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {if (!(id_mask & (1 << i))) continue;const char* buffer_name = android_log_id_to_name(static_cast<log_id_t>(i));auto logger = android_logger_open(logger_list.get(), static_cast<log_id_t>(i));if (logger == nullptr) {ReportErrorName(buffer_name, security_buffer_selected, &open_device_failures);continue;}......

判断输出是二进制调用WriteFully,否则调用ProcessBuffer

    while (!max_count_ || print_count_ < max_count_) {......if (print_binary_) {WriteFully(&log_msg, log_msg.len());} else {ProcessBuffer(&log_msg);}if (blocking && output_file_ == stdout) fflush(stdout);}return EXIT_SUCCESS;
}
  • 若日志类型是Event,调用android_log_processBinaryLogBuffer,否则调用android_log_processLogBuffer,他们都会将buf转为AndroidLogEntry用于输出
  • android_log_shouldPrintLine 判断tag和pri是否符合设置的过滤条件
  • 当日志超出大小时,调用RotateLogs建立新文件
void Logcat::ProcessBuffer(struct log_msg* buf) {AndroidLogEntry entry;char binaryMsgBuf[1024] __attribute__((__uninitialized__));bool is_binary =buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;int err;if (is_binary) {if (!event_tag_map_ && !has_opened_event_tag_map_) {event_tag_map_.reset(android_openEventTagMap(nullptr));has_opened_event_tag_map_ = true;}// This causes entry to point to binaryMsgBuf!err = android_log_processBinaryLogBuffer(&buf->entry, &entry, event_tag_map_.get(),binaryMsgBuf, sizeof(binaryMsgBuf));// printf(">>> pri=%d len=%d msg='%s'\n",//    entry.priority, entry.messageLen, entry.message);} else {err = android_log_processLogBuffer(&buf->entry, &entry);}if (err < 0 && !debug_) return;if (android_log_shouldPrintLine(logformat_.get(), std::string(entry.tag, entry.tagLen).c_str(),entry.priority)) {bool match = !regex_ ||std::regex_search(entry.message, entry.message + entry.messageLen, *regex_);print_count_ += match;if (match || print_it_anyway_) {PrintDividers(buf->id(), print_dividers_);out_byte_count_ += android_log_printLogLine(logformat_.get(), output_file_, &entry);}}if (log_rotate_size_kb_ > 0 && (out_byte_count_ / 1024) >= log_rotate_size_kb_) {RotateLogs();}
}

android_log_printLogLine时输出的最后一个步骤,调用android_log_formatLogLine格式化要输出的日志,调用fwrite把日志输出到文件描述符fd所描述的目标文件中

size_t android_log_printLogLine(AndroidLogFormat* p_format, FILE* fp,const AndroidLogEntry* entry) {char buf[4096] __attribute__((__uninitialized__));size_t line_length;char* line = android_log_formatLogLine(p_format, buf, sizeof(buf), entry, &line_length);if (!line) {fprintf(stderr, "android_log_formatLogLine failed\n");exit(1);}size_t bytesWritten = fwrite(line, 1, line_length, fp);if (bytesWritten != line_length) {perror("fwrite failed");exit(1);}if (line != buf) free(line);return bytesWritten;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/45300.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在Ubuntu下安装samba实现和Windows系统文件共享

一、安装 apt install -y samba samba-clientSamba is not being run as an AD Domain Controller: Masking samba-ad-dc.service Please ignore the following error about deb-systemd-helper not finding those services. (samba-ad-dc.service masked) Created symlink /et…

百度网盘资料使用

1. 将链接复制到浏览器打开&#xff0c;点击提取文件 2. 提取文件&#xff0c;进入如下界面 3. 因为文件太大&#xff0c;而且未开会员&#xff0c;所以无法全部转存到网盘&#xff0c;只能分批次转存 点击目录&#xff0c;进入文件夹 1&#xff09;选择一个喜欢的文件&#…

9.Python学习:Socket

1.网络通信要素&#xff08;IP端口传输协议&#xff09; 2.Socket编程 2.1TCP、UDP协议了解 2.2 Socket流程 服务端有两个socket对象&#xff0c;客户端有一个 3.Socket实战 服务端代码&#xff1a; import socket #创建Socket对象 sksocket.socket() #绑定ip与端口号-使…

在分布式环境中,怎样保证 PostgreSQL 数据的一致性和完整性?

文章目录 在分布式环境中保证 PostgreSQL 数据的一致性和完整性一、数据一致性和完整性的重要性二、分布式环境对数据一致性和完整性的挑战&#xff08;一&#xff09;网络延迟和故障&#xff08;二&#xff09;并发操作&#xff08;三&#xff09;数据分区和复制 三、保证 Pos…

Apache Spark分布式计算框架架构介绍

目录 一、概述 二、Apache Spark架构组件栈 2.1 概述 2.2 架构图 2.3 架构分层组件说明 2.3.1 支持数据源 2.3.2 调度运行模式 2.3.3 Spark Core核心 2.3.3.1 基础设施 2.3.3.2 存储系统 2.3.3.3 调度系统 2.3.3.4 计算引擎 2.3.4 生态组件 2.3.4.1 Spark SQL 2.…

网络基础:Vlan原理与配置

VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网&#xff09;是一种将一个物理网络划分为多个逻辑子网的技术。它通过在网络交换机上配置&#xff0c;使得不同VLAN中的设备即使连接在同一个物理交换机上&#xff0c;也不能直接进行通信&#xff0c;从而实现…

某企业数据治理总体解决方案(45页PPT)

引言&#xff1a;集团企业数据治理总体解决方案旨在构建一个高效、安全、合规且灵活的数据管理体系&#xff0c;以支持企业决策优化、业务创新、风险管理和运营效率提升。该方案通过整合数据资源、规范数据流程、强化数据质量和促进数据共享&#xff0c;实现数据资产的最大化价…

Lingo学习(三)——工厂合并、运算符、内置函数

一、工厂合并 &#xff08;一&#xff09; 工厂合并——生产二维矩阵 【引入】 sets: factory /1..6/ : a; plant /1..8/ : d; Cooperation(factory,p lant) : c, x; endsets 以上程序可…

【YOLOv8】 用YOLOv8实现数字式工业仪表智能读数(三)

上一篇圆形表盘指针式仪表的项目受到很多人的关注&#xff0c;咱们一鼓作气&#xff0c;把数字式工业仪表的智能读数也研究一下。本篇主要讲如何用YOLOV8实现数字式工业仪表的自动读数&#xff0c;并将读数结果进行输出&#xff0c;若需要完整数据集和源代码可以私信。 目录 &a…

网络安全应急响应信息收集利器-Eagle_Eye

项目介绍: 网络安全应急响应信息收集利器 - Eagle_Eye&#xff1a;您的终端信息自动收集专家 在网络安全的紧急时刻&#xff0c;每一秒都至关重要。Eagle_Eye&#xff0c;这款专为应急响应设计的工具&#xff0c;如同一位随时待命的侦察兵&#xff0c;能够在危机时刻迅速收集…

嵌入式工程师从0开始,到底该学什么,怎么学?

作为嵌入式工程师&#xff0c;从零开始学习需要掌握以下几个关键方面。我收集归类了一份嵌入式学习包&#xff0c;对于新手而言简直不要太棒&#xff0c;里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕设800套和语言类教学&#xff0c;敲个22就可以免费获得。 基…

yarn底层原理详解:(第33天)

系列文章目录 一、yarn总体架构 二、yarn核心组件及功能 三、yarn资源分配与调度 四、yarn提交和执行流程 五、yarn调度算法 六、yarn安全性与容错性 文章目录 系列文章目录前言一、总体架构二、核心组件及功能1. ResourceManager&#xff08;RM&#xff09;2. NodeManager&am…

day29--452. 用最少数量的箭引爆气球+435. 无重叠区间+763.划分字母区间

一、452. 用最少数量的箭引爆气球 题目链接&#xff1a;https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/ 文章讲解&#xff1a;https://programmercarl.com/0452.%E7%94%A8%E6%9C%80%E5%B0%91%E6%95%B0%E9%87%8F%E7%9A%84%E7%AE%AD%E5%BC%95%E7%88…

整数的英语表示

题目链接 整数的英语表示 题目描述 注意点 0 < num < 2^31 - 1 解答思路 每三个数字形成一组&#xff08;高位不足的部分可以用0填充&#xff09;&#xff0c;使用StringBuilder拼接每组的数字和单位关键是三个数字的英语表示&#xff0c;包含个位、十位、百位&…

C++ | Leetcode C++题解之第229题多数元素II

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> majorityElement(vector<int>& nums) {int n nums.size();vector<int> ans;unordered_map<int, int> cnt;for (auto & v : nums) {cnt[v];}for (auto & v : cnt…

搭建ELK+Filebead+zookeeper+kafka实验

目录 1.部署 Filebeat 2. Logstash配置 3.浏览器kibana访问验证 主机名ip地址主要软件es01192.168.9.114ElasticSearches02192.168.9.115ElasticSearches03192.168.9.116ElasticSearch、Kibananginx01192.168.9.113nginx、LogstashNA192.168.9.111nginx、FilebeatNA192.168…

YOLOv8改进 | 注意力机制| 对小目标友好的BiFormer【CVPR2023】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

告别中央服务器:Syncthing实现点对点文件同步

介绍 Syncthing 是一款开源的文件同步工具&#xff0c;可让您在多个设备之间同步文件。 它适用于 Mac OS X、Windows、Linux、FreeBSD、Solaris、OpenBSD等系统。 可以通过浏览器访问来配置和监控该应用程序。 Syncthing 具有以下特点: 1、点对点同步 2、无需中央服务器 …

推三返一,七星创客模式激活消费与分享热情

七星创客模式&#xff0c;作为一种创新的消费与营销融合策略&#xff0c;正悄然改变着传统商业生态的运作方式。其核心精髓“先消费后返利”&#xff0c;不仅颠覆了消费者的传统购物体验&#xff0c;还巧妙地在促进消费与激励分享之间搭建起了一座桥梁。这一模式通过随机返佣的…

【CT】LeetCode手撕—8. 字符串转换整数 (atoi)

目录 题目1- 思路2- 实现⭐8. 字符串转换整数 (atoi)——题解思路 3- ACM 实现 题目 原题连接&#xff1a;8. 字符串转换整数 (atoi) 1- 思路 思路 x 的平方根 ——> 利用二分 ——> 二分的 check条件为 k^2 < x 2- 实现 ⭐8. 字符串转换整数 (atoi)——题解思路 …