glog学习(二):glog主要接口和类分析

1.glog的主要接口如下。


#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
#define SYSLOG(severity) SYSLOG_ ## severity(0).stream()

// Initialize.
GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0);

// Shutdown
GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging();

// 设置回调接口,如果失败调用接口.
GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)());

//
Add or remove a LogSink as a consumer of logging data. Thread-safe. GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination); GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination); //设置日志扩展名称,thread-safe。 GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension( const char* filename_extension); // // 设置某种等级的日志标准输出,thread-safe. // GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity); // // 设置所有的日志标准输出. Thread-safe. // GOOGLE_GLOG_DLL_DECL void LogToStderr(); // // Make it so that all log messages of at least a particular severity are // logged via email to a list of addresses (in addition to logging to the // usual log file(s)). The list of addresses is just a string containing // the email addresses to send to (separated by spaces, say). Thread-safe. // GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity, const char* addresses); // A simple function that sends email. dest is a commma-separated // list of addressess. Thread-safe. GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest, const char *subject, const char *body); GOOGLE_GLOG_DLL_DECL const std::vector<std::string>& GetLoggingDirectories(); // For tests only: Clear the internal [cached] list of logging directories to // force a refresh the next time GetLoggingDirectories is called. // Thread-hostile. void TestOnly_ClearLoggingDirectoriesList(); // Returns a set of existing temporary directories, which will be a // subset of the directories returned by GetLogginDirectories(). // Thread-safe. GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories( std::vector<std::string>* list); // Print any fatal message again -- useful to call from signal handler // so that the last thing in the output is the fatal message. // Thread-hostile, but a race is unlikely. GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage(); // Truncate a log file that may be the append-only output of multiple // processes and hence can't simply be renamed/reopened (typically a // stdout/stderr). If the file "path" is > "limit" bytes, copy the // last "keep" bytes to offset 0 and truncate the rest. Since we could // be racing with other writers, this approach has the potential to // lose very small amounts of data. For security, only follow symlinks // if the path is /proc/self/fd/* GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path, int64 limit, int64 keep); // Truncate stdout and stderr if they are over the value specified by // --max_log_size; keep the final 1MB. This function has the same // race condition as TruncateLogFile. GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr(); // Return the string representation of the provided LogSeverity level. // Thread-safe. GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity);

 使用方法如下:

(1)初始化

(2)logdestination提供文件地址,文件前缀名,输出属性的设置接口。

(3)logmessage提供日志内容的具体输出接口,封装为LOG(…)。

(4)shutdown,析构

2.直接用到的类。

 从上述接口分析,我们可以得到我们直接接触到的类和文件。

2.1

LogMessage:This class more or less represents a particular log message.  You create an instance of LogMessage and then stream stuff to it. When you finish streaming to it, ~LogMessage is called and the full message gets streamed to the appropriate destination.从日志中我们可以看出,日志信息的输出主要看这个类,创建LogMessage的实例,获得数据流,结束后,调用析构函数~LogMessage(),然后stream流向目的文件。

LogMessage的主要成员说明,由于一些参数太长我就省略掉了,大家可以参考具体的文档。

 

LogMessage

Public:

class LogStream : public std::ostream  //

LogMessage(***);//构造函数

~LogMessage();//析构函数

void Flush();

void SendToLog(); 

void SendToSyslogAndLog();   

static void __declspec(noreturn) Fail();

std::ostream& stream();

Private:

void SendToSinkAndLog(); 

void SendToSink();

void WriteToStringAndLog();

void SaveOrSendToLog();

void Init(……);

LogMessageData* allocated_;

LogMessageData* data_;

friend class LogDestination;

LogMessage(const LogMessage&);

void operator=(const LogMessage&);

 

日志内容的具体输出接口为LOG(***);其实是LogMessage封装后的接口,##起到连接的作用。 LOG(INFO) << str;其实替换过去就是COMPACT_GOOGLE_LOG_INFO.stream()<<str;最终调用的还是LogMessage的构造函数。

 1 #define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
 2 #define SYSLOG(severity) SYSLOG_ ## severity(0).stream()
 3 
 4 #if GOOGLE_STRIP_LOG == 0
 5 #define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \
 6       __FILE__, __LINE__)
 7 #define LOG_TO_STRING_INFO(message) google::LogMessage( \
 8       __FILE__, __LINE__, google::GLOG_INFO, message)
 9 #else
10 #define COMPACT_GOOGLE_LOG_INFO google::NullStream()
11 #define LOG_TO_STRING_INFO(message) google::NullStream()
12 #endif

下面我们看看构造函数的内容,就是初始化了一些东西,文件名称,Log等级,行号等参数。

 1 LogMessage::LogMessage(const char* file, int line)
 2     : allocated_(NULL) {
 3   Init(file, line, GLOG_INFO, &LogMessage::SendToLog);
 4 }
 5 
 6 void LogMessage::Init(const char* file,int line,LogSeverity severity,void (LogMessage::*send_method)()) {/*do something*/ }

析构函数:

1 LogMessage::~LogMessage() {
2   Flush();//写数据
3 }
4 // Flush buffered message, called by the destructor, or any other function
5 // that needs to synchronize the log.
6 void LogMessage::Flush() {/*flush*/}

LogMessage中有一个友类,LogDestination,接下来我们看一下这个类。

2.2

LogDestination

LogDestination

Public:

  friend class LogMessage;

  friend void ReprintFatalMessage();

  friend base::Logger* base::GetLogger(LogSeverity);

  friend void base::SetLogger(LogSeverity, base::Logger*);

  static void SetLogDestination(LogSeverity severity, const char* base_filename);

  static void SetLogSymlink(LogSeverity severity, const char* symlink_basename);

  static void AddLogSink(LogSink *destination);

  static void RemoveLogSink(LogSink *destination);

  static void SetLogFilenameExtension(const char* filename_extension);

  static void SetStderrLogging(LogSeverity min_severity);

  static void SetEmailLogging(LogSeverity min_severity, const char* addresses);

  static void LogToStderr();

  static void FlushLogFiles(int min_severity);

  static void FlushLogFilesUnsafe(int min_severity);

  static const int kNetworkBytes = 1400;

  static const string& hostname();

  static const bool& terminal_supports_color();

  static void DeleteLogDestinations();

Private:

  LogDestination(LogSeverity severity, const char* base_filename);

  ~LogDestination() { }

  static vector<LogSink*>* sinks_;

  static Mutex sink_mutex_;

  LogDestination(const LogDestination&);

  LogDestination& operator=(const LogDestination&);

LogDestination主要提供了日志文件的名称设置接口和日志的书写方式。我们看个例子:

SetLogFilenameExtension:文件后缀名设置
效果如图

 

具体代码:
1 inline void LogDestination::SetLogFilenameExtension(const char* ext) {
2   // Prevent any subtle race conditions by wrapping a mutex lock around
3   // all this stuff.
4   MutexLock l(&log_mutex);
5   for ( int severity = 0; severity < NUM_SEVERITIES; ++severity ) {
6     log_destination(severity)->fileobject_.SetExtension(ext);
7   }
8 }
而SetExtension来自类LogFileObject,此处先不展开。我们看setExtension
 1 /*
 2 string filename_extension_;     // option users can specify (eg to add port#)
 3 */
 4 void LogFileObject::SetExtension(const char* ext) {
 5   MutexLock l(&lock_);
 6   if (filename_extension_ != ext) {
 7     // Get rid of old log file since we are changing names
 8     if (file_ != NULL) {
 9       fclose(file_);
10       file_ = NULL;
11       rollover_attempt_ = kRolloverAttemptFrequency-1;
12     }
13     filename_extension_ = ext;
14   }
15 }

可以看出LogDestination其实只是一个壳,负责和LogMessage以及其他的一些类进行互动。真正实现还得看LogFileObject类。

2.3

LogFileObject


LogFileObject : public base::Logger

  LogFileObject(LogSeverity severity, const char* base_filename);
  ~LogFileObject();

  virtual void Write(bool force_flush, // Should we force a flush here?
                     time_t timestamp,  // Timestamp for this entry
                     const char* message,
                     int message_len);

  // Configuration options
  void SetBasename(const char* basename);
  void SetExtension(const char* ext);
  void SetSymlinkBasename(const char* symlink_basename);

  // Normal flushing routine
  virtual void Flush();


  virtual uint32 LogSize() {
    MutexLock l(&lock_);
    return file_length_;
  }

  void FlushUnlocked();

  Mutex lock_;
  bool base_filename_selected_;
  string base_filename_;
  string symlink_basename_;
  string filename_extension_;   // option users can specify (eg to add port#)
  FILE* file_;
  LogSeverity severity_;
  uint32 bytes_since_flush_;
  uint32 dropped_mem_length_;
  uint32 file_length_;
  unsigned int rollover_attempt_;
  int64 next_flush_time_;

关于这个类,我们看一个函数:
 1 void LogFileObject::Write(bool force_flush,
 2                           time_t timestamp,
 3                           const char* message,
 4                           int message_len) {
 5   MutexLock l(&lock_);
 6 
 7   //条件判断,不符合直接return
 8   if (base_filename_selected_ && base_filename_.empty()) {
 9     return;
10   }
11 
12 
13     if (base_filename_selected_) 
14     {
15         /*如果没有创建日志文件,return*/
16       if (!CreateLogfile(time_pid_string)) {
17         perror("Could not create log file");
18         fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n",
19                 time_pid_string.c_str());
20         return;
21       }
22     } 
23     else
24     {
25     ostringstream file_header_stream;
26     file_header_stream.fill('0');
27 
28     file_header_stream << "Log file created at: "
29                        << 1900+tm_time.tm_year << '/'
30                        << setw(2) << 1+tm_time.tm_mon << '/'
31                        << setw(2) << tm_time.tm_mday
32                        << ' '
33                        << setw(2) << tm_time.tm_hour << ':'
34                        << setw(2) << tm_time.tm_min << ':'
35                        << setw(2) << tm_time.tm_sec << '\n'
36                        << "Running on machine: "
37                        << LogDestination::hostname() << '\n'
38                        << "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu "
39                        << "threadid file:line] msg" << '\n';
40     const string& file_header_string = file_header_stream.str();
41 
42     const int header_len = file_header_string.size();
43     fwrite(file_header_string.data(), 1, header_len, file_);
44     file_length_ += header_len;
45     bytes_since_flush_ += header_len;
46   }
47     /*写满了怎么办?*/
48   // Write to LOG file
49   if ( !stop_writing ) {
50     // fwrite() doesn't return an error when the disk is full, for
51     // messages that are less than 4096 bytes. When the disk is full,
52     // it returns the message length for messages that are less than
53     // 4096 bytes. fwrite() returns 4096 for message lengths that are
54     // greater than 4096, thereby indicating an error.
55     errno = 0;
56     fwrite(message, 1, message_len, file_);
57     if ( FLAGS_stop_logging_if_full_disk &&
58          errno == ENOSPC ) {  // disk full, stop writing to disk
59       stop_writing = true;  // until the disk is
60       return;
61     } else {
62       file_length_ += message_len;
63       bytes_since_flush_ += message_len;
64     }
65   } 
66   else {
67     if ( CycleClock_Now() >= next_flush_time_ )
68       stop_writing = false;  // check to see if disk has free space.
69     return;  // no need to flush
70   }
71 /*do otherthings*/
72 }
对应的就是以下内容
Log file created at: 2019/01/28 15:27:56
Running on machine: HostName
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
I0128 15:27:56.937649 16208 glogtest.cpp:20] hello log!
I0128 15:27:56.958592 16208 glogtest.cpp:21] info testhello log!
W0128 15:27:56.958592 16208 glogtest.cpp:22] warning test
E0128 15:27:56.958592 16208 glogtest.cpp:23] error test

我们再看一个函数:可以看出这个函数的功能就是创建文件名称。如果我们可以修改time_pid_string这个值,或者直接修改string_filename的值,就可以直接修改文件的名字,因为原始的名字真的是太长了,而且不便查找。

 1 bool LogFileObject::CreateLogfile(const string& time_pid_string) {
 2   string string_filename = base_filename_+filename_extension_+
 3                            time_pid_string;
 4   const char* filename = string_filename.c_str();
 5   int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, FLAGS_logfile_mode);
 6   if (fd == -1) return false;
 7 #ifdef HAVE_FCNTL
 8   // Mark the file close-on-exec. We don't really care if this fails
 9   fcntl(fd, F_SETFD, FD_CLOEXEC);
10 #endif
11 
12   file_ = fdopen(fd, "a");  // Make a FILE*.
13   if (file_ == NULL) {  // Man, we're screwed!
14     close(fd);
15     unlink(filename);  // Erase the half-baked evidence: an unusable log file
16     return false;
17   }
18 
19   // We try to create a symlink called <program_name>.<severity>,
20   // which is easier to use.  (Every time we create a new logfile,
21   // we destroy the old symlink and create a new one, so it always
22   // points to the latest logfile.)  If it fails, we're sad but it's
23   // no error.
24   /*do something*/
25   
26    return true;  // Everything worked
27 }

 

 

3.太深了……写不动了

 

参考网站:http://www.cnblogs.com/tianyajuanke/archive/2013/02/22/2921850.html

posted on 2019-01-29 11:18  陶大先生  阅读(3274)  评论(0编辑  收藏  举报

导航