| diff -uprN a/src/glog/logging.h.in b/src/glog/logging.h.in |
| --- a/src/glog/logging.h.in 2021-09-24 18:29:44.631818715 +0800 |
| +++ b/src/glog/logging.h.in 2021-09-24 18:25:17.592767387 +0800 |
| @@ -330,6 +330,12 @@ typedef unsigned __int64 uint64; |
| using fLS::FLAGS_##name |
| #endif |
| |
| +// Set max log file num |
| +DECLARE_int32(log_filenum_quota); |
| + |
| +// Set log file split method |
| +DECLARE_string(log_split_method); |
| + |
| // Set whether log messages go to stderr instead of logfiles |
| DECLARE_bool(logtostderr); |
| |
| diff -uprN a/src/logging.cc b/src/logging.cc |
| --- a/src/logging.cc 2021-09-24 18:29:21.619263109 +0800 |
| +++ b/src/logging.cc 2021-09-24 18:25:17.600767927 +0800 |
| @@ -34,6 +34,7 @@ |
| #include <algorithm> |
| #include <assert.h> |
| #include <iomanip> |
| +#include <list> |
| #include <string> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> // For _exit. |
| @@ -49,6 +50,7 @@ |
| #include <iostream> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| +#include <dirent.h> |
| #ifdef HAVE_PWD_H |
| # include <pwd.h> |
| #endif |
| @@ -178,6 +180,12 @@ GLOG_DEFINE_bool(stop_logging_if_full_di |
| GLOG_DEFINE_string(log_backtrace_at, "", |
| "Emit a backtrace when logging at file:linenum."); |
| |
| +GLOG_DEFINE_string(log_split_method, "day", |
| + "split log by size, day, hour"); |
| + |
| +GLOG_DEFINE_int32(log_filenum_quota, 10, |
| + "max log file num in log dir"); |
| + |
| // TODO(hamaji): consider windows |
| #define PATH_SEPARATOR '/' |
| |
| @@ -394,6 +402,15 @@ base::Logger::~Logger() { |
| |
| namespace { |
| |
| +typedef struct filetime { |
| + std::string name; |
| + time_t time; |
| + |
| + bool operator < (const struct filetime& o) const { |
| + return o.time > time; |
| + } |
| +} Filetime; |
| + |
| // Encapsulates all file-system related state |
| class LogFileObject : public base::Logger { |
| public: |
| @@ -424,6 +441,7 @@ class LogFileObject : public base::Logge |
| // can avoid grabbing a lock. Usually Flush() calls it after |
| // acquiring lock_. |
| void FlushUnlocked(); |
| + void CheckFileNumQuota(); |
| |
| private: |
| static const uint32 kRolloverAttemptFrequency = 0x20; |
| @@ -440,6 +458,9 @@ class LogFileObject : public base::Logge |
| uint32 file_length_; |
| unsigned int rollover_attempt_; |
| int64 next_flush_time_; // cycle count at which to flush log |
| + std::list<Filetime> file_list_; |
| + bool inited_; |
| + struct ::tm tm_time_; |
| |
| // Actually create a logfile using the value of base_filename_ and the |
| // supplied argument time_pid_string |
| @@ -589,7 +610,7 @@ inline void LogDestination::FlushLogFile |
| // all this stuff. |
| MutexLock l(&log_mutex); |
| for (int i = min_severity; i < NUM_SEVERITIES; i++) { |
| - LogDestination* log = log_destination(i); |
| + LogDestination* log = log_destinations_[i]; |
| if (log != NULL) { |
| log->logger_->Flush(); |
| } |
| @@ -771,8 +792,12 @@ inline void LogDestination::LogToAllLogf |
| if ( FLAGS_logtostderr ) { // global flag: never log to file |
| ColoredWriteToStderr(severity, message, len); |
| } else { |
| - for (int i = severity; i >= 0; --i) |
| - LogDestination::MaybeLogToLogfile(i, timestamp, message, len); |
| + if (severity >= 1) { |
| + LogDestination::MaybeLogToLogfile(1, timestamp, message, len); |
| + LogDestination::MaybeLogToLogfile(0, timestamp, message, len); |
| + } else if (severity == 0) { |
| + LogDestination::MaybeLogToLogfile(0, timestamp, message, len); |
| + } else {} |
| } |
| } |
| |
| @@ -841,7 +866,8 @@ LogFileObject::LogFileObject(LogSeverity |
| dropped_mem_length_(0), |
| file_length_(0), |
| rollover_attempt_(kRolloverAttemptFrequency-1), |
| - next_flush_time_(0) { |
| + next_flush_time_(0), |
| + inited_(false) { |
| assert(severity >= 0); |
| assert(severity < NUM_SEVERITIES); |
| } |
| @@ -906,7 +932,7 @@ bool LogFileObject::CreateLogfile(const |
| string string_filename = base_filename_+filename_extension_+ |
| time_pid_string; |
| const char* filename = string_filename.c_str(); |
| - int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, FLAGS_logfile_mode); |
| + int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, FLAGS_logfile_mode); |
| if (fd == -1) return false; |
| #ifdef HAVE_FCNTL |
| // Mark the file close-on-exec. We don't really care if this fails |
| @@ -920,6 +946,10 @@ bool LogFileObject::CreateLogfile(const |
| return false; |
| } |
| |
| + Filetime ft; |
| + ft.name = string_filename; |
| + file_list_.push_back(ft); |
| + |
| // We try to create a symlink called <program_name>.<severity>, |
| // which is easier to use. (Every time we create a new logfile, |
| // we destroy the old symlink and create a new one, so it always |
| @@ -961,6 +991,59 @@ bool LogFileObject::CreateLogfile(const |
| return true; // Everything worked |
| } |
| |
| +void LogFileObject::CheckFileNumQuota() { |
| + struct dirent *entry; |
| + DIR *dp; |
| + |
| + const vector<string> & log_dirs = GetLoggingDirectories(); |
| + if (log_dirs.size() < 1) return; |
| + |
| + //fprintf(stderr, "log dir: %s\n", log_dirs[0].c_str()); |
| + |
| + // list file in log dir |
| + dp = opendir(log_dirs[0].c_str()); |
| + if (dp == NULL) { |
| + fprintf(stderr, "open log dir %s fail\n", log_dirs[0].c_str()); |
| + return; |
| + } |
| + |
| + file_list_.clear(); |
| + while ((entry = readdir(dp)) != NULL) { |
| + if (DT_DIR == entry->d_type || |
| + DT_LNK == entry->d_type) { |
| + continue; |
| + } |
| + std::string filename = std::string(entry->d_name); |
| + //fprintf(stderr, "filename: %s\n", filename.c_str()); |
| + |
| + if (filename.find(symlink_basename_ + '.' + LogSeverityNames[severity_]) == 0) { |
| + std::string filepath = log_dirs[0] + "/" + filename; |
| + |
| + struct stat fstat; |
| + if (::stat(filepath.c_str(), &fstat) < 0) { |
| + fprintf(stderr, "state %s fail\n", filepath.c_str()); |
| + closedir(dp); |
| + return; |
| + } |
| + //fprintf(stderr, "filepath: %s\n", filepath.c_str()); |
| + |
| + Filetime file_time; |
| + file_time.time = fstat.st_mtime; |
| + file_time.name = filepath; |
| + file_list_.push_back(file_time); |
| + } |
| + } |
| + closedir(dp); |
| + |
| + file_list_.sort(); |
| + |
| + while (FLAGS_log_filenum_quota > 0 && file_list_.size() >= FLAGS_log_filenum_quota) { |
| + // fprintf(stderr, "delete %s\n", file_list_.front().name.c_str()); |
| + unlink(file_list_.front().name.c_str()); |
| + file_list_.pop_front(); |
| + } |
| +} |
| + |
| void LogFileObject::Write(bool force_flush, |
| time_t timestamp, |
| const char* message, |
| @@ -972,14 +1055,55 @@ void LogFileObject::Write(bool force_flu |
| return; |
| } |
| |
| - if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() || |
| - PidHasChanged()) { |
| + struct ::tm tm_time; |
| + |
| + bool is_split = false; |
| + if ("day" == FLAGS_log_split_method) { |
| + localtime_r(×tamp, &tm_time); |
| + if (tm_time.tm_year != tm_time_.tm_year |
| + || tm_time.tm_mon != tm_time_.tm_mon |
| + || tm_time.tm_mday != tm_time_.tm_mday) { |
| + is_split = true; |
| + } |
| + } else if ("hour" == FLAGS_log_split_method) { |
| + localtime_r(×tamp, &tm_time); |
| + if (tm_time.tm_year != tm_time_.tm_year |
| + || tm_time.tm_mon != tm_time_.tm_mon |
| + || tm_time.tm_mday != tm_time_.tm_mday |
| + || tm_time.tm_hour != tm_time_.tm_hour) { |
| + is_split = true; |
| + } |
| + } else if (static_cast<int>(file_length_ >> 20) >= MaxLogSize()) { |
| + // PidHasChanged()) { |
| + is_split = true; |
| + } |
| + |
| + if (is_split) { |
| if (file_ != NULL) fclose(file_); |
| file_ = NULL; |
| file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0; |
| rollover_attempt_ = kRolloverAttemptFrequency-1; |
| } |
| |
| + if ((file_ == NULL) && (!inited_) && (FLAGS_log_split_method == "size")) { |
| + CheckFileNumQuota(); |
| + const char* filename = file_list_.back().name.c_str(); |
| + int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, FLAGS_logfile_mode); |
| + if (fd != -1) { |
| +#ifdef HAVE_FCNTL |
| + // Mark the file close-on-exec. We don't really care if this fails |
| + fcntl(fd, F_SETFD, FD_CLOEXEC); |
| +#endif |
| + file_ = fopen(filename, "a+"); // Read and append a FILE*. |
| + if (file_ == NULL) { // Man, we're screwed!, try to create new log file |
| + close(fd); |
| + } |
| + fseek(file_, 0, SEEK_END); |
| + file_length_ = bytes_since_flush_ = ftell(file_); |
| + inited_ = true; |
| + } |
| + } |
| + |
| // If there's no destination file, make one before outputting |
| if (file_ == NULL) { |
| // Try to rollover the log file every 32 log messages. The only time |
| @@ -988,7 +1112,16 @@ void LogFileObject::Write(bool force_flu |
| if (++rollover_attempt_ != kRolloverAttemptFrequency) return; |
| rollover_attempt_ = 0; |
| |
| - struct ::tm tm_time; |
| + if (!inited_) { |
| + CheckFileNumQuota(); |
| + inited_ = true; |
| + } else { |
| + while (FLAGS_log_filenum_quota > 0 && file_list_.size() >= FLAGS_log_filenum_quota) { |
| + unlink(file_list_.front().name.c_str()); |
| + file_list_.pop_front(); |
| + } |
| + } |
| + |
| localtime_r(×tamp, &tm_time); |
| |
| // The logfile's filename will have the date/time & pid in it |
| @@ -996,13 +1129,19 @@ void LogFileObject::Write(bool force_flu |
| time_pid_stream.fill('0'); |
| time_pid_stream << 1900+tm_time.tm_year |
| << setw(2) << 1+tm_time.tm_mon |
| - << setw(2) << tm_time.tm_mday |
| - << '-' |
| - << setw(2) << tm_time.tm_hour |
| - << setw(2) << tm_time.tm_min |
| - << setw(2) << tm_time.tm_sec |
| - << '.' |
| - << GetMainThreadPid(); |
| + << setw(2) << tm_time.tm_mday; |
| + |
| + if ("hour" == FLAGS_log_split_method) { |
| + time_pid_stream << setw(2) << tm_time.tm_hour; |
| + } else if ("day" != FLAGS_log_split_method) { |
| + time_pid_stream << '-' |
| + << setw(2) << tm_time.tm_hour |
| + << setw(2) << tm_time.tm_min |
| + << setw(2) << tm_time.tm_sec; |
| + } |
| + |
| + tm_time_ = tm_time; |
| + |
| const string& time_pid_string = time_pid_stream.str(); |
| |
| if (base_filename_selected_) { |
| @@ -1035,10 +1174,7 @@ void LogFileObject::Write(bool force_flu |
| // attempt to hold on to the same mutex, and get into a |
| // deadlock. Simply use a name like invalid-user. |
| if (uidname.empty()) uidname = "invalid-user"; |
| - |
| - stripped_filename = stripped_filename+'.'+hostname+'.' |
| - +uidname+".log." |
| - +LogSeverityNames[severity_]+'.'; |
| + stripped_filename = stripped_filename + "." + LogSeverityNames[severity_] + ".log."; |
| // We're going to (potentially) try to put logs in several different dirs |
| const vector<string> & log_dirs = GetLoggingDirectories(); |
| |
| @@ -1062,28 +1198,6 @@ void LogFileObject::Write(bool force_flu |
| return; |
| } |
| } |
| - |
| - // Write a header message into the log file |
| - ostringstream file_header_stream; |
| - file_header_stream.fill('0'); |
| - file_header_stream << "Log file created at: " |
| - << 1900+tm_time.tm_year << '/' |
| - << setw(2) << 1+tm_time.tm_mon << '/' |
| - << setw(2) << tm_time.tm_mday |
| - << ' ' |
| - << setw(2) << tm_time.tm_hour << ':' |
| - << setw(2) << tm_time.tm_min << ':' |
| - << setw(2) << tm_time.tm_sec << '\n' |
| - << "Running on machine: " |
| - << LogDestination::hostname() << '\n' |
| - << "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu " |
| - << "threadid file:line] msg" << '\n'; |
| - const string& file_header_string = file_header_stream.str(); |
| - |
| - const int header_len = file_header_string.size(); |
| - fwrite(file_header_string.data(), 1, header_len, file_); |
| - file_length_ += header_len; |
| - bytes_since_flush_ += header_len; |
| } |
| |
| // Write to LOG file |
| diff -uprN a/configure.ac b/configure.ac |
| --- a/configure.ac 2022-01-24 22:47:23.776890869 +0800 |
| +++ b/configure.ac 2022-01-24 22:47:03.418891534 +0800 |
| @@ -38,7 +38,7 @@ AC_CHECK_HEADERS(sys/syscall.h) |
| # For backtrace with glibc. |
| AC_CHECK_HEADERS(execinfo.h) |
| # For backtrace with libunwind. |
| -AC_CHECK_HEADERS(libunwind.h, ac_cv_have_libunwind_h=1, ac_cv_have_libunwind_h=0) |
| +# AC_CHECK_HEADERS(libunwind.h, ac_cv_have_libunwind_h=1, ac_cv_have_libunwind_h=0) |
| AC_CHECK_HEADERS(ucontext.h) |
| AC_CHECK_HEADERS(sys/utsname.h) |
| AC_CHECK_HEADERS(pwd.h) |