Support arbitrary loggers.

While the defaults (logd or stderr) make sense for most use cases,
there are places that can only log to the kernel, or need to log to a
file, etc.

Allow the user to pass in an arbitrary logging object, and provide
LogdLogger and StderrLogger as defaults.

Change-Id: I62368acc795ff313242bb205d65017404bf64e88
diff --git a/base/include/base/logging.h b/base/include/base/logging.h
index 3460997..19c1b64 100644
--- a/base/include/base/logging.h
+++ b/base/include/base/logging.h
@@ -17,6 +17,7 @@
 #ifndef BASE_LOGGING_H
 #define BASE_LOGGING_H
 
+#include <functional>
 #include <memory>
 #include <ostream>
 
@@ -35,10 +36,32 @@
 };
 
 enum LogId {
+  DEFAULT,
   MAIN,
   SYSTEM,
 };
 
+typedef std::function<void(LogId, LogSeverity, const char*, const char*,
+                           unsigned int, const char*)> LogFunction;
+
+extern void StderrLogger(LogId, LogSeverity, const char*, const char*,
+                         unsigned int, const char*);
+
+#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
 //
@@ -47,8 +70,15 @@
 // 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.
+extern void InitLogging(char* argv[], LogFunction&& logger);
+
+// Configures logging using the default logger (logd for the device, stderr for
+// the host).
 extern void InitLogging(char* argv[]);
 
+// Replace the current logger.
+extern void SetLogger(LogFunction&& logger);
+
 // Returns the command line used to invoke the current tool or nullptr if
 // InitLogging hasn't been performed.
 extern const char* GetCmdLine();
@@ -65,8 +95,8 @@
 // FATAL it also causes an abort. For example:
 //
 //     LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity)                                                    \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::MAIN, \
+#define LOG(severity)                                                       \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                               ::android::base::severity, -1).stream()
 
 // Logs a message to logcat with the specified log ID on Android otherwise to
@@ -77,8 +107,8 @@
 
 // A variant of LOG that also logs the current errno value. To be used when
 // library calls fail.
-#define PLOG(severity)                                                   \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::MAIN, \
+#define PLOG(severity)                                                      \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                               ::android::base::severity, errno).stream()
 
 // Behaves like PLOG, but logs to the specified log ID.
@@ -96,20 +126,20 @@
 //
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
-#define CHECK(x)                                                         \
-  if (UNLIKELY(!(x)))                                                    \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::MAIN, \
-                              ::android::base::FATAL, -1).stream()       \
+#define CHECK(x)                                                            \
+  if (UNLIKELY(!(x)))                                                       \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+                              ::android::base::FATAL, -1).stream()          \
       << "Check failed: " #x << " "
 
 // Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP)                                           \
-  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);     \
-       UNLIKELY(!(_values.lhs OP _values.rhs));                          \
-       /* empty */)                                                      \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::MAIN, \
-                              ::android::base::FATAL, -1).stream()       \
-      << "Check failed: " << #LHS << " " << #OP << " " << #RHS           \
+#define CHECK_OP(LHS, RHS, OP)                                              \
+  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);        \
+       UNLIKELY(!(_values.lhs OP _values.rhs));                             \
+       /* empty */)                                                         \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+                              ::android::base::FATAL, -1).stream()          \
+      << "Check failed: " << #LHS << " " << #OP << " " << #RHS              \
       << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
 
 // Check whether a condition holds between x and y, LOG(FATAL) if not. The value
diff --git a/base/logging.cpp b/base/logging.cpp
index d2318cb..a36ac5f 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -21,6 +21,7 @@
 #include <mutex>
 #include <sstream>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/strings.h"
@@ -40,6 +41,12 @@
 
 static std::mutex logging_lock;
 
+#ifdef __ANDROID__
+static LogFunction gLogger = LogdLogger();
+#else
+static LogFunction gLogger = StderrLogger;
+#endif
+
 static LogSeverity gMinimumLogSeverity = INFO;
 static std::unique_ptr<std::string> gCmdLine;
 static std::unique_ptr<std::string> gProgramInvocationName;
@@ -61,6 +68,58 @@
              : "unknown";
 }
 
+void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
+                  unsigned int line, const char* message) {
+  static const char* log_characters = "VDIWEF";
+  CHECK_EQ(strlen(log_characters), FATAL + 1U);
+  char severity_char = log_characters[severity];
+  fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationShortName(),
+          severity_char, getpid(), gettid(), file, line, message);
+}
+
+
+#ifdef __ANDROID__
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
+}
+
+static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
+    ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
+    ANDROID_LOG_WARN,    ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
+};
+static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
+              "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
+              "in LogSeverity");
+
+static const 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");
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
+                            const char* file, unsigned int line,
+                            const char* message) {
+  int priority = kLogSeverityToAndroidLogPriority[severity];
+  if (id == DEFAULT) {
+    id = default_log_id_;
+  }
+
+  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);
+  }
+}
+#endif
+
+void InitLogging(char* argv[], LogFunction&& logger) {
+  SetLogger(std::forward<LogFunction>(logger));
+  InitLogging(argv);
+}
+
 void InitLogging(char* argv[]) {
   if (gCmdLine.get() != nullptr) {
     return;
@@ -124,6 +183,11 @@
   }
 }
 
+void SetLogger(LogFunction&& logger) {
+  std::lock_guard<std::mutex> lock(logging_lock);
+  gLogger = std::move(logger);
+}
+
 // This indirection greatly reduces the stack impact of having lots of
 // checks/logging in a function.
 class LogMessageData {
@@ -194,22 +258,18 @@
   }
   std::string msg(data_->ToString());
 
-  // Do the actual logging with the lock held.
-  {
-    std::lock_guard<std::mutex> lock(logging_lock);
-    if (msg.find('\n') == std::string::npos) {
+  if (msg.find('\n') == std::string::npos) {
+    LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
+            data_->GetSeverity(), msg.c_str());
+  } else {
+    msg += '\n';
+    size_t i = 0;
+    while (i < msg.size()) {
+      size_t nl = msg.find('\n', i);
+      msg[nl] = '\0';
       LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-              data_->GetSeverity(), msg.c_str());
-    } else {
-      msg += '\n';
-      size_t i = 0;
-      while (i < msg.size()) {
-        size_t nl = msg.find('\n', i);
-        msg[nl] = '\0';
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-                data_->GetSeverity(), &msg[i]);
-        i = nl + 1;
-      }
+              data_->GetSeverity(), &msg[i]);
+      i = nl + 1;
     }
   }
 
@@ -226,39 +286,11 @@
   return data_->GetBuffer();
 }
 
-#ifdef __ANDROID__
-static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
-    ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
-    ANDROID_LOG_WARN,    ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
-static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
-              "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
-              "in LogSeverity");
-
-static const log_id kLogIdToAndroidLogId[] = {LOG_ID_MAIN, LOG_ID_SYSTEM};
-static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
-              "Mismatch in size of kLogIdToAndroidLogId and values "
-              "in LogSeverity");
-#endif
-
 void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
-                         LogSeverity log_severity, const char* message) {
-#ifdef __ANDROID__
+                         LogSeverity severity, const char* message) {
   const char* tag = ProgramInvocationShortName();
-  int priority = kLogSeverityToAndroidLogPriority[log_severity];
-  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);
-  }
-#else
-  UNUSED(id);
-  static const char* log_characters = "VDIWEF";
-  CHECK_EQ(strlen(log_characters), FATAL + 1U);
-  char severity = log_characters[log_severity];
-  fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationShortName(),
-          severity, getpid(), gettid(), file, line, message);
-#endif
+  std::lock_guard<std::mutex> lock(logging_lock);
+  gLogger(id, severity, tag, file, line, message);
 }
 
 ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 0a03e38..d947c1d 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -61,7 +61,7 @@
   int old_stderr_;
 };
 
-HOST_TEST(logging, CHECK) {
+TEST(logging, CHECK) {
   ASSERT_DEATH(CHECK(false), "Check failed: false ");
   CHECK(true);
 
@@ -82,7 +82,7 @@
       log_char, message);
 }
 
-HOST_TEST(logging, LOG) {
+TEST(logging, LOG) {
   ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
 
   {
@@ -136,7 +136,7 @@
   }
 }
 
-HOST_TEST(logging, PLOG) {
+TEST(logging, PLOG) {
   {
     CapturedStderr cap;
     errno = ENOENT;
@@ -152,7 +152,7 @@
   }
 }
 
-HOST_TEST(logging, UNIMPLEMENTED) {
+TEST(logging, UNIMPLEMENTED) {
   {
     CapturedStderr cap;
     errno = ENOENT;
diff --git a/base/test_main.cpp b/base/test_main.cpp
index c49ca4b..546923d 100644
--- a/base/test_main.cpp
+++ b/base/test_main.cpp
@@ -20,6 +20,6 @@
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
-  android::base::InitLogging(argv);
+  android::base::InitLogging(argv, android::base::StderrLogger);
   return RUN_ALL_TESTS();
 }