Glog使用文檔
參考鏈接:http://www.yeolar.com/note/2014/12/20/glog/
來自Google的Glog是一個應用程序的日誌庫。它提供基於C++風格的流的日誌API,以及各種輔助的宏。打印日誌只需以流的形式傳給 LOG(level)
,例如:
#include <glog/logging.h>
int main(int argc, char* argv[]) {
// Initialize Google's logging library.
google::InitGoogleLogging(argv[0]);
// ...
LOG(INFO) << "Found " << num_cookies << " cookies";
}
Glog定義了一系列的宏來簡化記錄日誌的工作。你可以:按級別打印日誌,通過命令行控制日誌行爲,按條件打印日誌,不滿足條件時終止程序,引入自定義的日誌級別,等等。
日誌級別
可以指定下面這些級別(按嚴重性遞增排序): INFO, WARNING, ERROR, and FATAL
。打印 FATAL
消息會在打印完成後終止程序。和其他日誌庫類似,級別更高的日誌會在同級別和所有低級別的日誌文件中打印。
DFATAL
級別會在調試模式(沒有定義 NDEBUG
宏)中打印 FATAL
日誌,但是會自動降級爲 ERROR
級別,而不終止程序。
如果不指定的話,Glog輸出到文件 /tmp/...log..-
設置flag
一些flag會影響Glog的輸出行爲。如果安裝了GFlags庫,編譯時會默認使用它,這樣就可以在命令行傳遞flag(別忘了調用 ParseCommandLineFlags
初始化)。比如你想打開 --logtostderr
flag,可以這麼用:
./your_application --logtostderr=1
如果沒有安裝GFlags,那可以通過環境變量來設置,在flag名前面加上前綴 GLOG_
。比如:
GLOG_logtostderr=1 ./your_application
常用的flag有:
-
logtostderr
(bool
,默認爲false
)日誌輸出到stderr,不輸出到日誌文件。
-
colorlogtostderr
(bool
,默認爲false
)輸出彩色日誌到stderr。
-
stderrthreshold
(int
,默認爲2,即ERROR
)將大於等於該級別的日誌同時輸出到stderr。日誌級別
INFO, WARNING, ERROR, FATAL
的值分別爲0、1、2、3。 -
minloglevel
(int
,默認爲0,即INFO
)打印大於等於該級別的日誌。日誌級別的值同上。
-
log_dir
(string
,默認爲""
)指定輸出日誌文件的目錄。
-
v
(int
,默認爲0)顯示所有
VLOG(m)
的日誌,m
小於等於該flag的值。會被--vmodule
覆蓋。 -
vmodule
(string
,默認爲""
)每個模塊的詳細日誌的級別。參數爲逗號分隔的一組
<module name>=<log level>
。<module name>
支持通配(即gfs*代表所有gfs開頭的名字),匹配不包含擴展名的文件名(忽略 .cc/.h./-inl.h 等)。<log level>
會覆蓋--v
指定的值。
logging.cc 中還定義了其他一些flag。grep一下 DEFINE_
可以看到全部。
也可以通過修改 FLAGS_*
全局變量來改變flag的值。
LOG(INFO) << "file";
// Most flags work immediately after updating values.
FLAGS_logtostderr = 1;
LOG(INFO) << "stderr";
FLAGS_logtostderr = 0;
// This won't change the log destination. If you want to set this
// value, you should do this before google::InitGoogleLogging .
FLAGS_log_dir = "/some/log/directory";
LOG(INFO) << "the same file";
按條件/次數打印日誌
有時你可能只想在滿足一定條件的時候打印日誌。可以使用下面的宏來按條件打印日誌:
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
上面的日誌只有在滿足 num_cookies > 10
時纔會打印。
另一種情況,如果代碼被執行多次,可能只想對其中某幾次打印日誌。
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
上面的代碼會在執行的第1、11、21、…次時打印日誌。 google::COUNTER
用來表示是哪一次執行。
可以將這兩種日誌用下面的宏合併起來。
LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER
<< "th big cookie";
不只是每隔幾次打印日誌,也可以限制在前n次打印日誌:
LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
上面會在執行的前20次打印日誌。
調試模式
調試模式的日誌宏只在調試模式下有效,在非調試模式會被清除。可以避免生產環境的程序由於大量的日誌而變慢。
DLOG(INFO) << "Found cookies";
DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
CHECK宏
常做狀態檢查以儘早發現錯誤是一個很好的編程習慣。 CHECK
宏和標準庫中的 assert
宏類似,可以在給定的條件不滿足時終止程序。
CHECK
和 assert
不同的是,它不由 NDEBUG
控制,所以一直有效。因此下面的 fp->Write(x)
會一直執行:
CHECK(fp->Write(x) == 4) << "Write failed!";
有各種用於相等/不等檢查的宏: CHECK_EQ, CHECK_NE, CHECK_LE, CHECK_LT, CHECK_GE, CHECK_GT
。它們比較兩個值,在不滿足期望時打印包括這兩個值的 FATAL
日誌。注意這裏的值需要定義了 operator<<(ostream, ...)
。
比如:
CHECK_NE(1, 2) << ": The world must be ending!";
每個參數都可以保證只用一次,所以任何可以做爲函數參數的都可以傳給它。參數也可以是臨時的表達式,比如:
CHECK_EQ(string("abc")[1], 'b');
如果一個參數是指針,另一個是 NULL
,編譯器會報錯。可以給 NULL
加上對應類型的 static_cast
來繞過。
CHECK_EQ(some_ptr, static_cast<SomeType*>(NULL));
更好的辦法是用 CHECK_NOTNULL
宏:
CHECK_NOTNULL(some_ptr);
some_ptr->DoSomething();
該宏會返回傳入的指針,因此在構造函數的初始化列表中非常有用。
struct S {
S(Something* ptr) : ptr_(CHECK_NOTNULL(ptr)) {}
Something* ptr_;
};
因爲該特性,這個宏不能用作C++流。如果需要額外信息,請使用 CHECK_EQ
。
如果是需要比較C字符串( char*
),可以用 CHECK_STREQ, CHECK_STRNE, CHECK_STRCASEEQ, CHECK_STRCASENE
。 CASE
的版本是不區分大小寫的。這裏可以傳入 NULL
。 NULL
和任何非 NULL
的字符串是不等的,兩個 NULL
是相等的。
這裏的參數都可以是臨時字符串,比如 CHECK_STREQ(Foo().c_str(), Bar().c_str())
。
CHECK_DOUBLE_EQ
宏可以用來檢查兩個浮點值是否等價,允許一點誤差。 CHECK_NEAR
還可以傳入第三個浮點參數,指定誤差。
細節日誌
當你在追比較複雜的bug的時候,詳細的日誌信息非常有用。但同時,在通常開發中需要忽略太詳細的信息。對這種細節日誌的需求,Glog提供了 VLOG
宏,使你可以自定義一些日誌級別。通過 --v
可以控制輸出的細節日誌:
VLOG(1) << "I'm printed when you run the program with --v=1 or higher";
VLOG(2) << "I'm printed when you run the program with --v=2 or higher";
和日誌級別相反,級別越低的 VLOG
越會打印。比如 --v=1
的話, VLOG(1)
會打印, VLOG(2)
則不會打印。對 VLOG
宏和 --v
flag可以指定任何整數,但通常使用較小的正整數。 VLOG
的日誌級別是 INFO
。
細節日誌可以控制按模塊輸出:
--vmodule=mapreduce=2,file=1,gfs*=3 --v=0
會:
- 爲 mapreduce.{h,cc} 打印
VLOG(2)
和更低級別的日誌 - 爲 file.{h,cc} 打印
VLOG(1)
和更低級別的日誌 - 爲前綴爲gfs的文件打印
VLOG(3)
和更低級別的日誌 - 其他的打印
VLOG(0)
和更低級別的日誌
其中 © 給出的通配功能支持 *
(0或多個字符)和 ?
(單字符)通配符。
細節級別的條件判斷宏 VLOG_IS_ON(n)
當 --v
大於等於n時返回true。比如:
if (VLOG_IS_ON(2)) {
// do some logging preparation and logging
// that can't be accomplished with just VLOG(2) << ...;
}
此外還有 VLOG_IF, VLOG_EVERY_N, VLOG_IF_EVERY_N
,和 LOG_IF, LOG_EVERY_N, LOF_IF_EVERY
類似,但是它們傳入的是一個數字的細節級別。
VLOG_IF(1, (size > 1024))
<< "I'm printed when size is more than 1024 and when you run the "
"program with --v=1 or more";
VLOG_EVERY_N(1, 10)
<< "I'm printed every 10th occurrence, and when you run the program "
"with --v=1 or more. Present occurence is " << google::COUNTER;
VLOG_IF_EVERY_N(1, (size > 1024), 10)
<< "I'm printed on every 10th occurence of case when size is more "
" than 1024, when you run the program with --v=1 or more. ";
"Present occurence is " << google::COUNTER;
失敗信號處理
Glog庫還提供了一個信號處理器,能夠在 SIGSEGV
之類的信號導致的程序崩潰時導出有用的信息。使用 google::InstallFailureSignalHandler()
加載信號處理器。下面是它輸出的一個例子。
*** Aborted at 1225095260 (unix time) try "date -d @1225095260" if you are using GNU date ***
*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: ***
PC: @ 0x412eb1 TestWaitingLogSink::send()
@ 0x7f892fb417d0 (unknown)
@ 0x412eb1 TestWaitingLogSink::send()
@ 0x7f89304f7f06 google::LogMessage::SendToLog()
@ 0x7f89304f35af google::LogMessage::Flush()
@ 0x7f89304f3739 google::LogMessage::~LogMessage()
@ 0x408cf4 TestLogSinkWaitTillSent()
@ 0x4115de main
@ 0x7f892f7ef1c4 (unknown)
@ 0x4046f9 (unknown)
注意: InstallFailureSignalHandler()
在x86_64系統架構上可能會引發退棧的死鎖,導致遞歸地調用 malloc
。這是內置的退棧的bug,建議在安裝Glog之前安裝libunwind。更多解釋可以看Glog的 INSTALL 文件。
# apt-get install libunwind libunwind-dev
默認情況,信號處理器把失敗信息導出到stderr。可以用 InstallFailureWriter()
定製輸出位置。
其他
支持CMake
Glog並不自帶CMake支持,如果想在CMake腳本中使用它,可以把 FindGlog.cmake 添加到CMake的模塊目錄下。然後像下面這樣使用:
find_package (Glog REQUIRED)
include_directories (${GLOG_INCLUDE_DIR})
add_executable (foo main.cc)
target_link_libraries (foo glog)
性能
Glog提供的條件日誌宏(比如 CHECK, LOG_IF, VLOG, ...
)在條件判斷失敗時,不會執行右邊表達式。因此像下面這樣的檢查不會犧牲程序的性能。
CHECK(obj.ok) << obj.CreatePrettyFormattedStringButVerySlow();
自定義失敗處理函數
FATAL
級別的日誌和 CHECK
條件失敗時會終止程序。可以用 InstallFailureFunction
改變該行爲。
void YourFailureFunction() {
// Reports something...
exit(1);
}
int main(int argc, char* argv[]) {
google::InstallFailureFunction(&YourFailureFunction);
}
默認地,Glog會導出stacktrace,程序以狀態1退出。stacktrace只在Glog支持棧跟蹤的系統架構(x86和x86_64)上導出。
原始日誌
<glog/raw_logging.h>
可用於要求線程安全的日誌,它不分配任何內存,也不加鎖。因此,該頭文件中定義的宏可用於底層的內存分配和同步的代碼。
谷歌風格的perror()
PLOG(), PLOG_IF(), PCHECK()
和對應的 LOG*
和 CHECK
類似,但它們會同時在輸出中加上當前 errno
的描述。如:
PCHECK(write(1, NULL, 2) >= 0) << "Write NULL failed";
下面是它的輸出:
F0825 185142 test.cc:22] Check failed: write(1, NULL, 2) >= 0 Write NULL failed: Bad address [14]
Syslog
SYSLOG, SYSLOG_IF, SYSLOG_EVERY_N
宏會在正常日誌輸出的同時輸出到syslog。注意輸出日誌到syslog會大幅影響性能,特別是如果syslog配置爲遠程日誌輸出。所以在用它們之前一定要確定影響,一般來說很少使用。
跳過日誌
打印日誌的代碼中的字符串會增加可執行文件的大小,而且也會帶來泄密的風險。可以通過使用 GOOGLE_STRIP_LOG
宏來刪除所有低於特定級別的日誌:
比如使用下面的代碼:
#define GOOGLE_STRIP_LOG 1 // this must go before the #include!
#include <glog/logging.h>
編譯器會刪除所有級別低於該值的日誌。因爲 VLOG
的日誌級別是 INFO
(等於0),設置 GOOGLE_STRIP_LOG
大於等於1會刪除所有 VLOG
和 INFO
日誌。
Windows用戶的注意事項
Glog定義的 ERROR
日誌級別,和 windows.h 中的定義有衝突。可以在引入 glog/logging.h 之前定義 GLOG_NO_ABBREVIATED_SEVERITIES
,這樣Glog就不會定義 INFO, WARNING, ERROR, FATAL
。不過你仍然可以使用原來的宏:
#define GLOG_NO_ABBREVIATED_SEVERITIES
#include <windows.h>
#include <glog/logging.h>
// ...
LOG(ERROR) << "This should work";
LOG_IF(ERROR, x > y) << "This should be also OK";
但是你不能再在 glog/logging.h 中的函數中使用 INFO, WARNING, ERROR, FATAL
了。
#define GLOG_NO_ABBREVIATED_SEVERITIES
#include <windows.h>
#include <glog/logging.h>
// ...
// This won't work.
// google::FlushLogFiles(google::ERROR);
// Use this instead.
google::FlushLogFiles(google::GLOG_ERROR);
如果不需要使用 windows.h 中定義的 ERROR
,那麼也可以嘗試下面的方法:
- 在引入 windows.h 之前
#define WIN32_LEAN_AND_MEAN
或NOGDI
。 - 在引入 windows.h 之後
#undef ERROR
。
參考文獻
http://www.yeolar.com/note/2014/12/20/glog/