Skip to content

Commit

Permalink
Add logtostdout and colorlogtostdout flags for logging to stdout
Browse files Browse the repository at this point in the history
Currently, glog allows to use of logtostderr to send error logs to
stderr, but many log tailers would regard logs from stderr as error
logs. So we want to send non-error logs to stdout and only send error
logs to stderr according to the stderrthreshold.
  • Loading branch information
git-hulk authored and sergiud committed Feb 19, 2022
1 parent b38ed9d commit 180b700
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 28 deletions.
6 changes: 6 additions & 0 deletions src/glog/logging.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ typedef void(*CustomPrefixCallback)(std::ostream& s, const LogMessageInfo& l, vo
// Set whether appending a timestamp to the log file name
DECLARE_bool(timestamp_in_logfile_name);

// Set whether log messages go to stdout instead of logfiles
DECLARE_bool(logtostdout);

// Set color messages logged to stdout (if supported by terminal).
DECLARE_bool(colorlogtostdout);

// Set whether log messages go to stderr instead of logfiles
DECLARE_bool(logtostderr);

Expand Down
21 changes: 18 additions & 3 deletions src/googletest.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ static inline void CaptureTestOutput(int fd, const string & filename) {
CHECK(s_captured_streams[fd] == NULL);
s_captured_streams[fd] = new CapturedStream(fd, filename);
}
static inline void CaptureTestStdout() {
CaptureTestOutput(STDOUT_FILENO, FLAGS_test_tmpdir + "/captured.out");
}
static inline void CaptureTestStderr() {
CaptureTestOutput(STDERR_FILENO, FLAGS_test_tmpdir + "/captured.err");
}
Expand Down Expand Up @@ -504,9 +507,13 @@ static inline void WriteToFile(const string& body, const string& file) {
fclose(fp);
}

static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
CapturedStream* cap = s_captured_streams[STDERR_FILENO];
CHECK(cap) << ": did you forget CaptureTestStderr()?";
static inline bool MungeAndDiffTest(const string& golden_filename,
CapturedStream* cap) {
if (cap == s_captured_streams[STDOUT_FILENO]) {
CHECK(cap) << ": did you forget CaptureTestStdout()?";
} else {
CHECK(cap) << ": did you forget CaptureTestStderr()?";
}

cap->StopCapture();

Expand Down Expand Up @@ -536,6 +543,14 @@ static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
return true;
}

static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
return MungeAndDiffTest(golden_filename, s_captured_streams[STDERR_FILENO]);
}

static inline bool MungeAndDiffTestStdout(const string& golden_filename) {
return MungeAndDiffTest(golden_filename, s_captured_streams[STDOUT_FILENO]);
}

// Save flags used from logging_unittest.cc.
#ifndef HAVE_LIB_GFLAGS
struct FlagSaver {
Expand Down
75 changes: 52 additions & 23 deletions src/logging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ GLOG_DEFINE_bool(alsologtostderr, BoolFromEnv("GOOGLE_ALSOLOGTOSTDERR", false),
"log messages go to stderr in addition to logfiles");
GLOG_DEFINE_bool(colorlogtostderr, false,
"color messages logged to stderr (if supported by terminal)");
GLOG_DEFINE_bool(colorlogtostdout, false,
"color messages logged to stdout (if supported by terminal)");
GLOG_DEFINE_bool(logtostdout, BoolFromEnv("GOOGLE_LOGTOSTDOUT", false),
"log messages go to stdout instead of logfiles");
#ifdef GLOG_OS_LINUX
GLOG_DEFINE_bool(drop_log_memory, true, "Drop in-memory buffers of log contents. "
"Logs can grow very quickly and they are rarely read before they "
Expand Down Expand Up @@ -739,43 +743,63 @@ inline void LogDestination::SetEmailLogging(LogSeverity min_severity,
LogDestination::addresses_ = addresses;
}

static void ColoredWriteToStderr(LogSeverity severity,
const char* message, size_t len) {
const GLogColor color =
(LogDestination::terminal_supports_color() && FLAGS_colorlogtostderr) ?
SeverityToColor(severity) : COLOR_DEFAULT;
static void ColoredWriteToStderrOrStdout(FILE* output, LogSeverity severity,
const char* message, size_t len) {
bool is_stdout = (output == stdout);
const GLogColor color = (LogDestination::terminal_supports_color() &&
((!is_stdout && FLAGS_colorlogtostderr) ||
(is_stdout && FLAGS_colorlogtostdout)))
? SeverityToColor(severity)
: COLOR_DEFAULT;

// Avoid using cerr from this module since we may get called during
// exit code, and cerr may be partially or fully destroyed by then.
if (COLOR_DEFAULT == color) {
fwrite(message, len, 1, stderr);
fwrite(message, len, 1, output);
return;
}
#ifdef GLOG_OS_WINDOWS
const HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
const HANDLE output_handle =
GetStdHandle(is_stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);

// Gets the current text color.
CONSOLE_SCREEN_BUFFER_INFO buffer_info;
GetConsoleScreenBufferInfo(stderr_handle, &buffer_info);
GetConsoleScreenBufferInfo(output_handle, &buffer_info);
const WORD old_color_attrs = buffer_info.wAttributes;

// We need to flush the stream buffers into the console before each
// SetConsoleTextAttribute call lest it affect the text that is already
// printed but has not yet reached the console.
fflush(stderr);
SetConsoleTextAttribute(stderr_handle,
fflush(output);
SetConsoleTextAttribute(output_handle,
GetColorAttribute(color) | FOREGROUND_INTENSITY);
fwrite(message, len, 1, stderr);
fflush(stderr);
fwrite(message, len, 1, output);
fflush(output);
// Restores the text color.
SetConsoleTextAttribute(stderr_handle, old_color_attrs);
SetConsoleTextAttribute(output_handle, old_color_attrs);
#else
fprintf(stderr, "\033[0;3%sm", GetAnsiColorCode(color));
fwrite(message, len, 1, stderr);
fprintf(stderr, "\033[m"); // Resets the terminal to default.
fprintf(output, "\033[0;3%sm", GetAnsiColorCode(color));
fwrite(message, len, 1, output);
fprintf(output, "\033[m"); // Resets the terminal to default.
#endif // GLOG_OS_WINDOWS
}

static void ColoredWriteToStdout(LogSeverity severity, const char* message,
size_t len) {
FILE* output = stdout;
// We also need to send logs to the stderr when the severity is
// higher or equal to the stderr threshold.
if (severity >= FLAGS_stderrthreshold) {
output = stderr;
}
ColoredWriteToStderrOrStdout(output, severity, message, len);
}

static void ColoredWriteToStderr(LogSeverity severity, const char* message,
size_t len) {
ColoredWriteToStderrOrStdout(stderr, severity, message, len);
}

static void WriteToStderr(const char* message, size_t len) {
// Avoid using cerr from this module since we may get called during
// exit code, and cerr may be partially or fully destroyed by then.
Expand Down Expand Up @@ -847,8 +871,9 @@ inline void LogDestination::LogToAllLogfiles(LogSeverity severity,
time_t timestamp,
const char* message,
size_t len) {

if ( FLAGS_logtostderr ) { // global flag: never log to file
if (FLAGS_logtostdout) { // global flag: never log to file
ColoredWriteToStdout(severity, message, len);
} else if (FLAGS_logtostderr) { // global flag: never log to file
ColoredWriteToStderr(severity, message, len);
} else {
for (int i = severity; i >= 0; --i) {
Expand Down Expand Up @@ -1812,9 +1837,14 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
// global flag: never log to file if set. Also -- don't log to a
// file if we haven't parsed the command line flags to get the
// program name.
if (FLAGS_logtostderr || !IsGoogleLoggingInitialized()) {
ColoredWriteToStderr(data_->severity_,
data_->message_text_, data_->num_chars_to_log_);
if (FLAGS_logtostderr || FLAGS_logtostdout || !IsGoogleLoggingInitialized()) {
if (FLAGS_logtostdout) {
ColoredWriteToStdout(data_->severity_, data_->message_text_,
data_->num_chars_to_log_);
} else {
ColoredWriteToStderr(data_->severity_, data_->message_text_,
data_->num_chars_to_log_);
}

// this could be protected by a flag if necessary.
LogDestination::LogToSinks(data_->severity_,
Expand All @@ -1824,7 +1854,6 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
(data_->num_chars_to_log_ -
data_->num_prefix_chars_ - 1) );
} else {

// log this message to all log files of severity <= severity_
LogDestination::LogToAllLogfiles(data_->severity_, logmsgtime_.timestamp(),
data_->message_text_,
Expand Down Expand Up @@ -1862,7 +1891,7 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
fatal_time = logmsgtime_.timestamp();
}

if (!FLAGS_logtostderr) {
if (!FLAGS_logtostderr && !FLAGS_logtostdout) {
for (int i = 0; i < NUM_SEVERITIES; ++i) {
if (LogDestination::log_destinations_[i]) {
LogDestination::log_destinations_[i]->logger_->Write(true, 0, "", 0);
Expand Down
19 changes: 19 additions & 0 deletions src/logging_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,22 @@ int main(int argc, char **argv) {

FLAGS_logtostderr = false;

FLAGS_logtostdout = true;
FLAGS_stderrthreshold = NUM_SEVERITIES;
CaptureTestStdout();
TestRawLogging();
TestLoggingLevels();
TestLogString();
TestLogSink();
TestLogToString();
TestLogSinkWaitTillSent();
TestCHECK();
TestDCHECK();
TestSTREQ();
EXPECT_TRUE(
MungeAndDiffTestStdout(FLAGS_test_srcdir + "/src/logging_unittest.out"));
FLAGS_logtostdout = false;

TestBasename();
TestBasenameAppendWhenNoTimestamp();
TestTwoProcessesWrite();
Expand Down Expand Up @@ -1263,6 +1279,9 @@ class TestWaitingLogSink : public LogSink {
// Check that LogSink::WaitTillSent can be used in the advertised way.
// We also do golden-stderr comparison.
static void TestLogSinkWaitTillSent() {
// Clear global_messages here to make sure that this test case can be
// reentered
global_messages.clear();
{ TestWaitingLogSink sink;
// Sleeps give the sink threads time to do all their work,
// so that we get a reliable log capture to compare to the golden file.
Expand Down
Loading

0 comments on commit 180b700

Please sign in to comment.