diff --git a/src/cleanup_deferred_unittest.cc b/src/cleanup_deferred_unittest.cc new file mode 100644 index 000000000..1583543da --- /dev/null +++ b/src/cleanup_deferred_unittest.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2024, 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. + +#include "base/commandlineflags.h" +#include "glog/logging.h" +#include "glog/raw_logging.h" +#include "googletest.h" + +#ifdef GLOG_USE_GFLAGS +# include +using namespace GFLAGS_NAMESPACE; +#endif + +#ifdef HAVE_LIB_GMOCK +# include + +# include "mock-log.h" +// Introduce several symbols from gmock. +using google::glog_testing::ScopedMockLog; +using testing::_; +using testing::AllOf; +using testing::AnyNumber; +using testing::HasSubstr; +using testing::InitGoogleMock; +using testing::StrictMock; +using testing::StrNe; +#endif + +using namespace google; + +TEST(CleanDeferred, logging) { + using namespace std::chrono_literals; + const string dest = + FLAGS_test_tmpdir + "/test_cleanup_enable_generic_logs"; + google::EnableLogCleanerForGenericLogs(1h); + google::SetLogDestination(GLOG_INFO, dest.c_str()); + for (unsigned i = 0; i < 10; ++i) { + LOG(INFO) << "cleanup test"; + } + + google::DisableLogCleanerForGenericLogs(); + // delete the file + CHECK(unlink(dest.c_str()) == 0) << ": " << strerror(errno); +} + +int main(int argc, char** argv) { + FLAGS_colorlogtostderr = false; + FLAGS_timestamp_in_logfile_name = false; + FLAGS_logcleansecs = 1; +#ifdef GLOG_USE_GFLAGS + ParseCommandLineFlags(&argc, &argv, true); +#endif + // Make sure stderr is not buffered as stderr seems to be buffered + // on recent windows. + setbuf(stderr, nullptr); + + // Test some basics before InitGoogleLogging: + CaptureTestStderr(); + const string early_stderr = GetCapturedTestStderr(); + + EXPECT_FALSE(IsGoogleLoggingInitialized()); + + InitGoogleLogging(argv[0]); + + EXPECT_TRUE(IsGoogleLoggingInitialized()); + + InitGoogleTest(&argc, argv); +#ifdef HAVE_LIB_GMOCK + InitGoogleMock(&argc, argv); +#endif + + // so that death tests run before we use threads + CHECK_EQ(RUN_ALL_TESTS(), 0); +} diff --git a/src/glog/logging.h b/src/glog/logging.h index 47439667c..ad12a66a1 100644 --- a/src/glog/logging.h +++ b/src/glog/logging.h @@ -485,6 +485,10 @@ InstallFailureFunction(logging_fail_func_t fail_func); GLOG_EXPORT void EnableLogCleaner(const std::chrono::minutes& overdue); GLOG_EXPORT void DisableLogCleaner(); GLOG_EXPORT void SetApplicationFingerprint(const std::string& fingerprint); +GLOG_EXPORT void EnableLogCleaner(LogSeverity severity, const std::chrono::minutes& overdue); +GLOG_EXPORT void DisableLogCleaner(LogSeverity severity); +GLOG_EXPORT void EnableLogCleanerForGenericLogs(const std::chrono::minutes& overdue); +GLOG_EXPORT void DisableLogCleanerForGenericLogs(); class LogSink; // defined below diff --git a/src/logging.cc b/src/logging.cc index 08b2ab387..1ac58b9ee 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -72,6 +72,7 @@ #include #include #include +#include #ifdef HAVE__CHSIZE_S # include // for truncate log file @@ -337,6 +338,16 @@ const char* GetLogSeverityName(LogSeverity severity) { return LogSeverityNames[severity]; } +int FindFilepathLogSeverity(const std::string& filepath) { + for (int i = GLOG_INFO; i < NUM_SEVERITIES; i++) { + if (filepath.rfind(GetLogSeverityName(static_cast(i))) != std::string::npos) { + return i; + } + } + + return -1; +} + static bool SendEmailInternal(const char* dest, const char* subject, const char* body, bool use_logging); @@ -345,7 +356,6 @@ base::Logger::~Logger() = default; namespace { constexpr std::intmax_t kSecondsInDay = 60 * 60 * 24; -constexpr std::intmax_t kSecondsInWeek = kSecondsInDay * 7; // Optional user-configured callback to print custom prefixes. class PrefixFormatter { @@ -437,13 +447,18 @@ class LogCleaner { // Setting overdue to 0 days will delete all logs. void Enable(const std::chrono::minutes& overdue); + void Enable(const LogSeverity severity, const std::chrono::minutes& overdue); void Disable(); + void Disable(const LogSeverity severity); + // For files with no severity in their names + void EnableForGenericLogs(const std::chrono::minutes& overdue); + void DisableForGenericLogs(); void Run(const std::chrono::system_clock::time_point& current_time, bool base_filename_selected, const string& base_filename, const string& filename_extension); - bool enabled() const { return enabled_; } + bool enabled() const { return !overdue_.empty(); } private: vector GetOverdueLogNames( @@ -459,9 +474,7 @@ class LogCleaner { const string& filepath, const std::chrono::system_clock::time_point& current_time) const; - bool enabled_{false}; - std::chrono::minutes overdue_{ - std::chrono::duration>{1}}; + std::unordered_map overdue_; std::chrono::system_clock::time_point next_cleanup_time_; // cycle count at which to clean overdue log }; @@ -1285,16 +1298,36 @@ void LogFileObject::Write( LogCleaner::LogCleaner() = default; void LogCleaner::Enable(const std::chrono::minutes& overdue) { - enabled_ = true; - overdue_ = overdue; + // For the files that have no severity specified, we use -1 as the key + EnableForGenericLogs(overdue); + // For backward compatability, set all severities to the same value + for (int i = GLOG_INFO; i < NUM_SEVERITIES; i++) { + Enable(static_cast(i), overdue); + } +} + +void LogCleaner::Enable(const LogSeverity severity, const std::chrono::minutes& overdue) { + overdue_[severity] = overdue; +} + +void LogCleaner::Disable() { overdue_.clear(); } + +void LogCleaner::Disable(const LogSeverity severity) { + overdue_.erase(severity); +} + +void LogCleaner::EnableForGenericLogs(const std::chrono::minutes& overdue) { + overdue_[-1] = overdue; } -void LogCleaner::Disable() { enabled_ = false; } +void LogCleaner::DisableForGenericLogs() { + overdue_.erase(-1); +} void LogCleaner::Run(const std::chrono::system_clock::time_point& current_time, bool base_filename_selected, const string& base_filename, const string& filename_extension) { - assert(enabled_); + assert(enabled()); assert(!base_filename_selected || !base_filename.empty()); // avoid scanning logs too frequently @@ -1471,7 +1504,13 @@ bool LogCleaner::IsLogLastModifiedOver( const auto last_modified_time = std::chrono::system_clock::from_time_t(file_stat.st_mtime); const auto diff = current_time - last_modified_time; - return diff >= overdue_; + + auto severity_it = overdue_.find(FindFilepathLogSeverity(filepath)); + if (severity_it == overdue_.end()) { + return false; + } + + return diff >= severity_it->second; } // If failed to get file stat, don't return true! @@ -2627,8 +2666,24 @@ void EnableLogCleaner(const std::chrono::minutes& overdue) { log_cleaner.Enable(overdue); } +void EnableLogCleaner(LogSeverity severity, const std::chrono::minutes& overdue) { + log_cleaner.Enable(severity, overdue); +} + +void EnableLogCleanerForGenericLogs(const std::chrono::minutes& overdue) { + log_cleaner.EnableForGenericLogs(overdue); +} + void DisableLogCleaner() { log_cleaner.Disable(); } +void DisableLogCleaner(LogSeverity severity) { + log_cleaner.Disable(severity); +} + +void DisableLogCleanerForGenericLogs() { + log_cleaner.DisableForGenericLogs(); +} + LogMessageTime::LogMessageTime() = default; namespace {