From 549acaec11a8337c2fbf16917945a35e9b445e73 Mon Sep 17 00:00:00 2001 From: mturnock Date: Wed, 31 Jul 2024 13:12:18 +0100 Subject: [PATCH] Support cleaning old log files based on count of log files --- src/flags.cc | 5 +++++ src/glog/flags.h | 1 + src/logging.cc | 55 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/flags.cc b/src/flags.cc index c4c2aa4b2..9b712bb68 100644 --- a/src/flags.cc +++ b/src/flags.cc @@ -114,6 +114,11 @@ GLOG_DEFINE_int32(logbufsecs, 30, GLOG_DEFINE_int32(logcleansecs, 60 * 5, // every 5 minutes "Clean overdue logs every this many seconds"); +GLOG_DEFINE_uint32(max_num_log_files, 0, + "Maximum number of log files retained per log level when " + "using the log cleaner. Oldest logs are deleted first. " + "If 0, all log files are kept."); + GLOG_DEFINE_int32(logemaillevel, 999, "Email log messages logged at this level or higher" " (0 means email all; 3 means email FATAL only;" diff --git a/src/glog/flags.h b/src/glog/flags.h index cb542b930..cd5fbccba 100644 --- a/src/glog/flags.h +++ b/src/glog/flags.h @@ -104,6 +104,7 @@ DECLARE_int32(logemaillevel); DECLARE_int32(logcleansecs); +DECLARE_uint32(max_num_log_files); #ifdef GLOG_OS_LINUX DECLARE_bool(drop_log_memory); diff --git a/src/logging.cc b/src/logging.cc index 08b2ab387..a3e7552e0 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -446,7 +446,7 @@ class LogCleaner { bool enabled() const { return enabled_; } private: - vector GetOverdueLogNames( + vector GetLogNamesToDelete( string log_directory, const std::chrono::system_clock::time_point& current_time, const string& base_filename, const string& filename_extension) const; @@ -455,9 +455,9 @@ class LogCleaner { const string& base_filename, const string& filename_extension) const; - bool IsLogLastModifiedOver( + bool GetLastModifiedTime( const string& filepath, - const std::chrono::system_clock::time_point& current_time) const; + std::chrono::system_clock::time_point& last_modified_time) const; bool enabled_{false}; std::chrono::minutes overdue_{ @@ -1323,8 +1323,8 @@ void LogCleaner::Run(const std::chrono::system_clock::time_point& current_time, } for (const std::string& dir : dirs) { - vector logs = GetOverdueLogNames(dir, current_time, base_filename, - filename_extension); + vector logs = GetLogNamesToDelete(dir, current_time, base_filename, + filename_extension); for (const std::string& log : logs) { // NOTE May fail on Windows if the file is still open int result = unlink(log.c_str()); @@ -1335,14 +1335,13 @@ void LogCleaner::Run(const std::chrono::system_clock::time_point& current_time, } } -vector LogCleaner::GetOverdueLogNames( +vector LogCleaner::GetLogNamesToDelete( string log_directory, const std::chrono::system_clock::time_point& current_time, const string& base_filename, const string& filename_extension) const { - // The names of overdue logs. - vector overdue_log_names; - // Try to get all files within log_directory. + using LogFileInfo = std::pair; + vector log_file_info; DIR* dir; struct dirent* ent; @@ -1362,16 +1361,39 @@ vector LogCleaner::GetOverdueLogNames( filepath = log_directory + filepath; } + std::chrono::system_clock::time_point last_modified_time; if (IsLogFromCurrentProject(filepath, base_filename, filename_extension) && - IsLogLastModifiedOver(filepath, current_time)) { - overdue_log_names.push_back(filepath); + GetLastModifiedTime(filepath, last_modified_time)) { + log_file_info.push_back(std::make_pair(last_modified_time, filepath)); } } closedir(dir); } - return overdue_log_names; + // Sort the log files by last modified time, oldest first. + std::sort(log_file_info.begin(), log_file_info.end()); + + const auto max_num_log_files_set = FLAGS_max_num_log_files != 0; + const auto too_many_log_files = + max_num_log_files_set && log_file_info.size() > FLAGS_max_num_log_files; + const auto min_num_logs_to_del = + too_many_log_files ? log_file_info.size() - FLAGS_max_num_log_files : 0; + + // The names of logs to delete. + vector logs_to_delete; + for (const auto& info : log_file_info) { + const auto& last_modified_time = info.first; + const auto& filepath = info.second; + + const auto is_overdue = current_time - last_modified_time >= overdue_; + const auto need_to_delete = logs_to_delete.size() < min_num_logs_to_del; + + if (is_overdue || need_to_delete) { + logs_to_delete.push_back(filepath); + } + } + return logs_to_delete; } bool LogCleaner::IsLogFromCurrentProject( @@ -1461,17 +1483,16 @@ bool LogCleaner::IsLogFromCurrentProject( return true; } -bool LogCleaner::IsLogLastModifiedOver( +bool LogCleaner::GetLastModifiedTime( const string& filepath, - const std::chrono::system_clock::time_point& current_time) const { + std::chrono::system_clock::time_point& last_modified_time) const { // Try to get the last modified time of this file. struct stat file_stat; if (stat(filepath.c_str(), &file_stat) == 0) { - const auto last_modified_time = + 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_; + return true; } // If failed to get file stat, don't return true!