40
40
#include < iterator>
41
41
#include < memory>
42
42
#include < mutex>
43
+ #include < set>
43
44
#include < shared_mutex>
44
45
#include < string>
45
46
#include < thread>
@@ -430,6 +431,17 @@ class LogFileObject : public base::Logger {
430
431
bool CreateLogfile (const string& time_pid_string);
431
432
};
432
433
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
+
433
445
// Encapsulate all log cleaner related states
434
446
class LogCleaner {
435
447
public:
@@ -439,11 +451,11 @@ class LogCleaner {
439
451
void Enable (const std::chrono::minutes& overdue);
440
452
void Disable ();
441
453
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 ( );
445
457
446
- bool enabled () const { return enabled_; }
458
+ void Run ( const std::chrono::system_clock::time_point& current_time);
447
459
448
460
private:
449
461
vector<string> GetOverdueLogNames (
@@ -459,11 +471,15 @@ class LogCleaner {
459
471
const string& filepath,
460
472
const std::chrono::system_clock::time_point& current_time) const ;
461
473
474
+ std::string RemoveDuplicatePathDelimiters (const std::string& path) const ;
475
+ std::string GetLogDir (const std::string& filepath) const ;
476
+
462
477
bool enabled_{false };
463
478
std::chrono::minutes overdue_{
464
479
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_;
467
483
};
468
484
469
485
LogCleaner log_cleaner;
@@ -929,7 +945,11 @@ LogFileObject::LogFileObject(LogSeverity severity, const char* base_filename)
929
945
filename_extension_(),
930
946
severity_(severity),
931
947
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
+ }
933
953
934
954
LogFileObject::~LogFileObject () {
935
955
std::lock_guard<std::mutex> l{mutex_};
@@ -940,24 +960,36 @@ void LogFileObject::SetBasename(const char* basename) {
940
960
std::lock_guard<std::mutex> l{mutex_};
941
961
base_filename_selected_ = true ;
942
962
if (base_filename_ != basename) {
963
+ if (!base_filename_.empty ()) {
964
+ log_cleaner.UnregisterLogFile (base_filename_, filename_extension_);
965
+ }
943
966
// Get rid of old log file since we are changing names
944
967
if (file_ != nullptr ) {
945
968
file_ = nullptr ;
946
969
rollover_attempt_ = kRolloverAttemptFrequency - 1 ;
947
970
}
948
971
base_filename_ = basename;
972
+ if (!base_filename_.empty ()) {
973
+ log_cleaner.RegisterLogFile (base_filename_, filename_extension_);
974
+ }
949
975
}
950
976
}
951
977
952
978
void LogFileObject::SetExtension (const char * ext) {
953
979
std::lock_guard<std::mutex> l{mutex_};
954
980
if (filename_extension_ != ext) {
981
+ if (!base_filename_.empty ()) {
982
+ log_cleaner.UnregisterLogFile (base_filename_, filename_extension_);
983
+ }
955
984
// Get rid of old log file since we are changing names
956
985
if (file_ != nullptr ) {
957
986
file_ = nullptr ;
958
987
rollover_attempt_ = kRolloverAttemptFrequency - 1 ;
959
988
}
960
989
filename_extension_ = ext;
990
+ if (!base_filename_.empty ()) {
991
+ log_cleaner.RegisterLogFile (base_filename_, filename_extension_);
992
+ }
961
993
}
962
994
}
963
995
@@ -1097,11 +1129,8 @@ void LogFileObject::Write(
1097
1129
return ;
1098
1130
}
1099
1131
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);
1105
1134
};
1106
1135
1107
1136
// Remove old logs
@@ -1181,6 +1210,7 @@ void LogFileObject::Write(
1181
1210
for (const auto & log_dir : log_dirs) {
1182
1211
base_filename_ = log_dir + " /" + stripped_filename;
1183
1212
if (CreateLogfile (time_pid_string)) {
1213
+ log_cleaner.RegisterLogFile (base_filename_, filename_extension_);
1184
1214
success = true ;
1185
1215
break ;
1186
1216
}
@@ -1291,11 +1321,33 @@ void LogCleaner::Enable(const std::chrono::minutes& overdue) {
1291
1321
1292
1322
void LogCleaner::Disable () { enabled_ = false ; }
1293
1323
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_};
1299
1351
1300
1352
// avoid scanning logs too frequently
1301
1353
if (current_time < next_cleanup_time_) {
@@ -1307,24 +1359,10 @@ void LogCleaner::Run(const std::chrono::system_clock::time_point& current_time,
1307
1359
std::chrono::duration_cast<std::chrono::system_clock::duration>(
1308
1360
std::chrono::duration<int32>{FLAGS_logcleansecs});
1309
1361
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 );
1328
1366
for (const std::string& log : logs) {
1329
1367
// NOTE May fail on Windows if the file is still open
1330
1368
int result = unlink (log.c_str ());
@@ -1361,6 +1399,7 @@ vector<string> LogCleaner::GetOverdueLogNames(
1361
1399
log_directory[log_directory.size () - 1 ]) != dir_delim_end) {
1362
1400
filepath = log_directory + filepath;
1363
1401
}
1402
+ filepath = RemoveDuplicatePathDelimiters (filepath);
1364
1403
1365
1404
if (IsLogFromCurrentProject (filepath, base_filename,
1366
1405
filename_extension) &&
@@ -1380,22 +1419,7 @@ bool LogCleaner::IsLogFromCurrentProject(
1380
1419
// We should remove duplicated delimiters from `base_filename`, e.g.,
1381
1420
// before: "/tmp//<base_filename>.<create_time>.<pid>"
1382
1421
// 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);
1399
1423
1400
1424
// Return early if the filename doesn't start with `cleaned_base_filename`.
1401
1425
if (filepath.find (cleaned_base_filename) != 0 ) {
@@ -1405,6 +1429,7 @@ bool LogCleaner::IsLogFromCurrentProject(
1405
1429
// Check if in the string `filename_extension` is right next to
1406
1430
// `cleaned_base_filename` in `filepath` if the user
1407
1431
// has set a custom filename extension.
1432
+ size_t real_filepath_size = filepath.size ();
1408
1433
if (!filename_extension.empty ()) {
1409
1434
if (cleaned_base_filename.size () >= real_filepath_size) {
1410
1435
return false ;
@@ -1478,6 +1503,36 @@ bool LogCleaner::IsLogLastModifiedOver(
1478
1503
return false ;
1479
1504
}
1480
1505
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
+
1481
1536
} // namespace
1482
1537
1483
1538
// Static log data space to avoid alloc failures in a LOG(FATAL)
@@ -2613,6 +2668,7 @@ void InstallPrefixFormatter(PrefixFormatterCallback callback, void* data) {
2613
2668
void ShutdownGoogleLogging () {
2614
2669
ShutdownGoogleLoggingUtilities ();
2615
2670
LogDestination::DeleteLogDestinations ();
2671
+ log_cleaner.UnregisterAllLogFiles ();
2616
2672
logging_directories_list = nullptr ;
2617
2673
g_prefix_formatter = nullptr ;
2618
2674
}
0 commit comments