Skip to content
This repository was archived by the owner on Jun 30, 2025. It is now read-only.

Commit e60a2a4

Browse files
committed
Register all log files with cleaner
Ensures logs at other verbosity levels are cleaned up even if they are not logged
1 parent 31429d8 commit e60a2a4

File tree

1 file changed

+107
-51
lines changed

1 file changed

+107
-51
lines changed

src/logging.cc

Lines changed: 107 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
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
434446
class 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

469485
LogCleaner 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

934954
LogFileObject::~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

952978
void 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

12921322
void 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) {
26132668
void ShutdownGoogleLogging() {
26142669
ShutdownGoogleLoggingUtilities();
26152670
LogDestination::DeleteLogDestinations();
2671+
log_cleaner.UnregisterAllLogFiles();
26162672
logging_directories_list = nullptr;
26172673
g_prefix_formatter = nullptr;
26182674
}

0 commit comments

Comments
 (0)