diff --git a/AUTHORS b/AUTHORS index e5111cea7..1780c12ba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,6 +15,7 @@ Andy Ying Brian Silverman Google Inc. Guillaume Dumont +Marco Wang Michael Tanner MiniLight romange diff --git a/CMakeLists.txt b/CMakeLists.txt index 4254c6b09..60109cbac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ option (WITH_TLS "Enable Thread Local Storage (TLS) support" ON) option (BUILD_SHARED_LIBS "Build shared libraries" OFF) option (PRINT_UNSYMBOLIZED_STACK_TRACES "Print raw pc values on symbolization failure" OFF) +option (WITH_PKGCONFIG " Enable pkg-config support" ON) list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) @@ -423,6 +424,25 @@ configure_file (src/glog/logging.h.in glog/logging.h @ONLY) configure_file (src/glog/raw_logging.h.in glog/raw_logging.h @ONLY) configure_file (src/glog/stl_logging.h.in glog/stl_logging.h @ONLY) configure_file (src/glog/vlog_is_on.h.in glog/vlog_is_on.h @ONLY) +if (WITH_PKGCONFIG) + set(VERSION ${PROJECT_VERSION}) + set(prefix ${CMAKE_INSTALL_PREFIX}) + set(exec_prefix ${CMAKE_INSTALL_FULL_BINDIR}) + set(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) + set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) + + configure_file ( + "${PROJECT_SOURCE_DIR}/libglog.pc.in" + "${PROJECT_BINARY_DIR}/libglog.pc" + @ONLY + ) + + unset(VERSION) + unset(prefix) + unset(exec_prefix) + unset(libdir) + unset(includedir) +endif (WITH_PKGCONFIG) set (CMAKE_CXX_VISIBILITY_PRESET default) set (CMAKE_VISIBILITY_INLINES_HIDDEN 1) @@ -649,6 +669,13 @@ install (TARGETS glog LIBRARY DESTINATION ${_glog_CMake_LIBDIR} ARCHIVE DESTINATION ${_glog_CMake_LIBDIR}) +if (WITH_PKGCONFIG) + install ( + FILES "${PROJECT_BINARY_DIR}/libglog.pc" + DESTINATION "${_glog_CMake_LIBDIR}/pkgconfig" + ) +endif (WITH_PKGCONFIG) + set (glog_CMake_VERSION 3.0) if (gflags_FOUND) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index d63f62d16..f4b011f79 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -31,7 +31,9 @@ Fumitoshi Ukai Guillaume Dumont HÃ¥kan L. S. Younes Ivan Penkov +Jacob Trimble Jim Ray +Marco Wang Michael Tanner MiniLight Peter Collingbourne diff --git a/Makefile.am b/Makefile.am index e750157b0..d5644a2f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,6 @@ gloginclude_HEADERS = src/glog/log_severity.h nodist_gloginclude_HEADERS = src/glog/logging.h src/glog/raw_logging.h src/glog/vlog_is_on.h src/glog/stl_logging.h noinst_HEADERS = src/glog/logging.h.in src/glog/raw_logging.h.in src/glog/vlog_is_on.h.in src/glog/stl_logging.h.in -docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION) ## This is for HTML and other documentation you want to install. ## Add your documentation files (in doc/) in addition to these ## top-level boilerplate files. Also add a TODO file if you have one. diff --git a/src/glog/logging.h.in b/src/glog/logging.h.in index 9968b96d3..0ec46a167 100644 --- a/src/glog/logging.h.in +++ b/src/glog/logging.h.in @@ -522,6 +522,11 @@ GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging(); // Install a function which will be called after LOG(FATAL). GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); +// Enable/Disable old log cleaner. +GOOGLE_GLOG_DLL_DECL void EnableLogCleaner(int overdue_days); +GOOGLE_GLOG_DLL_DECL void DisableLogCleaner(); + + class LogSink; // defined below // If a non-NULL sink pointer is given, we push this message to that sink. @@ -1320,7 +1325,7 @@ inline void LogAtLevel(int const severity, std::string const &msg) { // reasonably good C++11 support, so we set LANG_CXX for it and // newer versions (_MSC_VER >= 1900). #if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ - (defined(_MSC_VER) && _MSC_VER >= 1900)) + (defined(_MSC_VER) && _MSC_VER >= 1900)) && !defined(__UCLIBCXX_MAJOR__) // Helper for CHECK_NOTNULL(). // // In C++11, all cases can be handled by a single function. Since the value @@ -1423,6 +1428,16 @@ class GOOGLE_GLOG_DLL_DECL LogSink { // Sink's logging logic (message_len is such as to exclude '\n' at the end). // This method can't use LOG() or CHECK() as logging system mutex(s) are held // during this call. + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len, int32 usecs) { + send(severity, full_filename, base_filename, line, + tm_time, message, message_len); + } + // This send() signature is obsolete. + // New implementations should define this in terms of + // the above send() method. virtual void send(LogSeverity severity, const char* full_filename, const char* base_filename, int line, const struct ::tm* tm_time, @@ -1447,7 +1462,15 @@ class GOOGLE_GLOG_DLL_DECL LogSink { // Can be useful to implement send(). static std::string ToString(LogSeverity severity, const char* file, int line, const struct ::tm* tm_time, - const char* message, size_t message_len); + const char* message, size_t message_len, + int32 usecs); + + // Obsolete + static std::string ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) { + return ToString(severity, file, line, tm_time, message, message_len, 0); + } }; // Add or remove a LogSink as a consumer of logging data. Thread-safe. diff --git a/src/googletest.h b/src/googletest.h index 49ddbc0a5..31a6a4426 100644 --- a/src/googletest.h +++ b/src/googletest.h @@ -574,7 +574,12 @@ class Thread { static inline void SleepForMilliseconds(int t) { #ifndef OS_WINDOWS +# if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L + const struct timespec req = {0, t * 1000 * 1000}; + nanosleep(&req, NULL); +# else usleep(t * 1000); +# endif #else Sleep(t); #endif diff --git a/src/logging.cc b/src/logging.cc index 0c86cf622..c1ca6efa3 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -44,6 +44,7 @@ #ifdef HAVE_SYS_UTSNAME_H # include // For uname. #endif +#include #include #include #include @@ -58,6 +59,11 @@ #include #include // for errno #include +#ifdef OS_WINDOWS +#include "windows/dirent.h" +#else +#include // for automatic removal of old logs +#endif #include "base/commandlineflags.h" // to get the program name #include "glog/logging.h" #include "glog/raw_logging.h" @@ -349,6 +355,7 @@ struct LogMessage::LogMessageData { }; time_t timestamp_; // Time of creation of LogMessage struct ::tm tm_time_; // Time of creation of LogMessage + int32 usecs_; // Time of creation of LogMessage - microseconds part 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 @@ -515,7 +522,8 @@ class LogDestination { int line, const struct ::tm* tm_time, const char* message, - size_t message_len); + size_t message_len, + int32 usecs); // Wait for all registered sinks via WaitTillSent // including the optional one in "data". @@ -782,12 +790,13 @@ inline void LogDestination::LogToSinks(LogSeverity severity, int line, const struct ::tm* tm_time, const char* message, - size_t message_len) { + size_t message_len, + int32 usecs) { ReaderMutexLock l(&sink_mutex_); if (sinks_) { for (int i = sinks_->size() - 1; i >= 0; i--) { (*sinks_)[i]->send(severity, full_filename, base_filename, - line, tm_time, message, message_len); + line, tm_time, message, message_len, usecs); } } } @@ -827,6 +836,86 @@ void LogDestination::DeleteLogDestinations() { sinks_ = NULL; } +namespace { + +bool IsGlogLog(const string& filename) { + // Check if filename matches the pattern of a glog file: + // "...log...". + const int kKeywordCount = 4; + std::string keywords[kKeywordCount] = { + glog_internal_namespace_::ProgramInvocationShortName(), + LogDestination::hostname(), + MyUserName(), + "log" + }; + + int start_pos = 0; + for (int i = 0; i < kKeywordCount; i++) { + if (filename.find(keywords[i], start_pos) == filename.npos) { + return false; + } + start_pos += keywords[i].size() + 1; + } + return true; +} + +bool LastModifiedOver(const string& filepath, int days) { + // Try to get the last modified time of this file. + struct stat file_stat; + + if (stat(filepath.c_str(), &file_stat) == 0) { + // A day is 86400 seconds, so 7 days is 86400 * 7 = 604800 seconds. + time_t last_modified_time = file_stat.st_mtime; + time_t current_time = time(NULL); + return difftime(current_time, last_modified_time) > days * 86400; + } + + // If failed to get file stat, don't return true! + return false; +} + +vector GetOverdueLogNames(string log_directory, int days) { + // The names of overdue logs. + vector overdue_log_names; + + // Try to get all files within log_directory. + DIR *dir; + struct dirent *ent; + + char dir_delim = '/'; +#ifdef OS_WINDOWS + dir_delim = '\\'; +#endif + + // If log_directory doesn't end with a slash, append a slash to it. + if (log_directory.at(log_directory.size() - 1) != dir_delim) { + log_directory += dir_delim; + } + + if ((dir=opendir(log_directory.c_str()))) { + while ((ent=readdir(dir))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + string filepath = log_directory + ent->d_name; + if (IsGlogLog(ent->d_name) && LastModifiedOver(filepath, days)) { + overdue_log_names.push_back(filepath); + } + } + closedir(dir); + } + + return overdue_log_names; +} + +// Is log_cleaner enabled? +// This option can be enabled by calling google::EnableLogCleaner(days) +bool log_cleaner_enabled_; +int log_cleaner_overdue_days_ = 7; + +} // namespace + + namespace { LogFileObject::LogFileObject(LogSeverity severity, @@ -1085,7 +1174,7 @@ void LogFileObject::Write(bool force_flush, file_length_ += header_len; bytes_since_flush_ += header_len; } - + // Write to LOG file if ( !stop_writing ) { // fwrite() doesn't return an error when the disk is full, for @@ -1124,18 +1213,32 @@ void LogFileObject::Write(bool force_flush, uint32 this_drop_length = total_drop_length - dropped_mem_length_; if (this_drop_length >= (2 << 20)) { // Only advise when >= 2MiB to drop +# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) + // 'posix_fadvise' introduced in API 21: + // * https://android.googlesource.com/platform/bionic/+/6880f936173081297be0dc12f687d341b86a4cfa/libc/libc.map.txt#732 +# else posix_fadvise(fileno(file_), dropped_mem_length_, this_drop_length, POSIX_FADV_DONTNEED); +# endif dropped_mem_length_ = total_drop_length; } } #endif + // Perform clean up for old logs + if (log_cleaner_enabled_) { + const vector& dirs = GetLoggingDirectories(); + for (size_t i = 0; i < dirs.size(); i++) { + vector logs = GetOverdueLogNames(dirs[i], log_cleaner_overdue_days_); + for (size_t j = 0; j < logs.size(); j++) { + static_cast(unlink(logs[j].c_str())); + } + } + } } } } // namespace - // Static log data space to avoid alloc failures in a LOG(FATAL) // // Since multiple threads may call LOG(FATAL), and we want to preserve @@ -1265,7 +1368,7 @@ void LogMessage::Init(const char* file, WallTime now = WallTime_Now(); data_->timestamp_ = static_cast(now); localtime_r(&data_->timestamp_, &data_->tm_time_); - int usecs = static_cast((now - data_->timestamp_) * 1000000); + data_->usecs_ = static_cast((now - data_->timestamp_) * 1000000); data_->num_chars_to_log_ = 0; data_->num_chars_to_syslog_ = 0; @@ -1285,7 +1388,7 @@ void LogMessage::Init(const char* file, << setw(2) << data_->tm_time_.tm_hour << ':' << setw(2) << data_->tm_time_.tm_min << ':' << setw(2) << data_->tm_time_.tm_sec << "." - << setw(6) << usecs + << setw(6) << data_->usecs_ << ' ' << setfill(' ') << setw(5) << static_cast(GetTID()) << setfill('0') @@ -1432,7 +1535,8 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { data_->line_, &data_->tm_time_, data_->message_text_ + data_->num_prefix_chars_, (data_->num_chars_to_log_ - - data_->num_prefix_chars_ - 1)); + data_->num_prefix_chars_ - 1), + data_->usecs_); } else { // log this message to all log files of severity <= severity_ @@ -1449,7 +1553,8 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { data_->line_, &data_->tm_time_, data_->message_text_ + data_->num_prefix_chars_, (data_->num_chars_to_log_ - - data_->num_prefix_chars_ - 1)); + - data_->num_prefix_chars_ - 1), + data_->usecs_); // NOTE: -1 removes trailing \n } @@ -1545,7 +1650,8 @@ void LogMessage::SendToSink() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { data_->line_, &data_->tm_time_, data_->message_text_ + data_->num_prefix_chars_, (data_->num_chars_to_log_ - - data_->num_prefix_chars_ - 1)); + data_->num_prefix_chars_ - 1), + data_->usecs_); } } @@ -1672,16 +1778,10 @@ void LogSink::WaitTillSent() { string LogSink::ToString(LogSeverity severity, const char* file, int line, const struct ::tm* tm_time, - const char* message, size_t message_len) { + const char* message, size_t message_len, int32 usecs) { ostringstream stream(string(message, message_len)); stream.fill('0'); - // 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 @@ -2174,4 +2274,18 @@ void ShutdownGoogleLogging() { logging_directories_list = NULL; } +void EnableLogCleaner(int overdue_days) { + log_cleaner_enabled_ = true; + + // Setting overdue_days to 0 day should not be allowed! + // Since all logs will be deleted immediately, which will cause troubles. + if (overdue_days > 0) { + log_cleaner_overdue_days_ = overdue_days; + } +} + +void DisableLogCleaner() { + log_cleaner_overdue_days_ = false; +} + _END_GOOGLE_NAMESPACE_ diff --git a/src/logging_unittest.cc b/src/logging_unittest.cc index 762c752d9..c9d210cc5 100644 --- a/src/logging_unittest.cc +++ b/src/logging_unittest.cc @@ -487,9 +487,16 @@ class TestLogSinkImpl : public LogSink { virtual void send(LogSeverity severity, const char* /* full_filename */, const char* base_filename, int line, const struct tm* tm_time, - const char* message, size_t message_len) { + const char* message, size_t message_len, int usecs) { errors.push_back( - ToString(severity, base_filename, line, tm_time, message, message_len)); + ToString(severity, base_filename, line, tm_time, message, message_len, usecs)); + } + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct tm* tm_time, + const char* message, size_t message_len) { + send(severity, full_filename, base_filename, line, + tm_time, message, message_len, 0); } }; @@ -1010,15 +1017,23 @@ class TestWaitingLogSink : public LogSink { virtual void send(LogSeverity severity, const char* /* full_filename */, const char* base_filename, int line, const struct tm* tm_time, - const char* message, size_t message_len) { + const char* message, size_t message_len, int usecs) { // Push it to Writer thread if we are the original logging thread. // Note: Something like ThreadLocalLogSink is a better choice // to do thread-specific LogSink logic for real. if (pthread_equal(tid_, pthread_self())) { writer_.Buffer(ToString(severity, base_filename, line, - tm_time, message, message_len)); + tm_time, message, message_len, usecs)); } } + + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct tm* tm_time, + const char* message, size_t message_len) { + send(severity, full_filename, base_filename, line, tm_time, message, message_len); + } + virtual void WaitTillSent() { // Wait for Writer thread if we are the original logging thread. if (pthread_equal(tid_, pthread_self())) writer_.Wait(); @@ -1076,13 +1091,14 @@ TEST(Strerror, logging) { // Simple routines to look at the sizes of generated code for LOG(FATAL) and // CHECK(..) via objdump +/* static void MyFatal() { LOG(FATAL) << "Failed"; } static void MyCheck(bool a, bool b) { CHECK_EQ(a, b); } - +*/ #ifdef HAVE_LIB_GMOCK TEST(DVLog, Basic) { diff --git a/src/stacktrace_libunwind-inl.h b/src/stacktrace_libunwind-inl.h index 0dc14c650..e29a50c00 100644 --- a/src/stacktrace_libunwind-inl.h +++ b/src/stacktrace_libunwind-inl.h @@ -49,7 +49,9 @@ _START_GOOGLE_NAMESPACE_ // recursive request, we'd end up with infinite recursion or deadlock. // Luckily, it's safe to ignore those subsequent traces. In such // cases, we return 0 to indicate the situation. -static bool g_now_entering = false; +// We can use the GCC __thread syntax here since libunwind is not supported on +// Windows. +static __thread bool g_tl_entered; // Initialized to false. // If you change this function, also change GetStackFrames below. int GetStackTrace(void** result, int max_depth, int skip_count) { @@ -58,9 +60,10 @@ int GetStackTrace(void** result, int max_depth, int skip_count) { unw_cursor_t cursor; unw_context_t uc; - if (sync_val_compare_and_swap(&g_now_entering, false, true)) { + if (g_tl_entered) { return 0; } + g_tl_entered = true; unw_getcontext(&uc); RAW_CHECK(unw_init_local(&cursor, &uc) >= 0, "unw_init_local failed"); @@ -80,7 +83,7 @@ int GetStackTrace(void** result, int max_depth, int skip_count) { break; } - g_now_entering = false; + g_tl_entered = false; return n; } diff --git a/src/symbolize.cc b/src/symbolize.cc index 1ffc6079a..ff027f216 100644 --- a/src/symbolize.cc +++ b/src/symbolize.cc @@ -110,7 +110,9 @@ _END_GOOGLE_NAMESPACE_ #if defined(__ELF__) +#if defined(HAVE_DLFCN_H) #include +#endif #if defined(OS_OPENBSD) #include #else @@ -832,7 +834,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, _END_GOOGLE_NAMESPACE_ -#elif defined(OS_MACOSX) && defined(HAVE_DLADDR) +#elif defined(OS_MACOSX) && defined(HAVE_DLADDR) && defined(HAVE_DLFCN_H) #include #include diff --git a/src/symbolize_unittest.cc b/src/symbolize_unittest.cc index 9a17e7b2a..35cc2d31f 100644 --- a/src/symbolize_unittest.cc +++ b/src/symbolize_unittest.cc @@ -401,7 +401,7 @@ int main(int argc, char **argv) { FLAGS_logtostderr = true; InitGoogleLogging(argv[0]); InitGoogleTest(&argc, argv); -#if defined(HAVE_SYMBOLIZE) +#if defined(HAVE_SYMBOLIZE) && defined(HAVE_STACKTRACE) # if defined(__ELF__) // We don't want to get affected by the callback interface, that may be // used to install some callback function at InitGoogle() time. diff --git a/src/utilities.cc b/src/utilities.cc index 6562a543c..d463b33b0 100644 --- a/src/utilities.cc +++ b/src/utilities.cc @@ -311,7 +311,7 @@ static void MyUserNameInitializer() { char buffer[1024] = {'\0'}; uid_t uid = geteuid(); int pwuid_res = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &result); - if (pwuid_res == 0) { + if (pwuid_res == 0 && result) { g_my_user_name = pwd.pw_name; } else { snprintf(buffer, sizeof(buffer), "uid%d", uid); diff --git a/src/windows/dirent.h b/src/windows/dirent.h new file mode 100644 index 000000000..f7a46dafc --- /dev/null +++ b/src/windows/dirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; + DWORD n; + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/src/windows/glog/logging.h b/src/windows/glog/logging.h index 3681fa3fc..2a739f9d8 100755 --- a/src/windows/glog/logging.h +++ b/src/windows/glog/logging.h @@ -526,6 +526,11 @@ GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging(); // Install a function which will be called after LOG(FATAL). GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); +// Enable/Disable old log cleaner. +GOOGLE_GLOG_DLL_DECL void EnableLogCleaner(int overdue_days); +GOOGLE_GLOG_DLL_DECL void DisableLogCleaner(); + + class LogSink; // defined below // If a non-NULL sink pointer is given, we push this message to that sink. @@ -1427,6 +1432,16 @@ class GOOGLE_GLOG_DLL_DECL LogSink { // Sink's logging logic (message_len is such as to exclude '\n' at the end). // This method can't use LOG() or CHECK() as logging system mutex(s) are held // during this call. + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len, int32 usecs) { + send(severity, full_filename, base_filename, line, + tm_time, message, message_len); + } + // This send() signature is obsolete. + // New implementations should define this in terms of + // the above send() method. virtual void send(LogSeverity severity, const char* full_filename, const char* base_filename, int line, const struct ::tm* tm_time, @@ -1451,7 +1466,15 @@ class GOOGLE_GLOG_DLL_DECL LogSink { // Can be useful to implement send(). static std::string ToString(LogSeverity severity, const char* file, int line, const struct ::tm* tm_time, - const char* message, size_t message_len); + const char* message, size_t message_len, + int32 usecs); + + // Obsolete + static std::string ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) { + return ToString(severity, file, line, tm_time, message, message_len, 0); + } }; // Add or remove a LogSink as a consumer of logging data. Thread-safe.