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.rhkb.cn/news/375603.html

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

相关文章

实验3-Spark基础-Spark的安装

文章目录 1. 下载安装 Scala1.1 下载 Scala 安装包1.2 基础环境准备1.3 安装 Scala 2. 下载安装 Spark2.1 下载 Spark 安装包2.2 安装 Spark2.3 配置 Spark2.4 创建配置文件 spark-env.sh 3. pyspark 启动4. 建立/user/spark文件夹 1. 下载安装 Scala 1.1 下载 Scala 安装包 下…

Hi3861鸿蒙开发环境搭建

1.1 安装配置Visual Studio Code 打开Download Visual Studio Code - Mac, Linux, Windows选择下载安装Windows系统的Visual Studio Code。 下载后进行安装。Visual Studio Code安装完成后&#xff0c;通过内置的插件市场搜索并安装开发所需的插件如图所示&#xff1a; 1.2 安…

轻松创建对象——简单工厂模式(Java实现)

1. 引言 大家好&#xff0c;又见面了&#xff01;在上一篇文章中&#xff0c;我们通过Python示例介绍了简单工厂模式&#xff0c;今天&#xff0c;我们继续深入这个话题&#xff0c;用Java来实现简单工厂模式。 2. 什么是简单工厂模式 简单工厂模式&#xff08;Simple Facto…

视频融合共享平台视频共享融合赋能平台数字化升级医疗体系

在当前&#xff0c;医疗健康直接关系到国计民生&#xff0c;然而&#xff0c;由于医疗水平和资源分布不均&#xff0c;以及信息系统老化等问题&#xff0c;整体医疗服务能力和水平的提升受到了限制。视频融合云平台作为数字医疗发展的关键推动力量&#xff0c;在医疗领域的广泛…

计算机网络知识汇总

OSI七层模型 七层模型一般指开放系统互连参考模型&#xff0c;开放系统互连参考模型 &#xff08;Open System Interconnect 简称OSI&#xff09;&#xff0c;OSI参考模型是具有7个层次的框架&#xff0c;自底向上的7个层次分别是物理层、数据链路层、网络层、传输层、会话层、…

省市县下拉框的逻辑以及多表联查的实例

2024.7.12 一. 省市县的逻辑开发。1、准备&#xff1a;1.1. 要求&#xff1a;1.2 数据库表&#xff1a; 2. 逻辑&#xff1a;3. 方法3.1 创建实体类3.2 数据访问层3.3 实现递归方法3.4 控制器实现3.5 前端处理 二、多表联查&#xff08;给我干红温了&#xff09;1. 出现了问题2…

python进阶(5):魔术方法篇(1)

之前使用的__init__ 构造方法&#xff0c;是Python类内置的方法之一。 这些内置的类方法&#xff0c;各自有各自特殊的功能&#xff0c;这些内置方法我们称之为&#xff1a;魔术方法 1 __str__ 字符串方法 class Student:name Noneage Nonetel Nonedef __init__(self,name…

shell脚本之if/case语句

一、条件测试 1、1 返回码 $? $? :返回码&#xff0c;用来判断命令或者脚本是否执行成功。 0 &#xff1a;表示true &#xff0c;成功&#xff1b;非0 则表示flase &#xff0c;失败。 1、2 test命令 可以进行条件测试&#xff0c;然后根据返回值来判断条件是否成立 -e…

Redis基础教程(十五):Redis GEO地理信息查询与管理

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

怎么提高音频的播放速度?可以提高音频播放速度的四种方法推荐

怎么提高音频的播放速度&#xff1f;提高音频的播放速度是一种有效的策略&#xff0c;可以显著节省时间和提升信息获取的效率。随着信息量不断增加和学习需求的多样化&#xff0c;快速播放音频已成为许多人在日常生活和工作中的常见做法。这种方法不仅可以用于提高学习效率&…

Git-Unity项目版本管理

目录 准备GitHub新建项目并添加ssh密钥Unity文件夹 本文记录如何用git对unity 项目进行版本管理&#xff0c;并可传至GitHub远端。 准备 名称版本windows11Unity2202.3.9.f1gitN.A.githubN.A. GitHub新建项目并添加ssh密钥 GitHub新建一个repositorywindows11 生成ssh-key&…

全志A527 T527 android13支持usb摄像头

1.前言 我们发现usb摄像头在A527 android13上面并不能正常使用,需要支持相关的摄像头。 2.系统节点查看 我们查看系统是否有相关的节点生成,发现/dev/video相关的节点已经生成了。并没有问题,拔插正常。 3.这里我们需要查看系统层是否支持相关的相机, 我们使用命令进行…

详解yolov5的网络结构

转载自文章 网络结构图&#xff08;简易版和详细版&#xff09; 此图是博主的老师&#xff0c;杜老师的图 网络框架介绍 前言&#xff1a; YOLOv5是一种基于轻量级卷积神经网络&#xff08;CNN&#xff09;的目标检测算法&#xff0c;整体可以分为三个部分&#xff0c; ba…

警钟!电池储能安全事故频发!物联网技术如何加强储能安全排查?

在新能源时代背景下&#xff0c;储能系统作为能源转型的关键支撑技术&#xff0c;其安全问题日益凸显&#xff0c;尤其是近期海外电池项目连续发生的事故&#xff0c;为全球储能行业敲响了警钟。面对这一挑战&#xff0c;物联网技术以其强大的数据采集、智能分析与远程监控能力…

【计算机组成原理 | 第二篇】计算机硬件架构的发展

目录 前言&#xff1a; 冯诺依曼计算机架构 现代计算机架构&#xff1a; 总结&#xff1a; 前言&#xff1a; 在当今数字化时代&#xff0c;计算机硬件不仅是技术进步的见证者&#xff0c;更是推动这一进步的基石。它们构成了我们日常生活中不可或缺的数字生态系统的核心&a…

4. 小迪安全v2023笔记 javaEE应用

4. 小迪安全v2023笔记 javaEE应用 ​ 大体上跟随小迪安全的课程&#xff0c;本意是记录自己的学习历程&#xff0c;不能说是完全原创吧&#xff0c;大家可以关注一下小迪安全。 若有冒犯&#xff0c;麻烦私信移除。 默认有java基础。 文章目录 4. 小迪安全v2023笔记 javaEE应…

C++程序进阶学习

目录 引言 C内存分区 一、内存分区模型 二、 程序运行前 三、程序执行后 C引用 引用的语法 作用 本质 优点 C封装 C对象特性 C对象模型和this指针 C友元 C运算符重载 C继承 C多态 C文件 引言 看过我博客的朋友可能都了解这篇文章内容了&#xff0c;这篇博…

谷粒商城学习笔记-19-快速开发-逆向生成所有微服务基本CRUD代码

文章目录 一&#xff0c;使用逆向工程步骤梳理1&#xff0c;修改逆向工程的application.yml配置2&#xff0c;修改逆向工程的generator.properties配置3&#xff0c;以Debug模式启动逆向工程4&#xff0c;使用逆向工程生成代码5&#xff0c;整合生成的代码到对应的模块中 二&am…

FastAPI 学习之路(三十五)项目结构优化

之前我们创建的文件都是在一个目录中&#xff0c;但是在我们的实际开发中&#xff0c;肯定不能这样设计&#xff0c;那么我们去创建一个目录&#xff0c;叫models&#xff0c;大致如下。 主要目录是&#xff1a; __init__.py 是一个空文件&#xff0c;说明models是一个package…

2.GAP:通用访问协议

GAP的简单理解 GAP这个名字&#xff0c;直接翻译过来不好理解。 简单点可以理解为&#xff1a; 这是蓝牙设备在互联之前&#xff0c;过程中&#xff0c;第一个用于交流的协议。在代码上&#xff0c;会给这个协议实现&#xff0c;连接参数的设置&#xff0c;连接事件的实现&am…