glog源码阅读笔记

C++项目中常用的日志库有glog和spdlog
之前看了一下glog的源码,一些内容记录分享一下。

glog是谷歌开源的一个日志库,使用 c++98 编写
注意:

  • glog是个同步日志库,即日志记录和落盘是在同一个线程进行
  • glog是线程安全的,不同线程之间通过互斥量来同步

源码路径

https://github.com/google/glog

代码量

vscode counter 统计代码量:
glog源码阅读笔记_第1张图片
glog源码阅读笔记_第2张图片

源码结构

glog支持bazel和cmake构建,主要的工程代码在glog/src目录下
其中 glog/src/glog/里面是头文件的模板文件,除了glog/src/glog文件夹外,glog/src里面有.cc后缀的源码实现文件
glog源码阅读笔记_第3张图片
glog源码阅读笔记_第4张图片

源码学习

glog/src/glog/logging.h.in

这个是一个模板文件,经过autotool 处理后得到 log/logging.h,即我们在使用glog时需要include的头文件
与这个头文件配套的是 glog/src/logging.cc
logging.h里的内容:

  • 各种宏定义
  • LogMessage类定义

google::InitGoogleLogging(argv[0])

在使用glog时,需要在main()里调用 InitGoogleLogging(argv[0])
其中 argv[0] 为进程第0 个参数,是程序的全名,
比如: ./test -v argv[0] -> ./test
根据下面的源码,InitGoogleLogging函数的作用就是将 test这字符串赋值给 全局变量 g_program_invocation_short_name

void InitGoogleLogging(const char* argv0) {
  glog_internal_namespace_::InitGoogleLoggingUtilities(argv0);
}
void InitGoogleLoggingUtilities(const char* argv0) {
  // 如果InitGoogleLogging被调用两次,则打印报错
  CHECK(!IsGoogleLoggingInitialized())
      << "You called InitGoogleLogging() twice!";
  // strrchr() 函数查找字符在指定字符串中从右面开始的第一次出现的位置,
  // 如果成功,返回该字符以及其后面的字符,如果失败,则返回 NULL
  const char* slash = strrchr(argv0, '/'); 
#ifdef GLOG_OS_WINDOWS
  if (!slash)  slash = strrchr(argv0, '\\');
#endif
  // 例如./test, 则 g_program_invocation_short_name = test
  g_program_invocation_short_name = slash ? slash + 1 : argv0;

#ifdef HAVE_STACKTRACE
  InstallFailureFunction(&DumpStackTraceAndExit);
#endif
}

bool IsGoogleLoggingInitialized() {
  return g_program_invocation_short_name != NULL;
}

日志等级定义

const int GLOG_INFO = 0, GLOG_WARNING = 1, GLOG_ERROR = 2, GLOG_FATAL = 3,
  NUM_SEVERITIES = 4;
#ifndef GLOG_NO_ABBREVIATED_SEVERITIES
# ifdef ERROR
#  error ERROR macro is defined. Define GLOG_NO_ABBREVIATED_SEVERITIES before including logging.h. See the document for detail.
# endif
const int INFO = GLOG_INFO, WARNING = GLOG_WARNING,
  ERROR = GLOG_ERROR, FATAL = GLOG_FATAL;
#endif

以LOG(INFO)宏为例看日志记录和落盘过程

日志记录

// severity: 严重性,表示日志等级
#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()

即LOG(INFO) 展开为:COMPACT_GOOGLE_LOG_INFO.stream()
根据下面的源码,COMPACT_GOOGLE_LOG_INFO也是一个宏,其展开为:

#if GOOGLE_STRIP_LOG == 0
#define COMPACT_GOOGLE_LOG_INFO @ac_google_namespace@::LogMessage( \
      __FILE__, __LINE__)
#else
#define COMPACT_GOOGLE_LOG_INFO @ac_google_namespace@::NullStream()
#endif

LogMessage的定义如下:LogMessage是一个类

class GLOG_EXPORT LogMessage {
public:
  enum {
    // Passing kNoLogPrefix for the line number disables the
    // log-message prefix. Useful for using the LogMessage
    // infrastructure as a printing utility. See also the --log_prefix
    // flag for controlling the log-message prefix on an
    // application-wide basis.
    kNoLogPrefix = -1
  };

  // LogStream inherit from non-DLL-exported class (std::ostrstream)
  // and VC++ produces a warning for this situation.
  // However, MSDN says "C4275 can be ignored in Microsoft Visual C++
  // 2005 if you are deriving from a type in the Standard C++ Library"
  // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
  // Let's just ignore the warning.
GLOG_MSVC_PUSH_DISABLE_WARNING(4275)
  class GLOG_EXPORT LogStream : public std::ostream {
GLOG_MSVC_POP_WARNING()
  public:
    LogStream(char *buf, int len, int64 ctr)
        : std::ostream(NULL),
          streambuf_(buf, len),
          ctr_(ctr),
          self_(this) {
      rdbuf(&streambuf_);
    }

    int64 ctr() const { return ctr_; }
    void set_ctr(int64 ctr) { ctr_ = ctr; }
    LogStream* self() const { return self_; }

    // Legacy std::streambuf methods.
    size_t pcount() const { return streambuf_.pcount(); }
    char* pbase() const { return streambuf_.pbase(); }
    char* str() const { return pbase(); }

  private:
    LogStream(const LogStream&);
    LogStream& operator=(const LogStream&);
    base_logging::LogStreamBuf streambuf_;
    int64 ctr_;  // Counter hack (for the LOG_EVERY_X() macro)
    LogStream *self_;  // Consistency check hack
  };

public:
  // icc 8 requires this typedef to avoid an internal compiler error.
  typedef void (LogMessage::*SendMethod)();

  LogMessage(const char* file, int line, LogSeverity severity, int64 ctr,
             SendMethod send_method);

  // Two special constructors that generate reduced amounts of code at
  // LOG call sites for common cases.

  // Used for LOG(INFO): Implied are:
  // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog.
  //
  // Using this constructor instead of the more complex constructor above
  // saves 19 bytes per call site.
  LogMessage(const char* file, int line);

  // Used for LOG(severity) where severity != INFO.  Implied
  // are: ctr = 0, send_method = &LogMessage::SendToLog
  //
  // Using this constructor instead of the more complex constructor above
  // saves 17 bytes per call site.
  LogMessage(const char* file, int line, LogSeverity severity);

  // Constructor to log this message to a specified sink (if not NULL).
  // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if
  // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise.
  LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink,
             bool also_send_to_log);

  // Constructor where we also give a vector pointer
  // for storing the messages (if the pointer is not NULL).
  // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog.
  LogMessage(const char* file, int line, LogSeverity severity,
             std::vector<std::string>* outvec);

  // Constructor where we also give a string pointer for storing the
  // message (if the pointer is not NULL).  Implied are: ctr = 0,
  // send_method = &LogMessage::WriteToStringAndLog.
  LogMessage(const char* file, int line, LogSeverity severity,
             std::string* message);

  // A special constructor used for check failures
  LogMessage(const char* file, int line, const CheckOpString& result);

  ~LogMessage();

  // Flush a buffered message to the sink set in the constructor.  Always
  // called by the destructor, it may also be called from elsewhere if
  // needed.  Only the first call is actioned; any later ones are ignored.
  void Flush();

  // An arbitrary limit on the length of a single log message.  This
  // is so that streaming can be done more efficiently.
  static const size_t kMaxLogMessageLen;

  // Theses should not be called directly outside of logging.*,
  // only passed as SendMethod arguments to other LogMessage methods:
  void SendToLog();  // Actually dispatch to the logs
  void SendToSyslogAndLog();  // Actually dispatch to syslog and the logs

  // Call abort() or similar to perform LOG(FATAL) crash.
  static void @ac_cv___attribute___noreturn@ Fail();

  std::ostream& stream();

  int preserved_errno() const;

  // Must be called without the log_mutex held.  (L < log_mutex)
  static int64 num_messages(int severity);

  const LogMessageTime& getLogMessageTime() const;

  struct LogMessageData;

private:
  // Fully internal SendMethod cases:
  void SendToSinkAndLog();  // Send to sink if provided and dispatch to the logs
  void SendToSink();  // Send to sink if provided, do nothing otherwise.

  // Write to string if provided and dispatch to the logs.
  void WriteToStringAndLog();

  void SaveOrSendToLog();  // Save to stringvec if provided, else to logs

  void Init(const char* file, int line, LogSeverity severity,
            void (LogMessage::*send_method)());

  // Used to fill in crash information during LOG(FATAL) failures.
  void RecordCrashReason(glog_internal_namespace_::CrashReason* reason);

  // Counts of messages sent at each priority:
  static int64 num_messages_[NUM_SEVERITIES];  // under log_mutex

  // We keep the data in a separate struct so that each instance of
  // LogMessage uses less stack space.
  LogMessageData* allocated_;
  LogMessageData* data_;
  LogMessageTime logmsgtime_;

  friend class LogDestination;

  LogMessage(const LogMessage&);
  void operator=(const LogMessage&);
};

LogMessage.stream(): 这里就构造了一个google::LogMessage的临时对象,语句执行完就会自动析构

LogMessage::LogMessage(const char* file, int line)
    : allocated_(NULL) {
  Init(file, line, GLOG_INFO, &LogMessage::SendToLog);
}

ostream& LogMessage::stream() {
  // data_是LogMessage的类成员变量
  // LogMessageData* data_;
  return data_->stream_;
}

在调用 LOG(INFO)时,创建一个临时LogMessage类对象,并把需要写入日志的信息写入到其成员变量 data_中,在LogMessage类对象退出作用域析构时执行析构函数(LogMessage的析构函数干了很重要的活),调用SendToLog()方法进行落盘(是否要真的落盘取决于缓存的日志字符串数或者缓存的时间)

日志落盘

在 临时对象LogMessage退出作用域时,执行其析构函数 ~LogMessage()
其中关键函数是 Flush()函数

LogMessage::~LogMessage() {
  Flush();
#ifdef GLOG_THREAD_LOCAL_STORAGE
  if (data_ == static_cast<void*>(&thread_msg_data)) {
    data_->~LogMessageData();
    thread_data_available = true;
  }
  else {
    delete allocated_;
  }
#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
  delete allocated_;
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
}

Flush()函数:
关键代码是 (this->*(data_->send_method_))(); send_method_ 即LogMessage::SendToLog()

// Flush buffered message, called by the destructor, or any other function
// that needs to synchronize the log.
void LogMessage::Flush() {
  if (data_->has_been_flushed_ || data_->severity_ < FLAGS_minloglevel) {
    return;
  }

  data_->num_chars_to_log_ = data_->stream_.pcount();
  data_->num_chars_to_syslog_ =
    data_->num_chars_to_log_ - data_->num_prefix_chars_;

  // Do we need to add a \n to the end of this message?
  bool append_newline =
      (data_->message_text_[data_->num_chars_to_log_-1] != '\n');
  char original_final_char = '\0';

  // If we do need to add a \n, we'll do it by violating the memory of the
  // ostrstream buffer.  This is quick, and we'll make sure to undo our
  // modification before anything else is done with the ostrstream.  It
  // would be preferable not to do things this way, but it seems to be
  // the best way to deal with this.
  if (append_newline) {
    original_final_char = data_->message_text_[data_->num_chars_to_log_];
    data_->message_text_[data_->num_chars_to_log_++] = '\n';
  }
  data_->message_text_[data_->num_chars_to_log_] = '\0';

  // Prevent any subtle race conditions by wrapping a mutex lock around
  // the actual logging action per se.
  {
    MutexLock l(&log_mutex);
    (this->*(data_->send_method_))();
    ++num_messages_[static_cast<int>(data_->severity_)];
  }
  LogDestination::WaitForSinks(data_);

  if (append_newline) {
    // Fix the ostrstream back how it was before we screwed with it.
    // It's 99.44% certain that we don't need to worry about doing this.
    data_->message_text_[data_->num_chars_to_log_-1] = original_final_char;
  }

  // If errno was already set before we enter the logging call, we'll
  // set it back to that value when we return from the logging call.
  // It happens often that we log an error message after a syscall
  // failure, which can potentially set the errno to some other
  // values.  We would like to preserve the original errno.
  if (data_->preserved_errno_ != 0) {
    errno = data_->preserved_errno_;
  }

  // Note that this message is now safely logged.  If we're asked to flush
  // again, as a result of destruction, say, we'll do nothing on future calls.
  data_->has_been_flushed_ = true;
}

LogMessage::SendToLog()定义如下:
其中调用了静态方法如 LogDestination::LogToAllLogfiles()

void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
  static bool already_warned_before_initgoogle = false;

  log_mutex.AssertHeld();

  RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
             data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");

  // Messages of a given severity get logged to lower severity logs, too

  if (!already_warned_before_initgoogle && !IsGoogleLoggingInitialized()) {
    const char w[] = "WARNING: Logging before InitGoogleLogging() is "
                     "written to STDERR\n";
    WriteToStderr(w, strlen(w));
    already_warned_before_initgoogle = true;
  }

  // global flag: never log to file if set.  Also -- don't log to a
  // file if we haven't parsed the command line flags to get the
  // program name.
  if (FLAGS_logtostderr || FLAGS_logtostdout || !IsGoogleLoggingInitialized()) {
    if (FLAGS_logtostdout) {
      ColoredWriteToStdout(data_->severity_, data_->message_text_,
                           data_->num_chars_to_log_);
    } else {
      ColoredWriteToStderr(data_->severity_, data_->message_text_,
                           data_->num_chars_to_log_);
    }

    // this could be protected by a flag if necessary.
    LogDestination::LogToSinks(data_->severity_,
                               data_->fullname_, data_->basename_,
                               data_->line_, logmsgtime_,
                               data_->message_text_ + data_->num_prefix_chars_,
                               (data_->num_chars_to_log_ -
                                data_->num_prefix_chars_ - 1) );
  } else {
    // log this message to all log files of severity <= severity_
    LogDestination::LogToAllLogfiles(data_->severity_, logmsgtime_.timestamp(),
                                     data_->message_text_,
                                     data_->num_chars_to_log_);

    LogDestination::MaybeLogToStderr(data_->severity_, data_->message_text_,
                                     data_->num_chars_to_log_,
                                     data_->num_prefix_chars_);
    LogDestination::MaybeLogToEmail(data_->severity_, data_->message_text_,
                                    data_->num_chars_to_log_);
    LogDestination::LogToSinks(data_->severity_,
                               data_->fullname_, data_->basename_,
                               data_->line_, logmsgtime_,
                               data_->message_text_ + data_->num_prefix_chars_,
                               (data_->num_chars_to_log_
                                - data_->num_prefix_chars_ - 1) );
    // NOTE: -1 removes trailing \n
  }

LogDestination::LogToAllLogfiles:
其内部调用了 LogDestination::MaybeLogToLogfile
核心为 destination->logger_->Write(should_flush, timestamp, message, len);

inline void LogDestination::LogToAllLogfiles(LogSeverity severity,
                                             time_t timestamp,
                                             const char* message,
                                             size_t len) {
  if (FLAGS_logtostdout) {  // global flag: never log to file
    ColoredWriteToStdout(severity, message, len);
  } 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);
    }
  }
}

inline void LogDestination::MaybeLogToLogfile(LogSeverity severity,
                                              time_t timestamp,
					      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);
}

destination->logger_->Write(should_flush, timestamp, message, len)如下:
这里会执行:
● 日志写入page cache
● 日志落盘
● 一些其他操作

void LogFileObject::Write(bool force_flush,
                          time_t timestamp,
                          const char* message,
                          size_t message_len) {
  MutexLock l(&lock_);

  // We don't log if the base_name_ is "" (which means "don't write")
  if (base_filename_selected_ && base_filename_.empty()) {
    return;
  }

  if (file_length_ >> 20U >= MaxLogSize() || PidHasChanged()) {
    if (file_ != NULL) fclose(file_);
    file_ = NULL;
    file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
    rollover_attempt_ = kRolloverAttemptFrequency - 1;
  }

  // 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
    // this could matter would be when we have trouble creating the log
    // file.  If that happens, we'll lose lots of log messages, of course!
    if (++rollover_attempt_ != kRolloverAttemptFrequency) return;
    rollover_attempt_ = 0;

    struct ::tm tm_time;
    if (FLAGS_log_utc_time) {
      gmtime_r(&timestamp, &tm_time);
    } else {
      localtime_r(&timestamp, &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();
    const string& time_pid_string = time_pid_stream.str();

    if (base_filename_selected_) {
      if (!CreateLogfile(time_pid_string)) {
        perror("Could not create log file");
        fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n",
                time_pid_string.c_str());
        return;
      }
    } else {
      // If no base filename for logs of this severity has been set, use a
      // default base filename of
      // "...log..".  So
      // logfiles will have names like
      // webserver.examplehost.root.log.INFO.19990817-150000.4354, where
      // 19990817 is a date (1999 August 17), 150000 is a time (15:00:00),
      // and 4354 is the pid of the logging process.  The date & time reflect
      // when the file was created for output.
      //
      // Where does the file get put?  Successively try the directories
      // "/tmp", and "."
      string stripped_filename(
          glog_internal_namespace_::ProgramInvocationShortName());
      string hostname;
      GetHostName(&hostname);

      string uidname = MyUserName();
      // We should not call CHECK() here because this function can be
      // called after holding on to log_mutex. We don't want to
      // 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_]+'.';
      // We're going to (potentially) try to put logs in several different dirs
      const vector<string> & log_dirs = GetLoggingDirectories();

      // Go through the list of dirs, and try to create the log file in each
      // until we succeed or run out of options
      bool success = false;
      for (vector<string>::const_iterator dir = log_dirs.begin();
           dir != log_dirs.end();
           ++dir) {
        base_filename_ = *dir + "/" + stripped_filename;
        if ( CreateLogfile(time_pid_string) ) {
          success = true;
          break;
        }
      }
      // If we never succeeded, we have to give up
      if ( success == false ) {
        perror("Could not create logging file");
        fprintf(stderr, "COULD NOT CREATE A LOGGINGFILE %s!",
                time_pid_string.c_str());
        return;
      }
    }

    // Write a header message into the log file
    if (FLAGS_log_file_header) {
      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
  if ( !stop_writing ) {
    // fwrite() doesn't return an error when the disk is full, for
    // messages that are less than 4096 bytes. When the disk is full,
    // it returns the message length for messages that are less than
    // 4096 bytes. fwrite() returns 4096 for message lengths that are
    // greater than 4096, thereby indicating an error.
    errno = 0;
    fwrite(message, 1, message_len, file_);
    if ( FLAGS_stop_logging_if_full_disk &&
         errno == ENOSPC ) {  // disk full, stop writing to disk
      stop_writing = true;  // until the disk is
      return;
    } else {
      file_length_ += message_len;
      bytes_since_flush_ += message_len;
    }
  } else {
    if (CycleClock_Now() >= next_flush_time_) {
      stop_writing = false;  // check to see if disk has free space.
    }
    return;  // no need to flush
  }

  // See important msgs *now*.  Also, flush logs at least every 10^6 chars,
  // or every "FLAGS_logbufsecs" seconds.
  if ( force_flush ||
       (bytes_since_flush_ >= 1000000) ||
       (CycleClock_Now() >= next_flush_time_) ) {
    FlushUnlocked();
#ifdef GLOG_OS_LINUX
    // Only consider files >= 3MiB
    if (FLAGS_drop_log_memory && file_length_ >= (3U << 20U)) {
      // Don't evict the most recent 1-2MiB so as not to impact a tailer
      // of the log file and to avoid page rounding issue on linux < 4.7
      uint32 total_drop_length =
          (file_length_ & ~((1U << 20U) - 1U)) - (1U << 20U);
      uint32 this_drop_length = total_drop_length - dropped_mem_length_;
      if (this_drop_length >= (2U << 20U)) {
        // Only advise when >= 2MiB to drop
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
        // 'posix_fadvise' introduced in API 21:
        // * https://android.googlesource.com/platform/bionic/+/6880f936173081297be0dc12f687d341b86a4cfa/libc/libc.map.txt#732
# else
        posix_fadvise(fileno(file_), static_cast<off_t>(dropped_mem_length_),
                      static_cast<off_t>(this_drop_length),
                      POSIX_FADV_DONTNEED);
# endif
        dropped_mem_length_ = total_drop_length;
      }
    }
#endif

    // Remove old logs
    if (log_cleaner.enabled()) {
      log_cleaner.Run(base_filename_selected_,
                      base_filename_,
                      filename_extension_);
    }
  }
}

在日志落盘时主要时执行LogFileObject::FlushUnlocked()函数,在该函数里关键是执行fflush库函数,将之前写入到page cache中的日志信息刷新到磁盘中去。
fflush是一个在C语言标准输入输出库中的函数,功能是冲洗流中的信息,该函数通常用于处理磁盘文件。fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中。

void LogFileObject::FlushUnlocked(){
  if (file_ != NULL) {
    fflush(file_);
    bytes_since_flush_ = 0;
  }
  // Figure out when we are due for another flush.
  const int64 next = (FLAGS_logbufsecs
                      * static_cast<int64>(1000000));  // in usec
  next_flush_time_ = CycleClock_Now() + UsecToCycles(next);
}

所以可以看出,glog是个同步类型的日志库,并不支持异步日志。

你可能感兴趣的:(c++)