+// Copyright (c) 1999, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
#define _GNU_SOURCE 1 // needed for O_NOFOLLOW and pread()/pwrite()
#include "utilities.h"
+#include <algorithm>
#include <assert.h>
#include <iomanip>
#include <string>
#include "glog/raw_logging.h"
#include "base/googleinit.h"
+#ifdef HAVE_STACKTRACE
+# include "stacktrace.h"
+#endif
+
using std::string;
using std::vector;
-using std::ostrstream;
using std::setw;
+using std::setfill;
using std::hex;
using std::dec;
using std::min;
using std::ostream;
using std::ostringstream;
-using std::strstream;
-DEFINE_bool(logtostderr, false,
- "log messages go to stderr instead of logfiles");
-DEFINE_bool(alsologtostderr, false,
- "log messages go to stderr in addition to logfiles");
+using std::FILE;
+using std::fwrite;
+using std::fclose;
+using std::fflush;
+using std::fprintf;
+using std::perror;
+
+#ifdef __QNX__
+using std::fdopen;
+#endif
+
+// There is no thread annotation support.
+#define EXCLUSIVE_LOCKS_REQUIRED(mu)
+
+static bool BoolFromEnv(const char *varname, bool defval) {
+ const char* const valstr = getenv(varname);
+ if (!valstr) {
+ return defval;
+ }
+ return memchr("tTyY1\0", valstr[0], 6) != NULL;
+}
+
+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
// well as the file.
// The default is ERROR instead of FATAL so that users can see problems
// when they run a program without having to look in another file.
DEFINE_int32(stderrthreshold,
- GOOGLE_NAMESPACE::ERROR,
+ GOOGLE_NAMESPACE::GLOG_ERROR,
"log messages at or above this level are copied to stderr in "
"addition to logfiles. This flag obsoletes --alsologtostderr.");
-DEFINE_string(alsologtoemail, "",
- "log messages go to these email addresses "
- "in addition to logfiles");
-DEFINE_bool(log_prefix, true,
- "Prepend the log prefix to the start of each log line");
-DEFINE_int32(minloglevel, 0, "Messages logged at a lower level than this don't "
- "actually get logged anywhere");
-DEFINE_int32(logbuflevel, 0,
- "Buffer log messages logged at this level or lower"
- " (-1 means don't buffer; 0 means buffer INFO only;"
- " ...)");
-DEFINE_int32(logbufsecs, 30,
- "Buffer log messages for at most this many seconds");
-DEFINE_int32(logemaillevel, 999,
- "Email log messages logged at this level or higher"
- " (0 means email all; 3 means email FATAL only;"
- " ...)");
-DEFINE_string(logmailer, "/bin/mail",
- "Mailer used to send logging email");
+GLOG_DEFINE_string(alsologtoemail, "",
+ "log messages go to these email addresses "
+ "in addition to logfiles");
+GLOG_DEFINE_bool(log_prefix, true,
+ "Prepend the log prefix to the start of each log line");
+GLOG_DEFINE_int32(minloglevel, 0, "Messages logged at a lower level than this don't "
+ "actually get logged anywhere");
+GLOG_DEFINE_int32(logbuflevel, 0,
+ "Buffer log messages logged at this level or lower"
+ " (-1 means don't buffer; 0 means buffer INFO only;"
+ " ...)");
+GLOG_DEFINE_int32(logbufsecs, 30,
+ "Buffer log messages for at most this many seconds");
+GLOG_DEFINE_int32(logemaillevel, 999,
+ "Email log messages logged at this level or higher"
+ " (0 means email all; 3 means email FATAL only;"
+ " ...)");
+GLOG_DEFINE_string(logmailer, "/bin/mail",
+ "Mailer used to send logging email");
// Compute the default value for --log_dir
static const char* DefaultLogDir() {
return "";
}
-DEFINE_string(log_dir, DefaultLogDir(),
- "If specified, logfiles are written into this directory instead "
- "of the default logging directory.");
-DEFINE_string(log_link, "", "Put additional links to the log "
- "files in this directory");
+GLOG_DEFINE_string(log_dir, DefaultLogDir(),
+ "If specified, logfiles are written into this directory instead "
+ "of the default logging directory.");
+GLOG_DEFINE_string(log_link, "", "Put additional links to the log "
+ "files in this directory");
+
+GLOG_DEFINE_int32(max_log_size, 1800,
+ "approx. maximum log file size (in MB). A value of 0 will "
+ "be silently overridden to 1.");
-DEFINE_int32(max_log_size, 1800,
- "approx. maximum log file size (in MB)");
+GLOG_DEFINE_bool(stop_logging_if_full_disk, false,
+ "Stop attempting to log to disk if the disk is full.");
-DEFINE_bool(stop_logging_if_full_disk, false,
- "Stop attempting to log to disk if the disk is full.");
+GLOG_DEFINE_string(log_backtrace_at, "",
+ "Emit a backtrace when logging at file:linenum.");
// TODO(hamaji): consider windows
#define PATH_SEPARATOR '/'
+#ifndef HAVE_PREAD
+#if defined(OS_WINDOWS)
+#include <BaseTsd.h>
+#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;
}
*hostname = buf.nodename;
#elif defined(OS_WINDOWS)
- char buf[256];
- DWORD len;
+ char buf[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerNameA(buf, &len)) {
*hostname = buf;
} else {
#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();
+
+ 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<std::string>* 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
"INFO", "WARNING", "ERROR", "FATAL"
};
+// Has the user called SetExitOnDFatal(true)?
+static bool exit_on_dfatal = true;
+
const char* GetLogSeverityName(LogSeverity severity) {
return LogSeverityNames[severity];
}
// 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
static const int kNetworkBytes = 1400;
static const string& hostname();
- private:
+ static const bool& terminal_supports_color() {
+ return terminal_supports_color_;
+ }
+
+ static void DeleteLogDestinations();
+ private:
LogDestination(LogSeverity severity, const char* base_filename);
~LogDestination() { }
static LogSeverity email_logging_severity_;
static string addresses_;
static string hostname_;
+ static bool terminal_supports_color_;
// arbitrary global logging destinations.
static vector<LogSink*>* sinks_;
// Errors do not get logged to email by default.
LogSeverity LogDestination::email_logging_severity_ = 99999;
-string LogDestination::addresses_ = "";
-string LogDestination::hostname_ = "";
+string LogDestination::addresses_;
+string LogDestination::hostname_;
vector<LogSink*>* LogDestination::sinks_ = NULL;
Mutex LogDestination::sink_mutex_;
+bool LogDestination::terminal_supports_color_ = TerminalSupportsColor();
/* static */
const string& LogDestination::hostname() {
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.
- write(STDERR_FILENO, message, len);
+ fwrite(message, len, 1, stderr);
}
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());
to += addresses_;
}
const string subject(string("[LOG] ") + LogSeverityNames[severity] + ": " +
- ProgramInvocationShortName());
+ glog_internal_namespace_::ProgramInvocationShortName());
string body(hostname());
body += "\n\n";
body.append(message, len);
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,
(*sinks_)[i]->WaitTillSent();
}
}
- if (data->send_method_ == &LogMessage::SendToSinkAndLog &&
- data->sink_ != NULL) {
+ const bool send_to_sink =
+ (data->send_method_ == &LogMessage::SendToSink) ||
+ (data->send_method_ == &LogMessage::SendToSinkAndLog);
+ if (send_to_sink && data->sink_ != NULL) {
data->sink_->WaitTillSent();
}
}
return log_destinations_[severity];
}
+void LogDestination::DeleteLogDestinations() {
+ for (int severity = 0; severity < NUM_SEVERITIES; ++severity) {
+ delete log_destinations_[severity];
+ log_destinations_[severity] = NULL;
+ }
+}
+
namespace {
LogFileObject::LogFileObject(LogSeverity severity,
const char* base_filename)
: base_filename_selected_(base_filename != NULL),
base_filename_((base_filename != NULL) ? base_filename : ""),
- symlink_basename_(ProgramInvocationShortName()),
+ symlink_basename_(glog_internal_namespace_::ProgramInvocationShortName()),
filename_extension_(),
file_(NULL),
severity_(severity),
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();
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;
- symlink(linkdest, linkpath.c_str()); // silently ignore failures
+ if (symlink(linkdest, linkpath.c_str()) != 0) {
+ // silently ignore failures
+ }
// Make an additional link to the log file in a place specified by
// FLAGS_log_link, if indicated
if (!FLAGS_log_link.empty()) {
linkpath = FLAGS_log_link + "/" + linkname;
unlink(linkpath.c_str()); // delete old one if it exists
- symlink(filename, linkpath.c_str()); // silently ignore failures
+ if (symlink(filename, linkpath.c_str()) != 0) {
+ // silently ignore failures
+ }
}
#endif
}
return;
}
- if (static_cast<int>(file_length_ >> 20) >= FLAGS_max_log_size) {
- fclose(file_);
+ if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() ||
+ PidHasChanged()) {
+ if (file_ != NULL) fclose(file_);
file_ = NULL;
file_length_ = bytes_since_flush_ = 0;
rollover_attempt_ = kRolloverAttemptFrequency-1;
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 {
//
// Where does the file get put? Successively try the directories
// "/tmp", and "."
- string stripped_filename(ProgramInvocationShortName()); // in cmdlineflag
+ string stripped_filename(
+ glog_internal_namespace_::ProgramInvocationShortName());
string hostname;
GetHostName(&hostname);
// 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 << '/'
<< setw(2) << tm_time.tm_sec << '\n'
<< "Running on machine: "
<< LogDestination::hostname() << '\n'
- << '\0';
- int header_len = strlen(file_header_string);
- fwrite(file_header_string, 1, header_len, file_);
+ << "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu "
+ << "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;
}
(bytes_since_flush_ >= 1000000) ||
(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);
+ }
+ }
+#endif
}
}
} // namespace
-//
-// LogMessage's constructor starts each message with a string like:
-// I1018 160715 logging.cc:1153]
-// (1st letter of severity level, GMT month, date, & time;
-// thread id (if not 0x400); basename of file and line of the logging command)
-// We ignore thread id 0x400 because it seems to be the default for single-
-// threaded programs.
-// 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)
+//
+// Since multiple threads may call LOG(FATAL), and we want to preserve
+// the data from the first call, we allocate two sets of space. One
+// for exclusive use by the first thread, and one for shared use by
+// all other threads.
+static Mutex fatal_msg_lock;
+static CrashReason crash_reason;
+static bool fatal_msg_exclusive = true;
+static LogMessage::LogMessageData fatal_msg_data_exclusive;
+static LogMessage::LogMessageData fatal_msg_data_shared;
-// Need to reserve some space for FATAL messages because
-// we usually do LOG(FATAL) when we ran out of heap memory.
-// However, since LogMessage() also calls new[], in this case,
-// it will recusively call LOG(FATAL), which then call new[] ... etc.
-// Eventually, the program will run out of stack memory and no message
-// will get logged. Note that we will not be protecting this buffer
-// with a lock because the chances are very small that there will
-// be a contention during LOG(FATAL) which can only happen at most
-// once per program.
-static char fatal_message_buffer[LogMessage::kMaxLogMessageLen+1];
-
-// Similarly we reserve space for a LogMessageData struct to be used
-// for FATAL messages.
-LogMessage::LogMessageData LogMessage::fatal_message_data_(0, FATAL, 0);
-
-LogMessage::LogMessageData::LogMessageData(int preserved_errno,
- LogSeverity severity,
- int ctr) :
- // ORDER DEPENDENCY: buf_ comes before message_text_ comes before stream_
- preserved_errno_(preserved_errno),
- // Use static buffer for LOG(FATAL)
- buf_((severity != FATAL) ? new char[kMaxLogMessageLen+1] : NULL),
- message_text_((severity != FATAL) ? buf_ : fatal_message_buffer),
- stream_(message_text_, kMaxLogMessageLen, ctr),
- severity_(severity) {
-}
-
-LogMessage::LogMessageData::~LogMessageData() {
- delete[] buf_;
-}
-
-LogMessage::LogMessageData* LogMessage::GetMessageData(int preserved_errno,
- LogSeverity severity,
- int ctr) {
- if (severity != FATAL) {
- return allocated_;
- } else {
- fatal_message_data_.preserved_errno_ = preserved_errno;
- fatal_message_data_.stream_.set_ctr(ctr);
- return &fatal_message_data_;
- }
+LogMessage::LogMessageData::LogMessageData()
+ : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {
}
LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
- int ctr, void (LogMessage::*send_method)()) :
- allocated_((severity != FATAL) ?
- new LogMessageData(errno, severity, ctr) : NULL),
- data_(GetMessageData(errno, severity, ctr)) {
+ int ctr, void (LogMessage::*send_method)())
+ : allocated_(NULL) {
Init(file, line, severity, send_method);
+ data_->stream_.set_ctr(ctr);
}
-LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
- : allocated_(NULL),
- data_(GetMessageData(errno, FATAL, 0)) {
- Init(file, line, FATAL, &LogMessage::SendToLog);
+LogMessage::LogMessage(const char* file, int line,
+ const CheckOpString& result)
+ : allocated_(NULL) {
+ Init(file, line, GLOG_FATAL, &LogMessage::SendToLog);
stream() << "Check failed: " << (*result.str_) << " ";
}
-LogMessage::LogMessage(const char* file, int line) :
- allocated_(new LogMessageData(errno, INFO, 0)),
- data_(GetMessageData(errno, INFO, 0)) {
- Init(file, line, INFO, &LogMessage::SendToLog);
+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) :
- allocated_((severity != FATAL) ?
- new LogMessageData(errno, severity, 0) : NULL),
- data_(GetMessageData(errno, severity, 0)) {
+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) :
- allocated_((severity != FATAL) ?
- new LogMessageData(errno, severity, 0) : NULL),
- data_(GetMessageData(errno, severity, 0)) {
- Init(file, line, severity, &LogMessage::SendToSinkAndLog);
+ 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<string> *outvec) :
- allocated_((severity != FATAL) ?
- new LogMessageData(errno, severity, 0) : NULL),
- data_(GetMessageData(errno, severity, 0)) {
+ vector<string> *outvec)
+ : allocated_(NULL) {
Init(file, line, severity, &LogMessage::SaveOrSendToLog);
data_->outvec_ = outvec; // override Init()'s setting to NULL
}
-void LogMessage::Init(const char* file, int line, LogSeverity severity,
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+ string *message)
+ : allocated_(NULL) {
+ Init(file, line, severity, &LogMessage::WriteToStringAndLog);
+ data_->message_ = message; // override Init()'s setting to NULL
+}
+
+void LogMessage::Init(const char* file,
+ int line,
+ LogSeverity severity,
void (LogMessage::*send_method)()) {
+ allocated_ = NULL;
+ if (severity != GLOG_FATAL || !exit_on_dfatal) {
+ allocated_ = new LogMessageData();
+ data_ = allocated_;
+ data_->first_fatal_ = false;
+ } else {
+ MutexLock l(&fatal_msg_lock);
+ if (fatal_msg_exclusive) {
+ fatal_msg_exclusive = false;
+ data_ = &fatal_msg_data_exclusive;
+ data_->first_fatal_ = true;
+ } else {
+ data_ = &fatal_msg_data_shared;
+ data_->first_fatal_ = false;
+ }
+ }
+
+ stream().fill('0');
+ data_->preserved_errno_ = errno;
+ data_->severity_ = severity;
data_->line_ = line;
data_->send_method_ = send_method;
-
- data_->outvec_ = NULL;
data_->sink_ = NULL;
- data_->timestamp_ = time(NULL);
+ data_->outvec_ = NULL;
+ WallTime now = WallTime_Now();
+ data_->timestamp_ = static_cast<time_t>(now);
localtime_r(&data_->timestamp_, &data_->tm_time_);
- RawLog__SetLastTime(data_->tm_time_);
+ int usecs = static_cast<int>((now - data_->timestamp_) * 1000000);
+ RawLog__SetLastTime(data_->tm_time_, usecs);
+
+ data_->num_chars_to_log_ = 0;
+ data_->num_chars_to_syslog_ = 0;
data_->basename_ = const_basename(file);
data_->fullname_ = file;
- data_->stream_.fill('0');
+ data_->has_been_flushed_ = false;
- // In some cases, we use logging like a print mechanism and
- // the prefixes get in the way
+ // If specified, prepend a prefix to each line. For example:
+ // I1018 160715 f5d4fbb0 logging.cc:1153]
+ // (log level, GMT month, date, time, thread_id, file basename, line)
+ // We exclude the thread_id for the default thread.
if (FLAGS_log_prefix && (line != kNoLogPrefix)) {
- if ( is_default_thread() ) {
- stream() << LogSeverityNames[severity][0]
- << setw(2) << 1+data_->tm_time_.tm_mon
- << setw(2) << data_->tm_time_.tm_mday
- << ' '
- << setw(2) << data_->tm_time_.tm_hour
- << setw(2) << data_->tm_time_.tm_min
- << setw(2) << data_->tm_time_.tm_sec
- << ' ' << data_->basename_ << ':' << data_->line_ << "] ";
- } else {
- stream() << LogSeverityNames[severity][0]
- << setw(2) << 1+data_->tm_time_.tm_mon
- << setw(2) << data_->tm_time_.tm_mday
- << ' '
- << setw(2) << data_->tm_time_.tm_hour
- << setw(2) << data_->tm_time_.tm_min
- << setw(2) << data_->tm_time_.tm_sec
- << ' ' << setw(8) << std::hex << pthread_self() << std::dec
- << ' ' << data_->basename_ << ':' << data_->line_ << "] ";
- }
+ stream() << LogSeverityNames[severity][0]
+ << setw(2) << 1+data_->tm_time_.tm_mon
+ << setw(2) << data_->tm_time_.tm_mday
+ << ' '
+ << setw(2) << data_->tm_time_.tm_hour << ':'
+ << setw(2) << data_->tm_time_.tm_min << ':'
+ << setw(2) << data_->tm_time_.tm_sec << "."
+ << setw(6) << usecs
+ << ' '
+ << setfill(' ') << setw(5)
+ << static_cast<unsigned int>(GetTID()) << setfill('0')
+ << ' '
+ << data_->basename_ << ':' << data_->line_ << "] ";
}
-
data_->num_prefix_chars_ = data_->stream_.pcount();
- data_->has_been_flushed_ = false;
+
+ if (!FLAGS_log_backtrace_at.empty()) {
+ char fileline[128];
+ snprintf(fileline, sizeof(fileline), "%s:%d", data_->basename_, line);
+#ifdef HAVE_STACKTRACE
+ if (!strcmp(FLAGS_log_backtrace_at.c_str(), fileline)) {
+ string stacktrace;
+ DumpStackTraceToString(&stacktrace);
+ stream() << " (stacktrace:\n" << stacktrace << ") ";
+ }
+#endif
+ }
}
LogMessage::~LogMessage() {
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() {
// Do we need to add a \n to the end of this message?
bool append_newline =
- (data_->message_text_[data_->num_chars_to_log_-1] != '\n');
+ (data_->message_text_[data_->num_chars_to_log_-1] != '\n');
char original_final_char = '\0';
// If we do need to add a \n, we'll do it by violating the memory of the
data_->has_been_flushed_ = true;
}
-// Copy of FATAL log message so that we can print it out again after
-// all the stack traces.
-// We cannot simply use fatal_message_buffer, because two or more FATAL log
-// messages may happen in a row. This is a real possibility given that
-// FATAL log messages are often associated with corrupted process state.
-// In this case, we still want to reprint the first FATAL log message, so
-// we need to save away the first message in a separate buffer.
+// 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_data_exclusive.
static time_t fatal_time;
static char fatal_message[256];
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(ERROR, fatal_time, fatal_message, n);
+ LogDestination::LogToAllLogfiles(GLOG_ERROR, fatal_time, fatal_message, n);
}
}
// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SendToLog() {
+void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
static bool already_warned_before_initgoogle = false;
log_mutex.AssertHeld();
// 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_,
// If we log a FATAL message, flush all the log destinations, then toss
// a signal for others to catch. We leave the logs in a state that
// someone else can use them (as long as they flush afterwards)
- if (data_->severity_ == FATAL) {
- // save away the fatal message so we can print it again later
- const int copy = min<int>(data_->num_chars_to_log_,
- sizeof(fatal_message)-1);
- memcpy(fatal_message, data_->message_text_, copy);
- fatal_message[copy] = '\0';
- fatal_time = data_->timestamp_;
+ if (data_->severity_ == GLOG_FATAL && exit_on_dfatal) {
+ if (data_->first_fatal_) {
+ // Store crash information so that it is accessible from within signal
+ // handlers that may be invoked later.
+ RecordCrashReason(&crash_reason);
+ SetCrashReason(&crash_reason);
+
+ // Store shortened fatal message for other logs and GWQ status
+ const int copy = min<int>(data_->num_chars_to_log_,
+ sizeof(fatal_message)-1);
+ memcpy(fatal_message, data_->message_text_, copy);
+ fatal_message[copy] = '\0';
+ fatal_time = data_->timestamp_;
+ }
if (!FLAGS_logtostderr) {
for (int i = 0; i < NUM_SEVERITIES; ++i) {
LogDestination::log_destinations_[i]->logger_->Write(true, 0, "", 0);
}
}
-
+
// release the lock that our caller (directly or indirectly)
// LogMessage::~LogMessage() grabbed so that signal handlers
// can use the logging facility. Alternately, we could add
log_mutex.Unlock();
LogDestination::WaitForSinks(data_);
+ const char* message = "*** Check failure stack trace: ***\n";
+ if (write(STDERR_FILENO, message, strlen(message)) < 0) {
+ // Ignore errors.
+ }
Fail();
}
}
+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_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);
+#else
+ reason->depth = 0;
+#endif
+}
+
+#ifdef HAVE___ATTRIBUTE__
+# define ATTRIBUTE_NORETURN __attribute__((noreturn))
+#else
+# define ATTRIBUTE_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
+ __debugbreak();
#else
abort();
#endif
}
-#ifdef HAVE___ATTRIBUTE__
+typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;
+
GOOGLE_GLOG_DLL_DECL
-void (*g_logging_fail_func)() __attribute__((noreturn)) = &logging_fail;
-#else
-GOOGLE_GLOG_DLL_DECL void (*g_logging_fail_func)() = &logging_fail;
-#endif
+logging_fail_func_t g_logging_fail_func = &logging_fail;
void InstallFailureFunction(void (*fail_func)()) {
- g_logging_fail_func = fail_func;
+ g_logging_fail_func = (logging_fail_func_t)fail_func;
}
void LogMessage::Fail() {
}
// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SendToSinkAndLog() {
+void LogMessage::SendToSink() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
if (data_->sink_ != NULL) {
RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");
(data_->num_chars_to_log_ -
data_->num_prefix_chars_ - 1));
}
+}
+
+// L >= log_mutex (callers must hold the log_mutex).
+void LogMessage::SendToSinkAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
+ SendToSink();
SendToLog();
}
// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SaveOrSendToLog() {
+void LogMessage::SaveOrSendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
if (data_->outvec_ != NULL) {
RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");
}
}
+void LogMessage::WriteToStringAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
+ if (data_->message_ != NULL) {
+ RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
+ data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");
+ // Omit prefix of message and trailing newline when writing to message_.
+ const char *start = data_->message_text_ + data_->num_prefix_chars_;
+ int len = data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1;
+ data_->message_->assign(start, len);
+ }
+ SendToLog();
+}
+
// L >= log_mutex (callers must hold the log_mutex).
void LogMessage::SendToSyslogAndLog() {
#ifdef HAVE_SYSLOG_H
// Before any calls to syslog(), make a single call to openlog()
static bool openlog_already_called = false;
if (!openlog_already_called) {
- openlog(ProgramInvocationShortName(), LOG_CONS | LOG_NDELAY | LOG_PID,
+ openlog(glog_internal_namespace_::ProgramInvocationShortName(),
+ LOG_CONS | LOG_NDELAY | LOG_PID,
LOG_USER);
openlog_already_called = true;
}
// 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<LogMessage::LogStream*>(&os);
+#else
LogMessage::LogStream *log = dynamic_cast<LogMessage::LogStream*>(&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;
}
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) {
ostringstream stream(string(message, message_len));
stream.fill('0');
- if ( is_default_thread() ) {
- stream << LogSeverityNames[severity][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
- << ' ' << file << ':' << line << "] ";
- } else {
- stream << LogSeverityNames[severity][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
- << ' ' << setw(8) << std::hex << pthread_self() << std::dec
- << ' ' << file << ':' << line << "] ";
- }
+ // FIXME(jrvb): Updating this to use the correct value for usecs
+ // requires changing the signature for both this method and
+ // LogSink::send(). This change needs to be done in a separate CL
+ // so subclasses of LogSink can be updated at the same time.
+ int usecs = 0;
+
+ stream << LogSeverityNames[severity][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 << '.'
+ << setw(6) << usecs
+ << ' '
+ << setfill(' ') << setw(5) << GetTID() << setfill('0')
+ << ' '
+ << file << ':' << line << "] ";
stream << string(message, message_len);
return stream.str();
LogDestination::LogToStderr();
}
+namespace base {
+namespace internal {
+
+bool GetExitOnDFatal() {
+ MutexLock l(&log_mutex);
+ return exit_on_dfatal;
+}
+
+// Determines whether we exit the program for a LOG(DFATAL) message in
+// debug mode. It does this by skipping the call to Fail/FailQuietly.
+// This is intended for testing only.
+//
+// This can have some effects on LOG(FATAL) as well. Failure messages
+// are always allocated (rather than sharing a buffer), the crash
+// reason is not recorded, the "gwq" status message is not updated,
+// and the stack trace is not recorded. The LOG(FATAL) *will* still
+// exit the program. Since this function is used only in testing,
+// these differences are acceptable.
+void SetExitOnDFatal(bool value) {
+ MutexLock l(&log_mutex);
+ exit_on_dfatal = value;
+}
+
+} // namespace internal
+} // namespace base
+
// use_logging controls whether the logging functions LOG/VLOG are used
// to log errors. It should be set to false when the caller holds the
// log_mutex.
subject, body, dest);
}
- string cmd(FLAGS_logmailer);
- cmd = cmd + " -s\"" + string(subject) + "\" " + string(dest);
+ string cmd =
+ FLAGS_logmailer + " -s\"" + subject + "\" " + dest;
FILE* pipe = popen(cmd.c_str(), "w");
if (pipe != NULL) {
// Add the body if we have one
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;
"/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
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) {
// rather scary.
// Instead just truncate the file to something we can manage
if (truncate(path, 0) == -1) {
- PLOG(ERROR) << "Unable to truncate " << path;
+ PLOG(ERROR) << "Unable to truncate " << path;
} else {
- LOG(ERROR) << "Truncated " << path << " due to EFBIG error";
+ LOG(ERROR) << "Truncated " << path << " due to EFBIG error";
}
} else {
PLOG(ERROR) << "Unable to open " << path;
}
return;
}
-
+
if (fstat(fd, &statbuf) == -1) {
- PLOG(ERROR) << "Unable to fstat()";
- goto out_close_fd;
+ PLOG(ERROR) << "Unable to fstat()";
+ goto out_close_fd;
}
// See if the path refers to a regular file bigger than the
// This log file is too large - we need to truncate it
LOG(INFO) << "Truncating " << path << " to " << keep << " bytes";
-
+
// Copy the last "keep" bytes of the file to the beginning of the file
- read_offset = statbuf.st_size - keep;
+ read_offset = statbuf.st_size - keep;
write_offset = 0;
int bytesin, bytesout;
while ((bytesin = pread(fd, copybuf, sizeof(copybuf), read_offset)) > 0) {
write_offset += bytesout;
}
if (bytesin == -1) PLOG(ERROR) << "Unable to read from " << path;
-
+
// Truncate the remainder of the file. If someone else writes to the
// end of the file after our last read() above, we lose their latest
// data. Too bad ...
void TruncateStdoutStderr() {
#ifdef HAVE_UNISTD_H
- int64 limit = FLAGS_max_log_size << 20;
+ int64 limit = MaxLogSize() << 20;
int64 keep = 1 << 20;
TruncateLogFile("/proc/self/fd/1", limit, keep);
TruncateLogFile("/proc/self/fd/2", limit, keep);
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)
return 0;
} else {
buf[0] = '\000';
-#if defined(OS_MACOSX) || defined(OS_FREEBSD)
- if (reinterpret_cast<int>(rc) < sys_nerr) {
+#if defined(OS_MACOSX) || defined(OS_FREEBSD) || defined(OS_OPENBSD)
+ if (reinterpret_cast<intptr_t>(rc) < sys_nerr) {
// This means an error on MacOSX or FreeBSD.
return -1;
}
}
}
+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, FATAL) {}
+ LogMessage(file, line, GLOG_FATAL) {}
LogMessageFatal::LogMessageFatal(const char* file, int line,
const CheckOpString& result) :
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);
+}
+
+void ShutdownGoogleLogging() {
+ glog_internal_namespace_::ShutdownGoogleLoggingUtilities();
+ LogDestination::DeleteLogDestinations();
+ delete logging_directories_list;
+ logging_directories_list = NULL;
+}
+
_END_GOOGLE_NAMESPACE_