X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Flogging.cc;h=0d9aad89d6eb1e0a392566d036e79615269ed408;hb=ce8799c8e2fff9e9e446358f7d17eee2f46ed45e;hp=1b864cddd63b2594953db88abd506b86bc2b8316;hpb=11e78e4c66a1db78e611fac91fd3429059976f38;p=platform%2Fupstream%2Fglog.git diff --git a/src/logging.cc b/src/logging.cc index 1b864cd..0d9aad8 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -31,6 +31,7 @@ #include "utilities.h" +#include #include #include #include @@ -68,7 +69,6 @@ using std::string; using std::vector; -using std::ostrstream; using std::setw; using std::setfill; using std::hex; @@ -76,7 +76,21 @@ using std::dec; using std::min; using std::ostream; using std::ostringstream; -using std::strstream; + +using std::FILE; +using std::fwrite; +using std::fclose; +using std::fflush; +using std::fprintf; +using std::perror; + +#ifdef __QNX__ +using std::fdopen; +#endif + +#ifdef _WIN32 +#define fdopen _fdopen +#endif // There is no thread annotation support. #define EXCLUSIVE_LOCKS_REQUIRED(mu) @@ -93,16 +107,13 @@ GLOG_DEFINE_bool(logtostderr, BoolFromEnv("GOOGLE_LOGTOSTDERR", false), "log messages go to stderr instead of logfiles"); 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)"); #ifdef 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 " "need to be evicted from memory. Instead, drop them from memory " "as soon as they are flushed to disk."); -_START_GOOGLE_NAMESPACE_ -namespace logging { -static const int64 kPageSize = getpagesize(); -} -_END_GOOGLE_NAMESPACE_ #endif // By default, errors (including fatal errors) get logged to stderr as @@ -149,6 +160,8 @@ static const char* DefaultLogDir() { return ""; } +GLOG_DEFINE_int32(logfile_mode, 0664, "Log file mode/permissions."); + GLOG_DEFINE_string(log_dir, DefaultLogDir(), "If specified, logfiles are written into this directory instead " "of the default logging directory."); @@ -168,6 +181,42 @@ GLOG_DEFINE_string(log_backtrace_at, "", // TODO(hamaji): consider windows #define PATH_SEPARATOR '/' +#ifndef HAVE_PREAD +#if defined(OS_WINDOWS) +#include +#define ssize_t SSIZE_T +#endif +static ssize_t pread(int fd, void* buf, size_t count, off_t offset) { + off_t orig_offset = lseek(fd, 0, SEEK_CUR); + if (orig_offset == (off_t)-1) + return -1; + if (lseek(fd, offset, SEEK_CUR) == (off_t)-1) + return -1; + ssize_t len = read(fd, buf, count); + if (len < 0) + return len; + if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1) + return -1; + return len; +} +#endif // !HAVE_PREAD + +#ifndef HAVE_PWRITE +static ssize_t pwrite(int fd, void* buf, size_t count, off_t offset) { + off_t orig_offset = lseek(fd, 0, SEEK_CUR); + if (orig_offset == (off_t)-1) + return -1; + if (lseek(fd, offset, SEEK_CUR) == (off_t)-1) + return -1; + ssize_t len = write(fd, buf, count); + if (len < 0) + return len; + if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1) + return -1; + return len; +} +#endif // !HAVE_PWRITE + static void GetHostName(string* hostname) { #if defined(HAVE_SYS_UTSNAME_H) struct utsname buf; @@ -190,13 +239,127 @@ static void GetHostName(string* hostname) { #endif } +// Returns true iff terminal supports using colors in output. +static bool TerminalSupportsColor() { + bool term_supports_color = false; +#ifdef OS_WINDOWS + // on Windows TERM variable is usually not set, but the console does + // support colors. + term_supports_color = true; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = getenv("TERM"); + if (term != NULL && term[0] != '\0') { + term_supports_color = + !strcmp(term, "xterm") || + !strcmp(term, "xterm-color") || + !strcmp(term, "xterm-256color") || + !strcmp(term, "screen-256color") || + !strcmp(term, "screen") || + !strcmp(term, "linux") || + !strcmp(term, "cygwin"); + } +#endif + return term_supports_color; +} + _START_GOOGLE_NAMESPACE_ +enum GLogColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +static GLogColor SeverityToColor(LogSeverity severity) { + assert(severity >= 0 && severity < NUM_SEVERITIES); + GLogColor color = COLOR_DEFAULT; + switch (severity) { + case GLOG_INFO: + color = COLOR_DEFAULT; + break; + case GLOG_WARNING: + color = COLOR_YELLOW; + break; + case GLOG_ERROR: + case GLOG_FATAL: + color = COLOR_RED; + break; + default: + // should never get here. + assert(false); + } + return color; +} + +#ifdef OS_WINDOWS + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GLogColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. +const char* GetAnsiColorCode(GLogColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + case COLOR_DEFAULT: return ""; + }; + return NULL; // stop warning about return type. +} + +#endif // OS_WINDOWS + // Safely get max_log_size, overriding to 1 if it somehow gets defined as 0 static int32 MaxLogSize() { return (FLAGS_max_log_size > 0 ? FLAGS_max_log_size : 1); } +// An arbitrary limit on the length of a single log message. This +// is so that streaming can be done more efficiently. +const size_t LogMessage::kMaxLogMessageLen = 30000; + +struct LogMessage::LogMessageData { + LogMessageData(); + void reset(); + + int preserved_errno_; // preserved errno + // Buffer space; contains complete message text. + char message_text_[LogMessage::kMaxLogMessageLen+1]; + LogStream stream_; + char severity_; // What level is this LogMessage logged at? + int line_; // line number where logging call is. + void (LogMessage::*send_method_)(); // Call this in destructor to send + union { // At most one of these is used: union to keep the size low. + LogSink* sink_; // NULL or sink to send message to + std::vector* outvec_; // NULL or vector to push message onto + std::string* message_; // NULL or string to write message into + }; + time_t timestamp_; // Time of creation of LogMessage + struct ::tm tm_time_; // Time of creation of LogMessage + size_t num_prefix_chars_; // # of chars of prefix in this message + size_t num_chars_to_log_; // # of chars of msg to send to log + size_t num_chars_to_syslog_; // # of chars of msg to send to syslog + const char* basename_; // basename of file that called LOG + const char* fullname_; // fullname of file that called LOG + bool has_been_flushed_; // false => data has not been flushed + bool first_fatal_; // true => this was first fatal msg + + private: + LogMessageData(const LogMessageData&); + void operator=(const LogMessageData&); +}; + // A mutex that allows only one thread to log at a time, to keep things from // getting jumbled. Some other very uncommon logging operations (like // changing the destination file for log messages of a given severity) also @@ -271,6 +434,7 @@ class LogFileObject : public base::Logger { FILE* file_; LogSeverity severity_; uint32 bytes_since_flush_; + uint32 dropped_mem_length_; uint32 file_length_; unsigned int rollover_attempt_; int64 next_flush_time_; // cycle count at which to flush log @@ -278,7 +442,7 @@ class LogFileObject : public base::Logger { // Actually create a logfile using the value of base_filename_ and the // supplied argument time_pid_string // REQUIRES: lock_ is held - bool CreateLogfile(const char* time_pid_string); + bool CreateLogfile(const string& time_pid_string); }; } // namespace @@ -311,6 +475,9 @@ class LogDestination { static const int kNetworkBytes = 1400; static const string& hostname(); + static const bool& terminal_supports_color() { + return terminal_supports_color_; + } static void DeleteLogDestinations(); @@ -361,6 +528,7 @@ class LogDestination { static LogSeverity email_logging_severity_; static string addresses_; static string hostname_; + static bool terminal_supports_color_; // arbitrary global logging destinations. static vector* sinks_; @@ -382,6 +550,7 @@ string LogDestination::hostname_; vector* LogDestination::sinks_ = NULL; Mutex LogDestination::sink_mutex_; +bool LogDestination::terminal_supports_color_ = TerminalSupportsColor(); /* static */ const string& LogDestination::hostname() { @@ -404,7 +573,7 @@ inline void LogDestination::FlushLogFilesUnsafe(int min_severity) { // assume we have the log_mutex or we simply don't care // about it for (int i = min_severity; i < NUM_SEVERITIES; i++) { - LogDestination* log = log_destination(i); + LogDestination* log = log_destinations_[i]; if (log != NULL) { // Flush the base fileobject_ logger directly instead of going // through any wrappers to reduce chance of deadlock. @@ -502,6 +671,43 @@ 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; + + // 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); + return; + } +#ifdef OS_WINDOWS + const HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stderr_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, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + fwrite(message, len, 1, stderr); + fflush(stderr); + // Restores the text color. + SetConsoleTextAttribute(stderr_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. +#endif // OS_WINDOWS +} + 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. @@ -511,7 +717,7 @@ static void WriteToStderr(const char* message, size_t len) { inline void LogDestination::MaybeLogToStderr(LogSeverity severity, const char* message, size_t len) { if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) { - WriteToStderr(message, len); + ColoredWriteToStderr(severity, message, len); #ifdef OS_WINDOWS // On Windows, also output to the debugger ::OutputDebugStringA(string(message,len).c_str()); @@ -560,12 +766,12 @@ inline void LogDestination::LogToAllLogfiles(LogSeverity severity, const char* message, size_t len) { - if ( FLAGS_logtostderr ) // global flag: never log to file - WriteToStderr(message, len); - else + if ( FLAGS_logtostderr ) { // global flag: never log to file + ColoredWriteToStderr(severity, message, len); + } else { for (int i = severity; i >= 0; --i) LogDestination::MaybeLogToLogfile(i, timestamp, message, len); - + } } inline void LogDestination::LogToSinks(LogSeverity severity, @@ -614,6 +820,9 @@ void LogDestination::DeleteLogDestinations() { delete log_destinations_[severity]; log_destinations_[severity] = NULL; } + MutexLock l(&sink_mutex_); + delete sinks_; + sinks_ = NULL; } namespace { @@ -627,6 +836,7 @@ LogFileObject::LogFileObject(LogSeverity severity, file_(NULL), severity_(severity), bytes_since_flush_(0), + dropped_mem_length_(0), file_length_(0), rollover_attempt_(kRolloverAttemptFrequency-1), next_flush_time_(0) { @@ -690,11 +900,11 @@ void LogFileObject::FlushUnlocked(){ next_flush_time_ = CycleClock_Now() + UsecToCycles(next); } -bool LogFileObject::CreateLogfile(const char* time_pid_string) { +bool LogFileObject::CreateLogfile(const string& time_pid_string) { string string_filename = base_filename_+filename_extension_+ time_pid_string; const char* filename = string_filename.c_str(); - int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0664); + int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, FLAGS_logfile_mode); if (fd == -1) return false; #ifdef HAVE_FCNTL // Mark the file close-on-exec. We don't really care if this fails @@ -723,8 +933,10 @@ bool LogFileObject::CreateLogfile(const char* time_pid_string) { linkpath += linkname; unlink(linkpath.c_str()); // delete old one if it exists +#if defined(OS_WINDOWS) + // TODO(hamaji): Create lnk file on Windows? +#elif defined(HAVE_UNISTD_H) // We must have unistd.h. -#ifdef HAVE_UNISTD_H // Make the symlink be relative (in the same dir) so that if the // entire log directory gets relocated the link is still valid. const char *linkdest = slash ? (slash + 1) : filename; @@ -762,7 +974,7 @@ void LogFileObject::Write(bool force_flush, PidHasChanged()) { if (file_ != NULL) fclose(file_); file_ = NULL; - file_length_ = bytes_since_flush_ = 0; + file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0; rollover_attempt_ = kRolloverAttemptFrequency-1; } @@ -778,24 +990,24 @@ void LogFileObject::Write(bool force_flush, localtime_r(×tamp, &tm_time); // The logfile's filename will have the date/time & pid in it - char time_pid_string[256]; // More than enough chars for time, pid, \0 - ostrstream time_pid_stream(time_pid_string, sizeof(time_pid_string)); + ostringstream time_pid_stream; time_pid_stream.fill('0'); time_pid_stream << 1900+tm_time.tm_year - << setw(2) << 1+tm_time.tm_mon - << setw(2) << tm_time.tm_mday - << '-' - << setw(2) << tm_time.tm_hour - << setw(2) << tm_time.tm_min - << setw(2) << tm_time.tm_sec - << '.' - << GetMainThreadPid() - << '\0'; + << setw(2) << 1+tm_time.tm_mon + << setw(2) << tm_time.tm_mday + << '-' + << setw(2) << tm_time.tm_hour + << setw(2) << tm_time.tm_min + << setw(2) << tm_time.tm_sec + << '.' + << GetMainThreadPid(); + const string& time_pid_string = time_pid_stream.str(); if (base_filename_selected_) { if (!CreateLogfile(time_pid_string)) { perror("Could not create log file"); - fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n", time_pid_string); + fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n", + time_pid_string.c_str()); return; } } else { @@ -843,15 +1055,14 @@ void LogFileObject::Write(bool force_flush, // If we never succeeded, we have to give up if ( success == false ) { perror("Could not create logging file"); - fprintf(stderr, "COULD NOT CREATE A LOGGINGFILE %s!", time_pid_string); + fprintf(stderr, "COULD NOT CREATE A LOGGINGFILE %s!", + time_pid_string.c_str()); return; } } // Write a header message into the log file - char file_header_string[512]; // Enough chars for time and binary info - ostrstream file_header_stream(file_header_string, - sizeof(file_header_string)); + ostringstream file_header_stream; file_header_stream.fill('0'); file_header_stream << "Log file created at: " << 1900+tm_time.tm_year << '/' @@ -864,10 +1075,11 @@ void LogFileObject::Write(bool force_flush, << "Running on machine: " << LogDestination::hostname() << '\n' << "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu " - << "threadid file:line] msg" << '\n' - << '\0'; - int header_len = strlen(file_header_string); - fwrite(file_header_string, 1, header_len, file_); + << "threadid file:line] msg" << '\n'; + const string& file_header_string = file_header_stream.str(); + + const int header_len = file_header_string.size(); + fwrite(file_header_string.data(), 1, header_len, file_); file_length_ += header_len; bytes_since_flush_ += header_len; } @@ -902,11 +1114,17 @@ void LogFileObject::Write(bool force_flush, (CycleClock_Now() >= next_flush_time_) ) { FlushUnlocked(); #ifdef OS_LINUX - if (FLAGS_drop_log_memory) { - if (file_length_ >= logging::kPageSize) { - // don't evict the most recent page - uint32 len = file_length_ & ~(logging::kPageSize - 1); - posix_fadvise(fileno(file_), 0, len, POSIX_FADV_DONTNEED); + // Only consider files >= 3MiB + if (FLAGS_drop_log_memory && file_length_ >= (3 << 20)) { + // Don't evict the most recent 1-2MiB so as not to impact a tailer + // of the log file and to avoid page rounding issue on linux < 4.7 + uint32 total_drop_length = (file_length_ & ~((1 << 20) - 1)) - (1 << 20); + uint32 this_drop_length = total_drop_length - dropped_mem_length_; + if (this_drop_length >= (2 << 20)) { + // Only advise when >= 2MiB to drop + posix_fadvise(fileno(file_), dropped_mem_length_, this_drop_length, + POSIX_FADV_DONTNEED); + dropped_mem_length_ = total_drop_length; } } #endif @@ -915,9 +1133,6 @@ void LogFileObject::Write(bool force_flush, } // namespace -// An arbitrary limit on the length of a single log message. This -// is so that streaming can be done more efficiently. -const size_t LogMessage::kMaxLogMessageLen = 30000; // Static log data space to avoid alloc failures in a LOG(FATAL) // @@ -928,55 +1143,67 @@ const size_t LogMessage::kMaxLogMessageLen = 30000; static Mutex fatal_msg_lock; static CrashReason crash_reason; static bool fatal_msg_exclusive = true; -static char fatal_msg_buf_exclusive[LogMessage::kMaxLogMessageLen+1]; -static char fatal_msg_buf_shared[LogMessage::kMaxLogMessageLen+1]; -static LogMessage::LogStream fatal_msg_stream_exclusive( - fatal_msg_buf_exclusive, LogMessage::kMaxLogMessageLen, 0); -static LogMessage::LogStream fatal_msg_stream_shared( - fatal_msg_buf_shared, LogMessage::kMaxLogMessageLen, 0); -LogMessage::LogMessageData LogMessage::fatal_msg_data_exclusive_; -LogMessage::LogMessageData LogMessage::fatal_msg_data_shared_; +static LogMessage::LogMessageData fatal_msg_data_exclusive; +static LogMessage::LogMessageData fatal_msg_data_shared; + +#ifdef GLOG_THREAD_LOCAL_STORAGE +// Static thread-local log data space to use, because typically at most one +// LogMessageData object exists (in this case glog makes zero heap memory +// allocations). +static GLOG_THREAD_LOCAL_STORAGE bool thread_data_available = true; +static GLOG_THREAD_LOCAL_STORAGE LogMessage::LogMessageData thread_msg_data; +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) -LogMessage::LogMessageData::~LogMessageData() { - delete[] buf_; - delete stream_alloc_; +LogMessage::LogMessageData::LogMessageData() + : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) { +} + +void LogMessage::LogMessageData::reset() { + stream_.reset(); } LogMessage::LogMessage(const char* file, int line, LogSeverity severity, - int ctr, void (LogMessage::*send_method)()) { + int ctr, void (LogMessage::*send_method)()) + : allocated_(NULL) { Init(file, line, severity, send_method); - data_->stream_->set_ctr(ctr); + data_->stream_.set_ctr(ctr); } LogMessage::LogMessage(const char* file, int line, - const CheckOpString& result) { + const CheckOpString& result) + : allocated_(NULL) { Init(file, line, GLOG_FATAL, &LogMessage::SendToLog); stream() << "Check failed: " << (*result.str_) << " "; } -LogMessage::LogMessage(const char* file, int line) { +LogMessage::LogMessage(const char* file, int line) + : allocated_(NULL) { Init(file, line, GLOG_INFO, &LogMessage::SendToLog); } -LogMessage::LogMessage(const char* file, int line, LogSeverity severity) { +LogMessage::LogMessage(const char* file, int line, LogSeverity severity) + : allocated_(NULL) { Init(file, line, severity, &LogMessage::SendToLog); } LogMessage::LogMessage(const char* file, int line, LogSeverity severity, - LogSink* sink, bool also_send_to_log) { + LogSink* sink, bool also_send_to_log) + : allocated_(NULL) { Init(file, line, severity, also_send_to_log ? &LogMessage::SendToSinkAndLog : &LogMessage::SendToSink); data_->sink_ = sink; // override Init()'s setting to NULL } LogMessage::LogMessage(const char* file, int line, LogSeverity severity, - vector *outvec) { + vector *outvec) + : allocated_(NULL) { Init(file, line, severity, &LogMessage::SaveOrSendToLog); data_->outvec_ = outvec; // override Init()'s setting to NULL } LogMessage::LogMessage(const char* file, int line, LogSeverity severity, - string *message) { + string *message) + : allocated_(NULL) { Init(file, line, severity, &LogMessage::WriteToStringAndLog); data_->message_ = message; // override Init()'s setting to NULL } @@ -987,29 +1214,33 @@ void LogMessage::Init(const char* file, void (LogMessage::*send_method)()) { allocated_ = NULL; if (severity != GLOG_FATAL || !exit_on_dfatal) { +#ifdef GLOG_THREAD_LOCAL_STORAGE + // No need for locking, because this is thread local. + if (thread_data_available) { + thread_data_available = false; + data_ = &thread_msg_data; + // Make sure to clear log data since it may have been used and filled with + // data. We do not want to append the new message to the previous one. + data_->reset(); + } else { + allocated_ = new LogMessageData(); + data_ = allocated_; + } +#else // !defined(GLOG_THREAD_LOCAL_STORAGE) allocated_ = new LogMessageData(); data_ = allocated_; - data_->buf_ = new char[kMaxLogMessageLen+1]; - data_->message_text_ = data_->buf_; - data_->stream_alloc_ = - new LogStream(data_->message_text_, kMaxLogMessageLen, 0); - data_->stream_ = data_->stream_alloc_; +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) data_->first_fatal_ = false; } else { MutexLock l(&fatal_msg_lock); if (fatal_msg_exclusive) { fatal_msg_exclusive = false; - data_ = &fatal_msg_data_exclusive_; - data_->message_text_ = fatal_msg_buf_exclusive; - data_->stream_ = &fatal_msg_stream_exclusive; + data_ = &fatal_msg_data_exclusive; data_->first_fatal_ = true; } else { - data_ = &fatal_msg_data_shared_; - data_->message_text_ = fatal_msg_buf_shared; - data_->stream_ = &fatal_msg_stream_shared; + data_ = &fatal_msg_data_shared; data_->first_fatal_ = false; } - data_->stream_alloc_ = NULL; } stream().fill('0'); @@ -1050,7 +1281,7 @@ void LogMessage::Init(const char* file, << ' ' << data_->basename_ << ':' << data_->line_ << "] "; } - data_->num_prefix_chars_ = data_->stream_->pcount(); + data_->num_prefix_chars_ = data_->stream_.pcount(); if (!FLAGS_log_backtrace_at.empty()) { char fileline[128]; @@ -1067,16 +1298,28 @@ void LogMessage::Init(const char* file, LogMessage::~LogMessage() { Flush(); +#ifdef GLOG_THREAD_LOCAL_STORAGE + if (data_ == &thread_msg_data) + thread_data_available = true; +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) delete allocated_; } +int LogMessage::preserved_errno() const { + return data_->preserved_errno_; +} + +ostream& LogMessage::stream() { + return data_->stream_; +} + // Flush buffered message, called by the destructor, or any other function // that needs to synchronize the log. void LogMessage::Flush() { if (data_->has_been_flushed_ || data_->severity_ < FLAGS_minloglevel) return; - data_->num_chars_to_log_ = data_->stream_->pcount(); + data_->num_chars_to_log_ = data_->stream_.pcount(); data_->num_chars_to_syslog_ = data_->num_chars_to_log_ - data_->num_prefix_chars_; @@ -1126,7 +1369,7 @@ void LogMessage::Flush() { // Copy of first FATAL log message so that we can print it out again // after all the stack traces. To preserve legacy behavior, we don't -// use fatal_msg_buf_exclusive. +// use fatal_msg_data_exclusive. static time_t fatal_time; static char fatal_message[256]; @@ -1134,7 +1377,7 @@ void ReprintFatalMessage() { if (fatal_message[0]) { const int n = strlen(fatal_message); if (!FLAGS_logtostderr) { - // Also write to stderr + // Also write to stderr (don't color to avoid terminal checks) WriteToStderr(fatal_message, n); } LogDestination::LogToAllLogfiles(GLOG_ERROR, fatal_time, fatal_message, n); @@ -1163,7 +1406,8 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { // file if we haven't parsed the command line flags to get the // program name. if (FLAGS_logtostderr || !IsGoogleLoggingInitialized()) { - WriteToStderr(data_->message_text_, data_->num_chars_to_log_); + ColoredWriteToStderr(data_->severity_, + data_->message_text_, data_->num_chars_to_log_); // this could be protected by a flag if necessary. LogDestination::LogToSinks(data_->severity_, @@ -1235,10 +1479,10 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { void LogMessage::RecordCrashReason( glog_internal_namespace_::CrashReason* reason) { - reason->filename = fatal_msg_data_exclusive_.fullname_; - reason->line_number = fatal_msg_data_exclusive_.line_; - reason->message = fatal_msg_buf_exclusive + - fatal_msg_data_exclusive_.num_prefix_chars_; + reason->filename = fatal_msg_data_exclusive.fullname_; + reason->line_number = fatal_msg_data_exclusive.line_; + reason->message = fatal_msg_data_exclusive.message_text_ + + fatal_msg_data_exclusive.num_prefix_chars_; #ifdef HAVE_STACKTRACE // Retrieve the stack trace, omitting the logging frames that got us here. reason->depth = GetStackTrace(reason->stack, ARRAYSIZE(reason->stack), 4); @@ -1253,16 +1497,13 @@ void LogMessage::RecordCrashReason( # define ATTRIBUTE_NORETURN #endif +#if defined(OS_WINDOWS) +__declspec(noreturn) +#endif static void logging_fail() ATTRIBUTE_NORETURN; static void logging_fail() { -#if defined(_DEBUG) && defined(_MSC_VER) - // When debugging on windows, avoid the obnoxious dialog and make - // it possible to continue past a LOG(FATAL) in the debugger - _asm int 3 -#else abort(); -#endif } typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN; @@ -1365,8 +1606,13 @@ int64 LogMessage::num_messages(int severity) { // Output the COUNTER value. This is only valid if ostream is a // LogStream. ostream& operator<<(ostream &os, const PRIVATE_Counter&) { +#ifdef DISABLE_RTTI + LogMessage::LogStream *log = static_cast(&os); +#else LogMessage::LogStream *log = dynamic_cast(&os); - CHECK(log == log->self()); +#endif + CHECK(log && log == log->self()) + << "You must not use COUNTER with non-glog ostream"; os << log->ctr(); return os; } @@ -1380,9 +1626,8 @@ ErrnoLogMessage::ErrnoLogMessage(const char* file, int line, ErrnoLogMessage::~ErrnoLogMessage() { // Don't access errno directly because it may have been altered // while streaming the message. - char buf[100]; - posix_strerror_r(preserved_errno(), buf, sizeof(buf)); - stream() << ": " << buf << " [" << preserved_errno() << "]"; + stream() << ": " << StrError(preserved_errno()) << " [" + << preserved_errno() << "]"; } void FlushLogFiles(LogSeverity min_severity) { @@ -1511,13 +1756,11 @@ static bool SendEmailInternal(const char*dest, const char *subject, bool ok = pclose(pipe) != -1; if ( !ok ) { if ( use_logging ) { - char buf[100]; - posix_strerror_r(errno, buf, sizeof(buf)); - LOG(ERROR) << "Problems sending mail to " << dest << ": " << buf; + LOG(ERROR) << "Problems sending mail to " << dest << ": " + << StrError(errno); } else { - char buf[100]; - posix_strerror_r(errno, buf, sizeof(buf)); - fprintf(stderr, "Problems sending mail to %s: %s\n", dest, buf); + fprintf(stderr, "Problems sending mail to %s: %s\n", + dest, StrError(errno).c_str()); } } return ok; @@ -1564,7 +1807,7 @@ static void GetTempDirectories(vector* list) { "/tmp", }; - for (int i = 0; i < ARRAYSIZE(candidates); i++) { + for (size_t i = 0; i < ARRAYSIZE(candidates); i++) { const char *d = candidates[i]; if (!d) continue; // Empty env var @@ -1639,8 +1882,11 @@ void TruncateLogFile(const char *path, int64 limit, int64 keep) { int64 read_offset, write_offset; // Don't follow symlinks unless they're our own fd symlinks in /proc int flags = O_RDWR; + // TODO(hamaji): Support other environments. +#ifdef OS_LINUX const char *procfd_prefix = "/proc/self/fd/"; if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW; +#endif int fd = open(path, flags); if (fd == -1) { @@ -1725,11 +1971,11 @@ void TruncateStdoutStderr() { bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2)); \ if (equal == expected) return NULL; \ else { \ - strstream ss; \ + ostringstream ss; \ if (!s1) s1 = ""; \ if (!s2) s2 = ""; \ ss << #name " failed: " << names << " (" << s1 << " vs. " << s2 << ")"; \ - return new string(ss.str(), ss.pcount()); \ + return new string(ss.str()); \ } \ } DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true) @@ -1788,6 +2034,15 @@ int posix_strerror_r(int err, char *buf, size_t len) { } } +string StrError(int err) { + char buf[100]; + int rc = posix_strerror_r(err, buf, sizeof(buf)); + if ((rc < 0) || (buf[0] == '\000')) { + snprintf(buf, sizeof(buf), "Error number %d", err); + } + return buf; +} + LogMessageFatal::LogMessageFatal(const char* file, int line) : LogMessage(file, line, GLOG_FATAL) {} @@ -1800,6 +2055,56 @@ LogMessageFatal::~LogMessageFatal() { LogMessage::Fail(); } +namespace base { + +CheckOpMessageBuilder::CheckOpMessageBuilder(const char *exprtext) + : stream_(new ostringstream) { + *stream_ << exprtext << " ("; +} + +CheckOpMessageBuilder::~CheckOpMessageBuilder() { + delete stream_; +} + +ostream* CheckOpMessageBuilder::ForVar2() { + *stream_ << " vs. "; + return stream_; +} + +string* CheckOpMessageBuilder::NewString() { + *stream_ << ")"; + return new string(stream_->str()); +} + +} // namespace base + +template <> +void MakeCheckOpValueString(std::ostream* os, const char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "char value " << (short)v; + } +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const signed char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "signed char value " << (short)v; + } +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const unsigned char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "unsigned char value " << (unsigned short)v; + } +} + void InitGoogleLogging(const char* argv0) { glog_internal_namespace_::InitGoogleLoggingUtilities(argv0); }