| --- glog-0.3.3/src/glog/logging.h.in 2013-02-01 14:20:46.000000000 +0800 |
| +++ glog-0.3.3/src/glog/logging.h.in 2016-03-18 10:32:00.127125712 +0800 |
| @@ -316,6 +316,11 @@ |
| using fLS::FLAGS_##name |
| #endif |
| |
| +DECLARE_int32(log_filenum_quota); |
| + |
| +DECLARE_string(log_split_method); |
| + |
| + |
| // Set whether log messages go to stderr instead of logfiles |
| DECLARE_bool(logtostderr); |
| |
| --- glog-0.3.3/src/logging.cc 2013-02-01 14:20:46.000000000 +0800 |
| +++ glog-0.3.3/src/logging.cc 2016-03-18 10:37:18.331126494 +0800 |
| @@ -48,13 +48,17 @@ |
| #include <iostream> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| +#include <dirent.h> |
| #ifdef HAVE_PWD_H |
| # include <pwd.h> |
| #endif |
| #ifdef HAVE_SYSLOG_H |
| # include <syslog.h> |
| #endif |
| +#include <algorithm> |
| #include <vector> |
| +#include <list> |
| +#include <string> |
| #include <errno.h> // for errno |
| #include <sstream> |
| #include "base/commandlineflags.h" // to get the program name |
| @@ -170,6 +174,12 @@ |
| "approx. maximum log file size (in MB). A value of 0 will " |
| "be silently overridden to 1."); |
| |
| +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_bool(stop_logging_if_full_disk, false, |
| "Stop attempting to log to disk if the disk is full."); |
| |
| @@ -351,6 +361,15 @@ |
| |
| 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: |
| @@ -382,6 +401,8 @@ |
| // acquiring lock_. |
| void FlushUnlocked(); |
| |
| + void CheckFileNumQuota(); |
| + |
| private: |
| static const uint32 kRolloverAttemptFrequency = 0x20; |
| |
| @@ -396,6 +417,9 @@ |
| 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 |
| @@ -414,7 +438,7 @@ |
| |
| // These methods are just forwarded to by their global versions. |
| static void SetLogDestination(LogSeverity severity, |
| - const char* base_filename); |
| + const char* base_filename); |
| static void SetLogSymlink(LogSeverity severity, |
| const char* symlink_basename); |
| static void AddLogSink(LogSink *destination); |
| @@ -446,17 +470,17 @@ |
| // Take a log message of a particular severity and log it to stderr |
| // iff it's of a high enough severity to deserve it. |
| static void MaybeLogToStderr(LogSeverity severity, const char* message, |
| - size_t len); |
| + size_t len); |
| |
| // Take a log message of a particular severity and log it to email |
| // iff it's of a high enough severity to deserve it. |
| static void MaybeLogToEmail(LogSeverity severity, const char* message, |
| - size_t len); |
| + size_t len); |
| // Take a log message of a particular severity and log it to a file |
| // iff the base filename is not "" (which means "don't log to me") |
| static void MaybeLogToLogfile(LogSeverity severity, |
| time_t timestamp, |
| - const char* message, size_t len); |
| + const char* message, size_t len); |
| // Take a log message of a particular severity and log it to the file |
| // for that severity and also for all files with severity less than |
| // this severity. |
| @@ -531,7 +555,7 @@ |
| // assume we have the log_mutex or we simply don't care |
| // about it |
| for (int i = min_severity; i < NUM_SEVERITIES; i++) { |
| - LogDestination* log = log_destination(i); |
| + LogDestination* log = log_destinations_[i]; |
| if (log != NULL) { |
| // Flush the base fileobject_ logger directly instead of going |
| // through any wrappers to reduce chance of deadlock. |
| @@ -620,7 +644,7 @@ |
| } |
| |
| inline void LogDestination::SetEmailLogging(LogSeverity min_severity, |
| - const char* addresses) { |
| + const char* addresses) { |
| assert(min_severity >= 0 && min_severity < NUM_SEVERITIES); |
| // Prevent any subtle race conditions by wrapping a mutex lock around |
| // all this stuff. |
| @@ -673,7 +697,7 @@ |
| } |
| |
| inline void LogDestination::MaybeLogToStderr(LogSeverity severity, |
| - const char* message, size_t len) { |
| + const char* message, size_t len) { |
| if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) { |
| ColoredWriteToStderr(severity, message, len); |
| #ifdef OS_WINDOWS |
| @@ -712,8 +736,8 @@ |
| |
| inline void LogDestination::MaybeLogToLogfile(LogSeverity severity, |
| time_t timestamp, |
| - const char* message, |
| - size_t len) { |
| + const char* message, |
| + size_t len) { |
| const bool should_flush = severity > FLAGS_logbuflevel; |
| LogDestination* destination = log_destination(severity); |
| destination->logger_->Write(should_flush, timestamp, message, len); |
| @@ -727,8 +751,12 @@ |
| 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 {} |
| } |
| } |
| |
| @@ -793,7 +821,8 @@ |
| bytes_since_flush_(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); |
| } |
| @@ -858,7 +887,7 @@ |
| 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, 0664); |
| + int fd = open(filename, O_WRONLY | O_CREAT /* | O_EXCL */ | O_APPEND, 0664); |
| if (fd == -1) return false; |
| #ifdef HAVE_FCNTL |
| // Mark the file close-on-exec. We don't really care if this fails |
| @@ -872,6 +901,10 @@ |
| 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 |
| @@ -911,6 +944,59 @@ |
| 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, |
| @@ -922,14 +1008,55 @@ |
| 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_ = 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_EXCL */ | O_APPEND, 0664); |
| + 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_ = fdopen(fd, "a"); // Make a FILE*. |
| + if (file_ == NULL) { // Man, we're screwed!, try to create new log file |
| + close(fd); |
| + } |
| + 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 |
| @@ -938,21 +1065,35 @@ |
| if (++rollover_attempt_ != kRolloverAttemptFrequency) return; |
| rollover_attempt_ = 0; |
| |
| - struct ::tm tm_time; |
| - localtime_r(×tamp, &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 |
| ostringstream time_pid_stream; |
| 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_) { |
| @@ -986,9 +1127,7 @@ |
| // 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(); |
| |
| @@ -1012,7 +1151,8 @@ |
| return; |
| } |
| } |
| - |
| + |
| + /* |
| // Write a header message into the log file |
| ostringstream file_header_stream; |
| file_header_stream.fill('0'); |
| @@ -1034,6 +1174,7 @@ |
| fwrite(file_header_string.data(), 1, header_len, file_); |
| file_length_ += header_len; |
| bytes_since_flush_ += header_len; |
| + */ |
| } |
| |
| // Write to LOG file |