| diff --git a/src/glog/logging.h.in b/src/glog/logging.h.in |
| index 95a573b..aaa00c2 100644 |
| --- a/src/glog/logging.h.in |
| +++ b/src/glog/logging.h.in |
| @@ -425,6 +425,16 @@ typedef void(*CustomPrefixCallback)(std::ostream& s, const LogMessageInfo& l, vo |
| using fLS::FLAGS_##name |
| #endif |
| |
| +// Set max log file num |
| +DECLARE_int32(log_filenum_quota); |
| + |
| +// Set max warn log file num |
| +#define GLOG_HAS_WARN_LOG_FILENUM_QUOTA |
| +DECLARE_int32(warn_log_filenum_quota); |
| + |
| +// Set log file split method |
| +DECLARE_string(log_split_method); |
| + |
| // Set whether appending a timestamp to the log file name |
| DECLARE_bool(timestamp_in_logfile_name); |
| |
| diff --git a/src/logging.cc b/src/logging.cc |
| index 4028ccc..e8d9e32 100644 |
| --- a/src/logging.cc |
| +++ b/src/logging.cc |
| @@ -34,6 +34,7 @@ |
| #include <algorithm> |
| #include <cassert> |
| #include <iomanip> |
| +#include <list> |
| #include <string> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> // For _exit. |
| @@ -50,6 +51,7 @@ |
| #include <iostream> |
| #include <cstdarg> |
| #include <cstdlib> |
| +#include <dirent.h> |
| #ifdef HAVE_PWD_H |
| # include <pwd.h> |
| #endif |
| @@ -204,6 +206,15 @@ GLOG_DEFINE_string(log_backtrace_at, "", |
| GLOG_DEFINE_bool(log_utc_time, false, |
| "Use UTC time for logging."); |
| |
| +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"); |
| + |
| +GLOG_DEFINE_int32(warn_log_filenum_quota, -1, |
| + "max warn log file num in log dir, -1 means equal to log_filenum_quota"); |
| + |
| // TODO(hamaji): consider windows |
| #define PATH_SEPARATOR '/' |
| |
| @@ -429,6 +440,15 @@ namespace { |
| |
| 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: |
| @@ -459,6 +479,7 @@ class LogFileObject : public base::Logger { |
| // can avoid grabbing a lock. Usually Flush() calls it after |
| // acquiring lock_. |
| void FlushUnlocked(); |
| + void CheckFileNumQuota(); |
| |
| private: |
| static const uint32 kRolloverAttemptFrequency = 0x20; |
| @@ -476,6 +497,9 @@ class LogFileObject : public base::Logger { |
| unsigned int rollover_attempt_; |
| int64 next_flush_time_; // cycle count at which to flush log |
| WallTime start_time_; |
| + std::list<Filetime> file_list_; |
| + bool inited_; |
| + struct ::tm tm_time_; |
| |
| // Actually create a logfile using the value of base_filename_ and the |
| // optional argument time_pid_string |
| @@ -665,7 +689,7 @@ inline void LogDestination::FlushLogFiles(int min_severity) { |
| // 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(); |
| } |
| @@ -876,9 +900,12 @@ inline void LogDestination::LogToAllLogfiles(LogSeverity severity, |
| } else 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 {} |
| } |
| } |
| |
| @@ -976,6 +1003,7 @@ LogFileObject::LogFileObject(LogSeverity severity, |
| file_length_(0), |
| rollover_attempt_(kRolloverAttemptFrequency-1), |
| next_flush_time_(0), |
| + inited_(false), |
| start_time_(WallTime_Now()) { |
| assert(severity >= 0); |
| assert(severity < NUM_SEVERITIES); |
| @@ -1045,11 +1073,11 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) { |
| string_filename += filename_extension_; |
| const char* filename = string_filename.c_str(); |
| //only write to files, create if non-existant. |
| - int flags = O_WRONLY | O_CREAT; |
| - if (FLAGS_timestamp_in_logfile_name) { |
| - //demand that the file is unique for our timestamp (fail if it exists). |
| - flags = flags | O_EXCL; |
| - } |
| + int flags = O_WRONLY | O_CREAT | O_APPEND; |
| + // if (FLAGS_timestamp_in_logfile_name) { |
| + // //demand that the file is unique for our timestamp (fail if it exists). |
| + // flags = flags | O_EXCL; |
| + // } |
| int fd = open(filename, flags, FLAGS_logfile_mode); |
| if (fd == -1) return false; |
| #ifdef HAVE_FCNTL |
| @@ -1097,6 +1125,10 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) { |
| } |
| } |
| #endif |
| + 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 |
| @@ -1138,6 +1170,63 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) { |
| 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(); |
| + |
| + auto log_filenum_quota = FLAGS_log_filenum_quota; |
| + if (severity_ == GLOG_WARNING && FLAGS_warn_log_filenum_quota > 0) { |
| + log_filenum_quota = FLAGS_warn_log_filenum_quota; |
| + } |
| + while (log_filenum_quota > 0 && file_list_.size() >= 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, |
| @@ -1149,13 +1238,55 @@ void LogFileObject::Write(bool force_flush, |
| return; |
| } |
| |
| - if (file_length_ >> 20U >= 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 (file_length_ >> 20U >= 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 |
| @@ -1164,7 +1295,19 @@ void LogFileObject::Write(bool force_flush, |
| if (++rollover_attempt_ != kRolloverAttemptFrequency) return; |
| rollover_attempt_ = 0; |
| |
| - struct ::tm tm_time; |
| + if (!inited_) { |
| + CheckFileNumQuota(); |
| + inited_ = true; |
| + } else { |
| + auto log_filenum_quota = FLAGS_log_filenum_quota; |
| + if (severity_ == GLOG_WARNING && FLAGS_warn_log_filenum_quota > 0) { |
| + log_filenum_quota = FLAGS_warn_log_filenum_quota; |
| + } |
| + while (log_filenum_quota > 0 && file_list_.size() >= log_filenum_quota) { |
| + unlink(file_list_.front().name.c_str()); |
| + file_list_.pop_front(); |
| + } |
| + } |
| if (FLAGS_log_utc_time) { |
| gmtime_r(×tamp, &tm_time); |
| } else { |
| @@ -1176,13 +1319,19 @@ void LogFileObject::Write(bool force_flush, |
| 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_) { |
| @@ -1216,9 +1365,7 @@ void LogFileObject::Write(bool force_flush, |
| // 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(); |
| |
| @@ -1243,36 +1390,6 @@ void LogFileObject::Write(bool force_flush, |
| } |
| } |
| |
| - // 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 << (FLAGS_log_utc_time ? " UTC\n" : "\n") |
| - << "Running on machine: " |
| - << LogDestination::hostname() << '\n'; |
| - |
| - if(!g_application_fingerprint.empty()) { |
| - file_header_stream << "Application fingerprint: " << g_application_fingerprint << '\n'; |
| - } |
| - const char* const date_time_format = FLAGS_log_year_in_prefix |
| - ? "yyyymmdd hh:mm:ss.uuuuuu" |
| - : "mmdd hh:mm:ss.uuuuuu"; |
| - file_header_stream << "Running duration (h:mm:ss): " |
| - << PrettyDuration(static_cast<int>(WallTime_Now() - start_time_)) << '\n' |
| - << "Log line format: [IWEF]" << date_time_format << " " |
| - << "threadid file:line] msg" << '\n'; |
| - const string& file_header_string = file_header_stream.str(); |
| - |
| - const size_t 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 |