In this blog, we will analyze the functions provided by logging.h. by referencing this file, you can include < Android base / logging.h >, which is implemented in logging.cpp.
First of all, let's take a look at its comments: it provides an interface of c++ stream, and PLOG will print out specific errors, and it also supports the printing of logcat, stderr and dmesg.
// // Google-style C++ logging. // // This header provides a C++ stream interface to logging. // // To log: // // LOG(INFO) << "Some text; " << some_value; // // Replace `INFO` with any severity from `enum LogSeverity`. // // To log the result of a failed function and include the string // representation of `errno` at the end: // // PLOG(ERROR) << "Write failed"; // // The output will be something like `Write failed: I/O error`. // Remember this as 'P' as in perror(3). // // To output your own types, simply implement operator<< as normal. // // By default, output goes to logcat on Android and stderr on the host. // A process can use `SetLogger` to decide where all logging goes. // Implementations are provided for logcat, stderr, and dmesg. // This header also provides assertions: // // CHECK(must_be_true); // CHECK_EQ(a, b) << z_is_interesting_too; // NOTE: For Windows, you must include logging.h after windows.h to allow the // following code to suppress the evil ERROR macro:
Let's take a look at this header file. Before using it, we must call the InitLogging function. Here are three loggers to choose from: kernelllogger, StderrLogger, and LogdLogger. In android, the default is LogdLogger.
void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); void DefaultAborter(const char* abort_message); #ifdef __ANDROID__ // We expose this even though it is the default because a user that wants to // override the default log buffer will have to construct this themselves. class LogdLogger { public: explicit LogdLogger(LogId default_log_id = android::base::MAIN); void operator()(LogId, LogSeverity, const char* tag, const char* file, unsigned int line, const char* message); private: LogId default_log_id_; }; #endif // Configure logging based on ANDROID_LOG_TAGS environment variable. // We need to parse a string that looks like // // *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i // // The tag (or '*' for the global level) comes first, followed by a colon and a // letter indicating the minimum priority level we're expected to log. This can // be used to reveal or conceal logs with specific tags. #ifdef __ANDROID__ #define INIT_LOGGING_DEFAULT_LOGGER LogdLogger() #else #define INIT_LOGGING_DEFAULT_LOGGER StderrLogger #endif void InitLogging(char* argv[], LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER, AbortFunction&& aborter = DefaultAborter); #undef INIT_LOGGING_DEFAULT_LOGGER
Let's take a look at the implementation of these three loggers.
The kernel is to write to dev/kmsg
void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, const char*, unsigned int, const char* msg) { // clang-format off static constexpr int kLogSeverityToKernelLogLevel[] = { [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log // level) [android::base::DEBUG] = 7, // KERN_DEBUG [android::base::INFO] = 6, // KERN_INFO [android::base::WARNING] = 4, // KERN_WARNING [android::base::ERROR] = 3, // KERN_ERROR [android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT [android::base::FATAL] = 2, // KERN_CRIT }; // clang-format on static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1, "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity"); static int klog_fd = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); if (klog_fd == -1) return; int level = kLogSeverityToKernelLogLevel[severity]; // The kernel's printk buffer is only 1024 bytes. // TODO: should we automatically break up long lines into multiple lines? // Or we could log but with something like "..." at the end? char buf[1024]; size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg); if (size > sizeof(buf)) { size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n", level, tag, size); } iovec iov[1]; iov[0].iov_base = buf; iov[0].iov_len = size; TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1)); }
Here's stderr
void StderrLogger(LogId, LogSeverity severity, const char*, const char* file, unsigned int line, const char* message) { struct tm now; time_t t = time(nullptr); #if defined(_WIN32) localtime_s(&now, &t); #else localtime_r(&t, &now); #endif char timestamp[32]; strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now); static const char log_characters[] = "VDIWEFF"; static_assert(arraysize(log_characters) - 1 == FATAL + 1, "Mismatch in size of log_characters and values in LogSeverity"); char severity_char = log_characters[severity]; fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", ProgramInvocationName().c_str(), severity_char, timestamp, getpid(), GetThreadId(), file, line, message); }
Logd is the last call to the __android_log_buf_print function to write to logd. This function calls log/log.h
void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file, unsigned int line, const char* message) { static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = { ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL, }; static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1, "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity"); int priority = kLogSeverityToAndroidLogPriority[severity]; if (id == DEFAULT) { id = default_log_id_; } static constexpr log_id kLogIdToAndroidLogId[] = { LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM, }; static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1, "Mismatch in size of kLogIdToAndroidLogId and values in LogId"); log_id lg_id = kLogIdToAndroidLogId[id]; if (priority == ANDROID_LOG_FATAL) { __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line, message); } else { __android_log_buf_print(lg_id, priority, tag, "%s", message); }
This file provides a very convenient function. It can not only use the c + + stream, but also switch between various logs, as long as you specify different logger s in InitLogging.
Let's take an example of init. Init calls the initkernelllogging function at the beginning. This function writes all the standard input and output and standard errors to / sys/fs/selinux/null, and there will be no more. Then I set an InitLogging as kernel logger, which is written in ksmg.
void InitKernelLogging(char* argv[]) { // Make stdin/stdout/stderr all point to /dev/null. int fd = open("/sys/fs/selinux/null", O_RDWR); if (fd == -1) { int saved_errno = errno; android::base::InitLogging(argv, &android::base::KernelLogger); errno = saved_errno; PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null"; } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); android::base::InitLogging(argv, &android::base::KernelLogger); }