4040#include < iterator>
4141#include < memory>
4242#include < mutex>
43+ #include < set>
4344#include < shared_mutex>
4445#include < string>
4546#include < thread>
@@ -430,6 +431,17 @@ class LogFileObject : public base::Logger {
430431 bool CreateLogfile (const string& time_pid_string);
431432};
432433
434+ struct LogCleanerFileInfo {
435+ std::string dir;
436+ std::string base_filename;
437+ std::string filename_extension;
438+ };
439+
440+ bool operator <(const LogCleanerFileInfo& l, const LogCleanerFileInfo& r) {
441+ return std::tie (l.dir , l.base_filename , l.filename_extension ) <
442+ std::tie (r.dir , r.base_filename , r.filename_extension );
443+ }
444+
433445// Encapsulate all log cleaner related states
434446class LogCleaner {
435447 public:
@@ -439,11 +451,11 @@ class LogCleaner {
439451 void Enable (const std::chrono::minutes& overdue);
440452 void Disable ();
441453
442- void Run (const std::chrono::system_clock::time_point& current_time,
443- bool base_filename_selected , const string& base_filename,
444- const string& filename_extension );
454+ void RegisterLogFile (const std::string& path, const std::string& ext);
455+ void UnregisterLogFile ( const std::string& path , const std:: string& ext);
456+ void UnregisterAllLogFiles ( );
445457
446- bool enabled () const { return enabled_; }
458+ void Run ( const std::chrono::system_clock::time_point& current_time);
447459
448460 private:
449461 vector<string> GetOverdueLogNames (
@@ -459,11 +471,15 @@ class LogCleaner {
459471 const string& filepath,
460472 const std::chrono::system_clock::time_point& current_time) const ;
461473
474+ std::string RemoveDuplicatePathDelimiters (const std::string& path) const ;
475+ std::string GetLogDir (const std::string& filepath) const ;
476+
462477 bool enabled_{false };
463478 std::chrono::minutes overdue_{
464479 std::chrono::duration<int , std::ratio<kSecondsInWeek >>{1 }};
465- std::chrono::system_clock::time_point
466- next_cleanup_time_; // cycle count at which to clean overdue log
480+ std::chrono::system_clock::time_point next_cleanup_time_;
481+ std::set<LogCleanerFileInfo> log_files_;
482+ std::mutex mutex_;
467483};
468484
469485LogCleaner log_cleaner;
@@ -929,7 +945,11 @@ LogFileObject::LogFileObject(LogSeverity severity, const char* base_filename)
929945 filename_extension_(),
930946 severity_(severity),
931947 rollover_attempt_(kRolloverAttemptFrequency - 1 ),
932- start_time_(std::chrono::system_clock::now()) {}
948+ start_time_(std::chrono::system_clock::now()) {
949+ if (!base_filename_.empty ()) {
950+ log_cleaner.RegisterLogFile (base_filename_, filename_extension_);
951+ }
952+ }
933953
934954LogFileObject::~LogFileObject () {
935955 std::lock_guard<std::mutex> l{mutex_};
@@ -940,24 +960,36 @@ void LogFileObject::SetBasename(const char* basename) {
940960 std::lock_guard<std::mutex> l{mutex_};
941961 base_filename_selected_ = true ;
942962 if (base_filename_ != basename) {
963+ if (!base_filename_.empty ()) {
964+ log_cleaner.UnregisterLogFile (base_filename_, filename_extension_);
965+ }
943966 // Get rid of old log file since we are changing names
944967 if (file_ != nullptr ) {
945968 file_ = nullptr ;
946969 rollover_attempt_ = kRolloverAttemptFrequency - 1 ;
947970 }
948971 base_filename_ = basename;
972+ if (!base_filename_.empty ()) {
973+ log_cleaner.RegisterLogFile (base_filename_, filename_extension_);
974+ }
949975 }
950976}
951977
952978void LogFileObject::SetExtension (const char * ext) {
953979 std::lock_guard<std::mutex> l{mutex_};
954980 if (filename_extension_ != ext) {
981+ if (!base_filename_.empty ()) {
982+ log_cleaner.UnregisterLogFile (base_filename_, filename_extension_);
983+ }
955984 // Get rid of old log file since we are changing names
956985 if (file_ != nullptr ) {
957986 file_ = nullptr ;
958987 rollover_attempt_ = kRolloverAttemptFrequency - 1 ;
959988 }
960989 filename_extension_ = ext;
990+ if (!base_filename_.empty ()) {
991+ log_cleaner.RegisterLogFile (base_filename_, filename_extension_);
992+ }
961993 }
962994}
963995
@@ -1097,11 +1129,8 @@ void LogFileObject::Write(
10971129 return ;
10981130 }
10991131
1100- auto cleanupLogs = [this , current_time = timestamp] {
1101- if (log_cleaner.enabled ()) {
1102- log_cleaner.Run (current_time, base_filename_selected_, base_filename_,
1103- filename_extension_);
1104- }
1132+ auto cleanupLogs = [current_time = timestamp] {
1133+ log_cleaner.Run (current_time);
11051134 };
11061135
11071136 // Remove old logs
@@ -1181,6 +1210,7 @@ void LogFileObject::Write(
11811210 for (const auto & log_dir : log_dirs) {
11821211 base_filename_ = log_dir + " /" + stripped_filename;
11831212 if (CreateLogfile (time_pid_string)) {
1213+ log_cleaner.RegisterLogFile (base_filename_, filename_extension_);
11841214 success = true ;
11851215 break ;
11861216 }
@@ -1291,11 +1321,33 @@ void LogCleaner::Enable(const std::chrono::minutes& overdue) {
12911321
12921322void LogCleaner::Disable () { enabled_ = false ; }
12931323
1294- void LogCleaner::Run (const std::chrono::system_clock::time_point& current_time,
1295- bool base_filename_selected, const string& base_filename,
1296- const string& filename_extension) {
1297- assert (enabled_);
1298- assert (!base_filename_selected || !base_filename.empty ());
1324+ void LogCleaner::RegisterLogFile (const std::string& base_filepath,
1325+ const std::string& filename_extension) {
1326+ std::lock_guard<std::mutex> l{mutex_};
1327+ log_files_.insert ({GetLogDir (base_filepath), base_filepath, filename_extension});
1328+
1329+ // Reset the next cleanup time so that the next Run() will clean up the new logs
1330+ next_cleanup_time_ = {};
1331+ }
1332+
1333+ void LogCleaner::UnregisterLogFile (const std::string& base_filepath,
1334+ const std::string& filename_extension) {
1335+ std::lock_guard<std::mutex> l{mutex_};
1336+ log_files_.erase ({GetLogDir (base_filepath), base_filepath, filename_extension});
1337+ }
1338+
1339+ void LogCleaner::UnregisterAllLogFiles ()
1340+ {
1341+ std::lock_guard<std::mutex> l{mutex_};
1342+ log_files_.clear ();
1343+ }
1344+
1345+ void LogCleaner::Run (const std::chrono::system_clock::time_point& current_time) {
1346+ if (!enabled_) {
1347+ return ;
1348+ }
1349+
1350+ std::lock_guard<std::mutex> l{mutex_};
12991351
13001352 // avoid scanning logs too frequently
13011353 if (current_time < next_cleanup_time_) {
@@ -1307,24 +1359,10 @@ void LogCleaner::Run(const std::chrono::system_clock::time_point& current_time,
13071359 std::chrono::duration_cast<std::chrono::system_clock::duration>(
13081360 std::chrono::duration<int32>{FLAGS_logcleansecs});
13091361
1310- vector<string> dirs;
1311-
1312- if (!base_filename_selected) {
1313- dirs = GetLoggingDirectories ();
1314- } else {
1315- size_t pos = base_filename.find_last_of (possible_dir_delim, string::npos,
1316- sizeof (possible_dir_delim));
1317- if (pos != string::npos) {
1318- string dir = base_filename.substr (0 , pos + 1 );
1319- dirs.push_back (dir);
1320- } else {
1321- dirs.emplace_back (" ." );
1322- }
1323- }
1324-
1325- for (const std::string& dir : dirs) {
1326- vector<string> logs = GetOverdueLogNames (dir, current_time, base_filename,
1327- filename_extension);
1362+ for (const auto & log_file_info : log_files_) {
1363+ const auto logs = GetOverdueLogNames (log_file_info.dir , current_time,
1364+ log_file_info.base_filename ,
1365+ log_file_info.filename_extension );
13281366 for (const std::string& log : logs) {
13291367 // NOTE May fail on Windows if the file is still open
13301368 int result = unlink (log.c_str ());
@@ -1361,6 +1399,7 @@ vector<string> LogCleaner::GetOverdueLogNames(
13611399 log_directory[log_directory.size () - 1 ]) != dir_delim_end) {
13621400 filepath = log_directory + filepath;
13631401 }
1402+ filepath = RemoveDuplicatePathDelimiters (filepath);
13641403
13651404 if (IsLogFromCurrentProject (filepath, base_filename,
13661405 filename_extension) &&
@@ -1380,22 +1419,7 @@ bool LogCleaner::IsLogFromCurrentProject(
13801419 // We should remove duplicated delimiters from `base_filename`, e.g.,
13811420 // before: "/tmp//<base_filename>.<create_time>.<pid>"
13821421 // after: "/tmp/<base_filename>.<create_time>.<pid>"
1383- string cleaned_base_filename;
1384-
1385- const char * const dir_delim_end =
1386- possible_dir_delim + sizeof (possible_dir_delim);
1387-
1388- size_t real_filepath_size = filepath.size ();
1389- for (char c : base_filename) {
1390- if (cleaned_base_filename.empty ()) {
1391- cleaned_base_filename += c;
1392- } else if (std::find (possible_dir_delim, dir_delim_end, c) ==
1393- dir_delim_end ||
1394- (!cleaned_base_filename.empty () &&
1395- c != cleaned_base_filename[cleaned_base_filename.size () - 1 ])) {
1396- cleaned_base_filename += c;
1397- }
1398- }
1422+ string cleaned_base_filename = RemoveDuplicatePathDelimiters (base_filename);
13991423
14001424 // Return early if the filename doesn't start with `cleaned_base_filename`.
14011425 if (filepath.find (cleaned_base_filename) != 0 ) {
@@ -1405,6 +1429,7 @@ bool LogCleaner::IsLogFromCurrentProject(
14051429 // Check if in the string `filename_extension` is right next to
14061430 // `cleaned_base_filename` in `filepath` if the user
14071431 // has set a custom filename extension.
1432+ size_t real_filepath_size = filepath.size ();
14081433 if (!filename_extension.empty ()) {
14091434 if (cleaned_base_filename.size () >= real_filepath_size) {
14101435 return false ;
@@ -1478,6 +1503,36 @@ bool LogCleaner::IsLogLastModifiedOver(
14781503 return false ;
14791504}
14801505
1506+ std::string LogCleaner::RemoveDuplicatePathDelimiters (const std::string& path) const {
1507+ string cleaned_path;
1508+
1509+ const char * const dir_delim_end =
1510+ possible_dir_delim + sizeof (possible_dir_delim);
1511+
1512+ for (char c : path) {
1513+ if (cleaned_path.empty ()) {
1514+ cleaned_path += c;
1515+ } else if (std::find (possible_dir_delim, dir_delim_end, c) ==
1516+ dir_delim_end ||
1517+ (!cleaned_path.empty () &&
1518+ c != cleaned_path[cleaned_path.size () - 1 ])) {
1519+ cleaned_path += c;
1520+ }
1521+ }
1522+
1523+ return cleaned_path;
1524+ }
1525+
1526+ std::string LogCleaner::GetLogDir (const std::string& filepath) const {
1527+ const size_t pos = filepath.find_last_of (
1528+ possible_dir_delim, string::npos, sizeof (possible_dir_delim));
1529+ if (pos != std::string::npos) {
1530+ return filepath.substr (0 , pos + 1 );
1531+ } else {
1532+ return " ." ;
1533+ }
1534+ }
1535+
14811536} // namespace
14821537
14831538// Static log data space to avoid alloc failures in a LOG(FATAL)
@@ -2613,6 +2668,7 @@ void InstallPrefixFormatter(PrefixFormatterCallback callback, void* data) {
26132668void ShutdownGoogleLogging () {
26142669 ShutdownGoogleLoggingUtilities ();
26152670 LogDestination::DeleteLogDestinations ();
2671+ log_cleaner.UnregisterAllLogFiles ();
26162672 logging_directories_list = nullptr ;
26172673 g_prefix_formatter = nullptr ;
26182674}
0 commit comments