赞
踩
4种类型的日志通过下面4个设备文件来访问
无论什么类型,最终调用write_to_log写入Logger日志驱动程序
tag为整数,根据/system/etc/event-log-tags转为字符串
/system/etc/event-log-tags还用来描述events类型的日志内容的格式
值格式为
2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1)
如上为/system/etc/event-log-tags的内容
msg格式为
./system/logging/liblog/include/log/log_read.h(Android13)
logger_entry描述一个日志记录,最大长度为4K,其有效负载长度最大等于4K减去结构体logger_entry的大小
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 */
};
#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 __cplusplus uint64_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 };
system/logging/logd/main.cpp,进行初始化操作
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 tags LogTags 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 logd SetProperty("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::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); }
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)) char buffer[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 terminator buffer[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.sh sock = 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; }
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
system/logging/liblog/logd_writer.cpp
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; }
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_; };
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; }
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); } #endif get_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; } #else return 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_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; }
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); }
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
/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__)
/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
/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
system/logging/liblog/include/log/log.h
#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
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
根据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; }
frameworks/base/core/java/android/util/Slog.java
只可在系统内部使用,同上调用Log中的println_native传入LOG_ID_SYSTEM
public final class Slog { ...... @UnsupportedAppUsage public static int v(@Nullable String tag, @NonNull String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg); } @UnsupportedAppUsage public static int d(@Nullable String tag, @NonNull String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg); } @UnsupportedAppUsage public static int i(@Nullable String tag, @NonNull String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg); } @UnsupportedAppUsage public static int w(@Nullable String tag, @NonNull String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg); } @UnsupportedAppUsage public static int e(@Nullable String tag, @NonNull String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg); } }
frameworks/base/core/java/android/util/Rlog.java
只可在系统内部使用,同上调用Log中的println_native传入LOG_ID_RADIO
public final class Rlog { ..... @UnsupportedAppUsage public static int v(String tag, String msg) { return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg); } @UnsupportedAppUsage public static int d(String tag, String msg) { return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag, msg); } @UnsupportedAppUsage public static int i(String tag, String msg) { return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag, msg); } @UnsupportedAppUsage public static int w(String tag, String msg) { return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag, msg); } @UnsupportedAppUsage public 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); } }
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); ...... }
根据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); }
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)); }
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 file output_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); } }
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; } #endif return 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 logcat if (err < 0) { fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg.c_str()); } if (err > 0) hasSetLogFormat = true; } } if (!hasSetLogFormat) { SetLogFormat("threadtime"); } }
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 variable const 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 commandline for (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_STRNDUP tagName = 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;
}
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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。