Merge "Support arbitrary loggers."
This commit is contained in:
commit
f78ff16aee
|
@ -17,6 +17,7 @@
|
|||
#ifndef BASE_LOGGING_H
|
||||
#define BASE_LOGGING_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
|
@ -35,10 +36,32 @@ enum LogSeverity {
|
|||
};
|
||||
|
||||
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 @@ enum LogId {
|
|||
// 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 @@ extern const char* ProgramInvocationShortName();
|
|||
// 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 @@ extern const char* ProgramInvocationShortName();
|
|||
|
||||
// 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 @@ extern const char* ProgramInvocationShortName();
|
|||
//
|
||||
// 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
|
||||
|
|
124
base/logging.cpp
124
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 @@ namespace base {
|
|||
|
||||
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 @@ const char* ProgramInvocationShortName() {
|
|||
: "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 InitLogging(char* argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ LogMessage::~LogMessage() {
|
|||
}
|
||||
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 @@ std::ostream& LogMessage::stream() {
|
|||
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) {
|
||||
|
|
|
@ -61,7 +61,7 @@ class CapturedStderr {
|
|||
int old_stderr_;
|
||||
};
|
||||
|
||||
HOST_TEST(logging, CHECK) {
|
||||
TEST(logging, CHECK) {
|
||||
ASSERT_DEATH(CHECK(false), "Check failed: false ");
|
||||
CHECK(true);
|
||||
|
||||
|
@ -82,7 +82,7 @@ std::string make_log_pattern(android::base::LogSeverity severity,
|
|||
log_char, message);
|
||||
}
|
||||
|
||||
HOST_TEST(logging, LOG) {
|
||||
TEST(logging, LOG) {
|
||||
ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
|
||||
|
||||
{
|
||||
|
@ -136,7 +136,7 @@ HOST_TEST(logging, LOG) {
|
|||
}
|
||||
}
|
||||
|
||||
HOST_TEST(logging, PLOG) {
|
||||
TEST(logging, PLOG) {
|
||||
{
|
||||
CapturedStderr cap;
|
||||
errno = ENOENT;
|
||||
|
@ -152,7 +152,7 @@ HOST_TEST(logging, PLOG) {
|
|||
}
|
||||
}
|
||||
|
||||
HOST_TEST(logging, UNIMPLEMENTED) {
|
||||
TEST(logging, UNIMPLEMENTED) {
|
||||
{
|
||||
CapturedStderr cap;
|
||||
errno = ENOENT;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue