diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h index cc7aaf68c..05a12e71a 100644 --- a/base/include/android-base/logging.h +++ b/base/include/android-base/logging.h @@ -100,8 +100,17 @@ using LogFunction = std::function; using AbortFunction = std::function; +// Loggers for use with InitLogging/SetLogger. + +// Log to the kernel log (dmesg). void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); +// Log to stderr in the full logcat format (with pid/tid/time/tag details). void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); +// Log just the message to stdout/stderr (without pid/tid/time/tag details). +// The choice of stdout versus stderr is based on the severity. +// Errors are also prefixed by the program name (as with err(3)/error(3)). +// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools. +void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); void DefaultAborter(const char* abort_message); diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h index b95fa07ce..b29676f7c 100644 --- a/base/include/android-base/test_utils.h +++ b/base/include/android-base/test_utils.h @@ -58,21 +58,33 @@ class TemporaryDir { DISALLOW_COPY_AND_ASSIGN(TemporaryDir); }; -class CapturedStderr { +class CapturedStdFd { public: - CapturedStderr(); - ~CapturedStderr(); + CapturedStdFd(int std_fd); + ~CapturedStdFd(); int fd() const; + std::string str(); private: - void init(); - void reset(); + void Init(); + void Reset(); TemporaryFile temp_file_; - int old_stderr_; + int std_fd_; + int old_fd_; - DISALLOW_COPY_AND_ASSIGN(CapturedStderr); + DISALLOW_COPY_AND_ASSIGN(CapturedStdFd); +}; + +class CapturedStderr : public CapturedStdFd { + public: + CapturedStderr() : CapturedStdFd(STDERR_FILENO) {} +}; + +class CapturedStdout : public CapturedStdFd { + public: + CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {} }; #define ASSERT_MATCH(str, pattern) \ diff --git a/base/logging.cpp b/base/logging.cpp index a33da2211..978d56d07 100644 --- a/base/logging.cpp +++ b/base/logging.cpp @@ -212,6 +212,16 @@ void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file timestamp, getpid(), GetThreadId(), file, line, message); } +void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/, + unsigned int /*line*/, const char* message) { + if (severity >= WARNING) { + fflush(stdout); + fprintf(stderr, "%s: %s\n", getprogname(), message); + } else { + fprintf(stdout, "%s\n", message); + } +} + void DefaultAborter(const char* abort_message) { #ifdef __ANDROID__ android_set_abort_message(abort_message); diff --git a/base/logging_test.cpp b/base/logging_test.cpp index 5f689faf3..75b4ea045 100644 --- a/base/logging_test.cpp +++ b/base/logging_test.cpp @@ -206,11 +206,9 @@ static std::string make_log_pattern(android::base::LogSeverity severity, } #endif -static void CheckMessage(const CapturedStderr& cap, android::base::LogSeverity severity, +static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity, const char* expected, const char* expected_tag = nullptr) { - std::string output; - ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); - android::base::ReadFdToString(cap.fd(), &output); + std::string output = cap.str(); // We can't usefully check the output of any of these on Windows because we // don't have std::regex, but we can at least make sure we printed at least as @@ -623,3 +621,23 @@ TEST(logging, SetDefaultTag) { } CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag); } + +TEST(logging, StdioLogger) { + std::string err_str; + std::string out_str; + { + CapturedStderr cap_err; + CapturedStdout cap_out; + android::base::SetLogger(android::base::StdioLogger); + LOG(INFO) << "out"; + LOG(ERROR) << "err"; + err_str = cap_err.str(); + out_str = cap_out.str(); + } + + // For INFO we expect just the literal "out\n". + ASSERT_EQ("out\n", out_str) << out_str; + // Whereas ERROR logging includes the program name. + ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", err_str) + << err_str; +} diff --git a/base/test_utils.cpp b/base/test_utils.cpp index 1619c2134..5096369e7 100644 --- a/base/test_utils.cpp +++ b/base/test_utils.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "android-base/logging.h" #include "android-base/test_utils.h" #include @@ -33,6 +32,9 @@ #include +#include +#include + #ifdef _WIN32 int mkstemp(char* template_name) { if (_mktemp(template_name) == nullptr) { @@ -123,31 +125,38 @@ bool TemporaryDir::init(const std::string& tmp_dir) { return (mkdtemp(path) != nullptr); } -CapturedStderr::CapturedStderr() : old_stderr_(-1) { - init(); +CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) { + Init(); } -CapturedStderr::~CapturedStderr() { - reset(); +CapturedStdFd::~CapturedStdFd() { + Reset(); } -int CapturedStderr::fd() const { +int CapturedStdFd::fd() const { return temp_file_.fd; } -void CapturedStderr::init() { +std::string CapturedStdFd::str() { + std::string result; + CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET))); + android::base::ReadFdToString(fd(), &result); + return result; +} + +void CapturedStdFd::Init() { #if defined(_WIN32) // On Windows, stderr is often buffered, so make sure it is unbuffered so // that we can immediately read back what was written to stderr. - CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0)); + if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0)); #endif - old_stderr_ = dup(STDERR_FILENO); - CHECK_NE(-1, old_stderr_); - CHECK_NE(-1, dup2(fd(), STDERR_FILENO)); + old_fd_ = dup(std_fd_); + CHECK_NE(-1, old_fd_); + CHECK_NE(-1, dup2(fd(), std_fd_)); } -void CapturedStderr::reset() { - CHECK_NE(-1, dup2(old_stderr_, STDERR_FILENO)); - CHECK_EQ(0, close(old_stderr_)); +void CapturedStdFd::Reset() { + CHECK_NE(-1, dup2(old_fd_, std_fd_)); + CHECK_EQ(0, close(old_fd_)); // Note: cannot restore prior setvbuf() setting. } diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp index 5232b7e5e..df497eade 100644 --- a/init/host_init_parser.cpp +++ b/init/host_init_parser.cpp @@ -48,7 +48,7 @@ static Result do_stub(const BuiltinArguments& args) { #include "generated_stub_builtin_function_map.h" int main(int argc, char** argv) { - android::base::InitLogging(argv, &android::base::StderrLogger); + android::base::InitLogging(argv, &android::base::StdioLogger); if (argc != 2) { LOG(ERROR) << "Usage: " << argv[0] << " "; return -1;