1. 日志輸出宏 這裡我們以一條最簡單的日至輸出為例說明: LOG(WARNING) << "This is a warning message"; 這裡LOG是一個宏,其定義如下(logging.h line 487): #define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() 這裡根據LOG宏中的severity的不同有分別擴展成了另外四個宏,其中severity 有四個預定義(log_severity.h line 51-59),分別代表不同級別的日志輸出,有INFO、WARNING、ERROR、FATAL,以WARNING為例,LOG(WARNING)被擴展為COMPACT_GOOGLE_LOG_WARNING.stream()。其中COMPACT_GOOGLE_LOG_ WARNING又是另外一個宏(logging.h line 391): 復制代碼 1 #if GOOGLE_STRIP_LOG <= 1 2 #define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \ 3 __FILE__, __LINE__, google::GLOG_WARNING) 4 #define LOG_TO_STRING_WARNING(message) google::LogMessage( \ 5 __FILE__, __LINE__, google::GLOG_WARNING, message) 6 #else 7 #define COMPACT_GOOGLE_LOG_WARNING google::NullStream() 8 #define LOG_TO_STRING_WARNING(message) google::NullStream() 9 #endif 復制代碼 到這裡基本就能看出門道了,google::LogMessage和google::NullStream都是類,根據GOOGLE_STRIP_LOG的不同定義,COMPACT_GOOGLE_LOG_ WARNING被定義為LogMessage或者NullStream, NullStream比較簡單,從名字上也能測到它就是一個無輸出的流(僅僅重載了operator <<,但實際上並不輸出任何信息),用以實現某些level的日志信息不被顯式輸出)。這裡主要看LogMessage。 此時根據文件名, 行號, 日志級別構造一個LogMessage類對象(logging.cc line 1153): LogMessage::LogMessage(const char* file, int line, LogSeverity severity) : allocated_(NULL) { Init(file, line, severity, &LogMessage::SendToLog); } LogMessage有很多重載構造,這裡不再一一列舉了。注意構造裡的初始化函數Init,除了文件名, 行號, 日志級別,還多了一個參數,Init聲明如下: void Init(const char* file, int line, LogSeverity severity, void (LogMessage::*send_method)()); 即最後一個參數是一個函數指針,且可配置,用以設置真正的日志輸出,比如輸出到文件、控制台等,甚至有可能配置成輸出到遠程網絡端。Init內部用以初始化日志輸入的流緩沖區,初始化日志創建時間,格式,確定打印日志文件名等等。 此時一個完整的LogMessage的對象就創建並初始化完成了,回到LOG(severity)宏定義處,此時LOG宏可以表示成如下定義: #define LOG(severity) google::LogMessage().stream() 也即是最終被展開為google::LogMessage類的成員函數stream()的返回值,stream()實現如下: std::ostream& LogMessage::stream() { return data_->stream_; } data_->stream_是一個LogStream對象,其定義如下: 復制代碼 1 class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream { 2 public: 3 LogStream(char *buf, int len, int ctr); 4 //..............此處省略 5 private: 6 base_logging::LogStreamBuf streambuf_; 7 int ctr_; // Counter hack (for the LOG_EVERY_X() macro) 8 LogStream *self_; // Consistency check hack 9 }; 復制代碼 上面所提及的google::NullStream即是繼承自LogStream,所以也是一個std::ostream對象。 至此一個日志輸出語句, LOG(WARNING) << "This is a warning message"; 即可以表示為: google:: LogStream() << "This is a warning message"; 到這裡就會發現這個和我們熟悉的cout輸出是一樣的了: std::cout << "This is a warning message"; 一個google:: LogStream對象和std::cout都是std::ostream對象。 從上面也可以看出,每一次輸出一條日志信息都要創建一個google::LogMessage對象,在每次輸出結束後釋放LogMessage對象,在其析構函數中有如下代碼: 1 LogMessage::~LogMessage() { 2 Flush(); 3 delete allocated_; 4 } Flush成員函數即是刷新日志緩存區,相當於C++中流操作的flush或者C中文件操作的fflush。另外注意Flush實現裡有如下代碼: 復制代碼 1 //...... 2 { 3 MutexLock l(&log_mutex); 4 (this->*(data_->send_method_))(); 5 ++num_messages_[static_cast<int>(data_->severity_)]; 6 } 7 //...... 復制代碼 這是為了保證多個日志同時向同一介質進行輸出時到保持有序。注意鎖的使用前後有{}包圍。呵呵,這種用法其實我也偶爾使用,好處就是在一個比較大的語句塊中創建一個作用域更小的對象,這樣能使該對象及早釋放,避免和整個語句塊使用同一作用域。比如上面代碼中的在加鎖時使用了一個更小的作用域,該作用域結束後鎖就會立刻釋放,而不是等到Flush函數返回時才釋放,這樣就進一步提高了響應時間(其實這裡也有別的做法,比如我之前寫的文章:do{...}while(0)的妙用)。 到此一條日志輸出就算完成了,其他宏像DLOG、VLOG、VLOG_IF(帶條件檢測的輸出)都是按這種思路展開的,不再一一介紹了。 2. CHECK_XX宏 我個人感覺這類CHECK_XX宏比上面的LOG宏實現的還要隱晦難懂,當然設計的還是很巧妙的,值得學習一下,我嘗試做個分析。 在測試工程的logging_unittest.cc文件line 535的TestCHECK()函數中有如下代碼: 1 CHECK_NE(1, 2); 2 CHECK_GE(1, 1); 3 CHECK_GE(2, 1); 4 CHECK_LE(1, 1); 5 CHECK_LE(1, 2); 這些宏從名字上也能猜到是干嘛用的,他們的定義如下: 1 #define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) 2 #define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) 3 #define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) 4 #define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) 5 #define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) 6 #define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) 其中CHECK_OP宏定義如下: #define CHECK_OP(name, op, val1, val2) \ CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal) 而CHECK_OP_LOG宏定義如下: 復制代碼 1 typedef std::string _Check_string; 2 #define CHECK_OP_LOG(name, op, val1, val2, log) \ 3 while (google::_Check_string* _result = \ 4 google::Check##name##Impl( \ 5 google::GetReferenceableValue(val1), \ 6 google::GetReferenceableValue(val2), \ 7 #val1 " " #op " " #val2)) \ 8 log(__FILE__, __LINE__, \ 9 google::CheckOpString(_result)).stream() 復制代碼 接下來我們以CHECK_LE(1, 2);為例,將其逐步擴展: 復制代碼 1 CHECK_LE(1, 2) 2 ------> CHECK_OP(_LE, <=, 1, 2) 3 ------> CHECK_OP_LOG(_LE, <=, 1, 2, google::LogMessageFatal) 4 ------> #define CHECK_OP_LOG(_LE, <=, 1, 2, google::LogMessageFatal) \ 5 while (std::string * _result = \ 6 google::Check_LEImpl( \ 7 1, \ 8 2, \ 9 "1 <= 2")) \ 10 log(__FILE__, __LINE__, \ 11 google::CheckOpString(_result)).stream() 復制代碼 其中google::Check_LEImpl也是通過宏預先實現的,這個宏就是DEFINE_CHECK_OP_IMPL(Check_LE, <=): 復制代碼 1 #define DEFINE_CHECK_OP_IMPL(name, op) \ 2 template <typename T1, typename T2> \ 3 inline std::string* name##Impl(const T1& v1, const T2& v2, \ 4 const char* exprtext) { \ 5 if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \ 6 else return MakeCheckOpString(v1, v2, exprtext); \ 7 } \ 8 inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \ 9 return name##Impl<int, int>(v1, v2, exprtext); \ 10 } 復制代碼 展開後就實現了google::Check_LEImpl函數(其他與此類似,這裡只以“<=”為例說明): CHECK_LE(1, 2) ------> while (std::string * _result = google::Check_LEImpl(1, 2, "1 <= 2")) log(__FILE__, __LINE__,google::CheckOpString(_result)).stream() 其中google::Check_LEImpl又調用了模板實現的Check_LEImpl,該函數根據兩個參數v1、v2和操作符op決定了要麼返回NULL,要麼返回一個string*,如果返回NULL,則不再執行下面的輸出,否則則輸出日志信息。 其中宏GOOGLE_PREDICT_TRUE、內聯函數GetReferenceableValue、函數MakeCheckOpString、函數CheckOpString、結構體CheckOpString都是比較簡單的,就不再講了。 至此,就完成了CHECK_LE(1, 2)的擴展,如果檢測為true,則返回NULL,否則就會返回一個有明確提示信息的字符串指針,並輸出該信息,然後是程序宕掉。 比如如果驗證CHECK_LE(1, 0),因為為false,則觸發日志輸出: F0503 17:39:09.961318 4232 logging_unittest.cc:203] Check failed: 1 <= 0 (1 vs. 0) *** Check failure stack trace: *** 然後程序異常退出。 3. 在宏中使用do-while(0) 比如在logging.h的817行有如下宏定義: 1 #define CHECK_DOUBLE_EQ(val1, val2) \ 2 do { \ 3 CHECK_LE((val1), (val2)+0.000000000000001L); \ 4 CHECK_GE((val1), (val2)-0.000000000000001L); \ 5 } while (0) 這裡主要關注do-while(0)的使用,恰巧我之前也寫過一篇文章介紹do-while(0)的妙用,請看:do{...}while(0)的妙用,這裡不再多言了。 4. 使用宏進行全局初始化 請看下面的宏用法: REGISTER_MODULE_INITIALIZER(utilities, yUserNameInitializer()); REGISTER_MODULE_INITIALIZER在googleinit.h的44行定義: 1 #define REGISTER_MODULE_INITIALIZER(name, body) \ 2 namespace { \ 3 static void google_init_module_##name () { body; } \ 4 GoogleInitializer google_initializer_module_##name(#name, \ 5 google_init_module_##name); \ 6 } 其中類GoogleInitializer的實現如下: 復制代碼 1 class GoogleInitializer { 2 public: 3 typedef void (*void_function)(void); 4 GoogleInitializer(const char*, void_function f) { 5 f(); 6 } 7 }; 復制代碼 這個比較簡單,其實就是做一些比如注冊、全局初始化的工作,相應的也可以設置對應的宏用於做程序退出時的清理工作。