Merge "Add StdioLogger for command-line tools."
This commit is contained in:
commit
98c1b1ccf6
6 changed files with 84 additions and 26 deletions
|
@ -100,8 +100,17 @@ using LogFunction = std::function<void(LogId, LogSeverity, const char*, const ch
|
|||
unsigned int, const char*)>;
|
||||
using AbortFunction = std::function<void(const char*)>;
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "android-base/test_utils.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
@ -33,6 +32,9 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#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.
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ static Result<Success> 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] << " <init file to parse>";
|
||||
return -1;
|
||||
|
|
Loading…
Reference in a new issue