一、安裝配置
1、簡介
google 出的一個C++輕量級日志庫,支持以下功能:
◆ 參數設置,以命令行參數的方式設置標志參數來控制日志記錄行為;
◆ 嚴重性分級,根據日志嚴重性分級記錄日志;
◆ 可有條件地記錄日志信息;
◆ 條件中止程序。豐富的條件判定宏,可預設程序終止條件;
◆ 異常信號處理。程序異常情況,可自定義異常處理過程;
◆ 支持debug功能;
◆ 自定義日志信息;
◆ 線程安全日志記錄方式;
◆ 系統級日志記錄;
◆ google perror風格日志信息;
◆ 精簡日志字符串信息
2、安裝
下載地址:https://code.google.com/p/google-glog/downloads/list
解壓安裝:
tar zxvf glog-0.3.3.tar.gz && cd glog-0.3.3 && ./configure && make
頭文件目錄為 /src/glog ,鏈接庫為 .libs/libglog.{a,so}
幫助文檔為 doc/glog.html 或直接訪問以下 URL: http://google-glog.googlecode.com/svn/trunk/doc/glog.html
3、簡單 Demo
#include <glog/logging.h>
int main(int argc,char* argv[]) { LOG(INFO) << "Hello,GLOG!"; }
鏈接時,需要 -lglog ,也可能會需要 -lunwind -lpthread (有一次遇到的,記不起來了,一般不需要)
執行時,可使用 ./glogdemo 將日志輸出到 stderr,可使用 valgrind 檢測,未發現內存泄漏。
二、使用方法
1、錯誤級別
GLOG 有四個錯誤級別,枚舉如下:
enum SeverityLevel
{
google::INFO = 0,
google::WARNING = 1,
google::ERROR = 2,
google::FATAL = 3,
};
2、Flags 設置:
在上面的簡單 Demo 中,只能將日志輸出到 stderr ,如果想將日志重定向到文件,需要:
google::InitGoogleLogging(argv[0]); /* GLOG代碼 */ google::ShutdownGoogleLogging();
則默認運行會將日志輸出到 /tmp 目錄下(格式為 "<program name>.<hostname>.<user name>.log.<severity level>.<date>.<time>.<pid>"),也可以使用設置臨時環境變量的方式(給命令行參數加上 GLOG_ 前綴),如 GLOG_logtostderr=1 ./your_application 將日志輸出到 stderr。
另外,glog 使用了 gflags 庫,如果已經安裝好了 gflags 庫(./configure && make && make install 然后再配置編譯GLOG庫)還可以通過 ./your_application --logtostderr=1 來指定運行參數,但需要在使用時加上如下代碼:(詳見 http://www.cnblogs.com/tianyajuanke/p/3467572.html)
google::ParseCommandLineFlags(&argc, &argv, true); /* GLOG代碼 */ google::ShutDownCommandLineFlags();
但此方法會使 valgrind 檢測有內存泄漏(截止 valgrind 3.9.0 依然如此),這是由於使用 gflags 庫造成的。(即使不使用此方法,但只要在編譯 glog 庫之前安裝好了 gflags 庫,就會使 libglog.so 有內存泄漏,介意者可進入 gflags 目錄 make uninstall 之后,再對 glog 進行 ./configure && make )。
常用的運行參數如下:
logtostderr (bool, default=false) //是否將所有日志輸出到 stderr,而非文件
alsologtostderr(bool,default=false) //是否同時將日志輸出到文件和stderr
minloglevel (int, default=google::INFO) //限制輸出到 stderr 的部分信息,包括此錯誤級別和更高錯誤級別的日志信息
stderrthreshold (int, default=google::ERROR) //除了將日志輸出到文件之外,還將此錯誤級別和更高錯誤級別的日志同時輸出到 stderr,這個只能使用 -stderrthreshold=1 或代碼中設置,而不能使用環境變量的形式。(這個參數可以替代上面兩個參數)
colorlogtostderr(bool, default=false) //將輸出到 stderr 上的錯誤日志顯示相應的顏色
v (int, default=0) //只記錄此錯誤級別和更高錯誤級別的 VLOG 日志信息
log_dir (string, default="") //設置日志文件輸出目錄
v (int, default=0) //只有當自定義日志(VLOG)級別值小於此值時,才進行輸出,默認為0(注:自定義日志的優先級與GLOG內置日志優級相反,值越小優先級越高!!!)。
vmodule (string, default="") //分文件(不包括文件名后綴,支持通配符)設置自定義日志的可輸出級別,如:GLOG_vmodule=server=2,client=3 表示文件名為server.* 的只輸出小於 2 的日志,文件名為 client.* 的只輸出小於 3 的日志。如果同時使用 GLOG_v 選項,將覆蓋 GLOG_v 選項。
更多運行參數見 logging.cc 中的 DEFINE_ 開頭的定義。
運行參數設置的第三種方法是,可以在代碼里通過加上 FLAGS_ 前綴來設置,如:
FLAGS_stderrthreshold=google::INFO; FLAGS_colorlogtostderr=true;
3、條件輸出:
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; //當條件滿足時輸出日志
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; //google::COUNTER 記錄該語句被執行次數,從1開始,在第一次運行輸出日志之后,每隔 10 次再輸出一次日志信息
LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER << "th big cookie"; //上述兩者的結合,不過要注意,是先每隔 10 次去判斷條件是否滿足,如果滯則輸出日志;而不是當滿足某條件的情況下,每隔 10 次輸出一次日志信息。
LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie"; //當此語句執行的前 20 次都輸出日志,然后不再輸出
演示代碼如下:
#include <glog/logging.h>
int main(int argc,char* argv[]) { google::InitGoogleLogging(argv[0]); FLAGS_stderrthreshold=google::INFO; FLAGS_colorlogtostderr=true; for(int i = 1; i <= 100;i++) { LOG_IF(INFO,i==100)<<"LOG_IF(INFO,i==100) google::COUNTER="<<google::COUNTER<<" i="<<i; LOG_EVERY_N(INFO,10)<<"LOG_EVERY_N(INFO,10) google::COUNTER="<<google::COUNTER<<" i="<<i; LOG_IF_EVERY_N(WARNING,(i>50),10)<<"LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER="<<google::COUNTER<<" i="<<i; LOG_FIRST_N(ERROR,5)<<"LOG_FIRST_N(INFO,5) google::COUNTER="<<google::COUNTER<<" i="<<i; } google::ShutdownGoogleLogging(); }
輸出結果如下:
I1210 13:23:20.059790 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=1 i=1 E1210 13:23:20.060670 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=1 i=1 E1210 13:23:20.061272 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=2 i=2 E1210 13:23:20.061337 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=3 i=3 E1210 13:23:20.061393 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=4 i=4 E1210 13:23:20.061450 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=5 i=5 I1210 13:23:20.061506 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=11 i=11 I1210 13:23:20.061529 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=21 i=21 I1210 13:23:20.061553 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=31 i=31 I1210 13:23:20.061575 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=41 i=41 I1210 13:23:20.061599 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=51 i=51 W1210 13:23:20.061621 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=51 i=51 I1210 13:23:20.061667 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=61 i=61 W1210 13:23:20.061691 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=61 i=61 I1210 13:23:20.061738 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=71 i=71 W1210 13:23:20.061761 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=71 i=71 I1210 13:23:20.061807 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=81 i=81 W1210 13:23:20.061831 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=81 i=81 I1210 13:23:20.061877 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=91 i=91 W1210 13:23:20.061902 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=91 i=91 I1210 13:23:20.062140 6322 test01.cpp:10] LOG_IF(INFO,i==100) google::COUNTER=0 i=100
4、日志類型
LOG //內置日志
VLOG //自定義日志
DLOG //DEBUG模式可輸出的日志
DVLOG //DEBUG模式可輸出的自定義日志
SYSLOG //系統日志,同時通過 syslog() 函數寫入到 /var/log/message 文件
PLOG //perror風格日志,設置errno狀態並輸出到日志中
RAW_LOG //線程安全的日志,需要#include <glog/raw_logging.h>
前六種的日志使用方法完全相同(包括條件日志輸出),而 RAW_LOG 使用方法比較特殊,且不支持條件日志輸出,另外不接受 colorlogtostderr 的顏色設置。自定義日志也不接受 colorlogtostderr 的顏色設置,另外其日志嚴重級別也為自定義數字,且與默認日志嚴重級別相反,數字越小嚴重級別越高。如:
1 #include <glog/logging.h>
2 #include <glog/raw_logging.h>
3
4 class GLogHelper 5 { 6 public: 7 GLogHelper(char* program) 8 { 9 google::InitGoogleLogging(program); 10 FLAGS_stderrthreshold=google::INFO; 11 FLAGS_colorlogtostderr=true; 12 FLAGS_v = 3; 13 } 14 ~GLogHelper() 15 { 16 google::ShutdownGoogleLogging(); 17 } 18 }; 19
20 int main(int argc,char* argv[]) 21 { 22 GLogHelper gh(argv[0]); 23 LOG(ERROR)<<"LOG"; 24 VLOG(3)<<"VLOG"; 25 DLOG(ERROR)<<"DLOG"; 26 DVLOG(3)<<"DVLOG"; 27 SYSLOG(ERROR)<<"SYSLOG"; 28 PLOG(ERROR)<<"PLOG"; 29 RAW_LOG(ERROR,"RAW_LOG"); 30 }
輸出結果如下:
E1211 03:04:22.718116 13083 test01.cpp:23] LOG
I1211 03:04:22.719225 13083 test01.cpp:24] VLOG
E1211 03:04:22.719297 13083 test01.cpp:25] DLOG
I1211 03:04:22.719365 13083 test01.cpp:26] DVLOG
E1211 03:04:22.719391 13083 test01.cpp:27] SYSLOG E1211 03:04:22.719650 13083 test01.cpp:28] PLOG: Success [0]
E1211 03:04:22.719650 13083 test01.cpp:29] RAW: RAW_LOG
5、CHECK 宏
當通過該宏指定的條件不成立的時候,程序會中止,並且記錄對應的日志信息。功能類似於ASSERT,區別是 CHECK 宏不受 NDEBUG 約束,在 release 版中同樣有效。
目前這個功能我暫時不需要,就不實踐了,簡單介紹下,如:
CHECK(port == 80)<<"HTTP port 80 is not exit.";
其它還有:CHECK_EQ、 CHECK_NOTNULL、CHECK_STREQ、CHECK_DOUBLE_EQ 等判斷數字、空指針,字符串,浮點數的 CHECK 宏,需要使用時可以搜索 glog/logging.h 文件中以 CHECK_ 開頭的宏定義。
此外,類似的,還有 PCHECK 和 RAW_CHECK 版本,使用方法類似,只是 RAW_CHECK 使用方法特殊,形如 RAW_CHECK(i<3,"RAW_CHECK");
6、core dumped
通過 google::InstallFailureSignalHandler(); 即可注冊,將 core dumped 信息輸出到 stderr,如:
#include <glog/logging.h> #include <string> #include <fstream>
//將信息輸出到單獨的文件和 LOG(ERROR) void SignalHandle(const char* data, int size) { std::ofstream fs("glog_dump.log",std::ios::app); std::string str = std::string(data,size); fs<<str; fs.close(); LOG(ERROR)<<str; } class GLogHelper { public: GLogHelper(char* program) { google::InitGoogleLogging(program); FLAGS_colorlogtostderr=true; google::InstallFailureSignalHandler(); //默認捕捉 SIGSEGV 信號信息輸出會輸出到 stderr,可以通過下面的方法自定義輸出方式: google::InstallFailureWriter(&SignalHandle); } ~GLogHelper() { google::ShutdownGoogleLogging(); } }; void fun() { int* pi = new int; delete pi; pi = 0; int j = *pi; } int main(int argc,char* argv[]) { GLogHelper gh(argv[0]); fun(); }
輸出的錯誤報告如下,可定位錯誤於 fun() 函數內:
E1211 06:07:04.787719 15444 test01.cpp:11] *** Aborted at 1386742024 (unix time) try "date -d @1386742024" if you are using GNU date ***
E1211 06:07:04.789120 15444 test01.cpp:11] PC: @ 0x401227 fun()
E1211 06:07:04.789481 15444 test01.cpp:11] *** SIGSEGV (@0x0) received by PID 15444 (TID 0x7f03ce478720) from PID 0; stack trace: ***
E1211 06:07:04.791168 15444 test01.cpp:11] @ 0x7f03cd505960 (unknown)
E1211 06:07:04.791453 15444 test01.cpp:11] @ 0x401227 fun()
E1211 06:07:04.791712 15444 test01.cpp:11] @ 0x40125b main
E1211 06:07:04.792908 15444 test01.cpp:11] @ 0x7f03cd4f1cdd __libc_start_main
E1211 06:07:04.793227 15444 test01.cpp:11] @ 0x400fc9 (unknown)
段錯誤 (core dumped)
如果不使用 google::InstallFailureSignalHandler(); 則只會輸出 “段錯誤” 三個字,難於排查。
7、其它常用配置
google::SetLogDestination(google::ERROR,"log/prefix_"); //第一個參數為日志級別,第二個參數表示輸出目錄及日志文件名前綴。
google::SetStderrLogging(google::INFO); //輸出到標准輸出的時候大於 INFO 級別的都輸出;等同於 FLAGS_stderrthreshold=google::INFO;
FLAGS_logbufsecs =0; //實時輸出日志
FLAGS_max_log_size =100; //最大日志大小(MB)
#define GOOGLE_STRIP_LOG 3 // 小於此級別的日志語句將在編譯時清除,以減小編譯后的文件大小,必須放在 #include 前面才有效。
8、日志文件說明
如果可執行文件名為 "test",則將日志輸出到文件后,還會生成 test.ERROR,test.WARNING,test.INFO 三個鏈接文件,分別鏈接到對應級別的日志文件。如果日志輸出超過 FLAGS_max_log_size 設置的大小,則會分為多個文件存儲,鏈接文件就會指向其中最新的對應級別的日志文件。所以當日志文件較多時,查看鏈接文件來查看最新日志挺方便的。
三、實用封裝
GLogHelper.h 如下:
#include <glog/logging.h> #include <glog/raw_logging.h>
//將信息輸出到單獨的文件和 LOG(ERROR)
void SignalHandle(const char* data, int size); class GLogHelper { public: //GLOG配置:
GLogHelper(char* program); //GLOG內存清理:
~GLogHelper(); };
GlogHelper.cpp 如下:
#include <stdlib.h> #include "GLogHelper.h"
//配置輸出日志的目錄:
#define LOGDIR "log"
#define MKDIR "mkdir -p "LOGDIR
//將信息輸出到單獨的文件和 LOG(ERROR)
void SignalHandle(const char* data, int size) { std::string str = std::string(data,size); /* std::ofstream fs("glog_dump.log",std::ios::app); fs<<str; fs.close(); */ LOG(ERROR)<<str; //也可以直接在這里發送郵件或短信通知,不過這個方法是被回調多次的(每次回調只輸出一行錯誤信息,所以如上面的記錄到文件,也需要>以追加模式方可),所以這里發郵件或短信不是很適合,不過倒是可以調用一個 SHELL 或 PYTHON 腳本,而此腳本會先 sleep 3秒左右,然后將錯
誤信息通過郵件或短信發送出去,這樣就不需要監控腳本定時高頻率執行,浪費效率了。 } //GLOG配置:
GLogHelper::GLogHelper(char* program) { system(MKDIR); google::InitGoogleLogging(program); google::SetStderrLogging(google::INFO); //設置級別高於 google::INFO 的日志同時輸出到屏幕
FLAGS_colorlogtostderr=true; //設置輸出到屏幕的日志顯示相應顏色 //google::SetLogDestination(google::ERROR,"log/error_"); //設置 google::ERROR 級別的日志存儲路徑和文件名前綴
google::SetLogDestination(google::INFO,LOGDIR"/INFO_"); //設置 google::INFO 級別的日志存儲路徑和文件名前綴
google::SetLogDestination(google::WARNING,LOGDIR"/WARNING_"); //設置 google::WARNING 級別的日志存儲路徑和文件名前綴
google::SetLogDestination(google::ERROR,LOGDIR"/ERROR_"); //設置 google::ERROR 級別的日志存儲路徑和文件名前綴
FLAGS_logbufsecs =0; //緩沖日志輸出,默認為30秒,此處改為立即輸出
FLAGS_max_log_size =100; //最大日志大小為 100MB
FLAGS_stop_logging_if_full_disk = true; //當磁盤被寫滿時,停止日志輸出
google::SetLogFilenameExtension("91_"); //設置文件名擴展,如平台?或其它需要區分的信息
google::InstallFailureSignalHandler(); //捕捉 core dumped
google::InstallFailureWriter(&SignalHandle); //默認捕捉 SIGSEGV 信號信息輸出會輸出到 stderr,可以通過下面的方法自定義輸出>方式:
} //GLOG內存清理:
GLogHelper::~GLogHelper() { google::ShutdownGoogleLogging(); }
測試文件 test.cpp 如下:
#include "GLogHelper.h"
int main(int argc,char* argv[]) { //要使用 GLOG ,只需要在 main 函數開始處添加這句即可
GLogHelper gh(argv[0]); LOG(INFO)<<"INFO"; LOG(ERROR)<<"ERROR"; }
三、自定義修改
參考:http://www.cppfans.org/1566.html
1、增加日志按天輸出
glog默認是根據進程ID是否改變和文件大小是否超過預定值來確定是否需要新建日志文件的,此處可以參考glog源碼 logging.cc 文件中的 void LogFileObject::Write 函數中
if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() || PidHasChanged()) {
我們只需要在此處加一個日期判斷就可以了,PidHasChanged() 定義於 utilities.cc 文件中,可以加一個類似的 DayHasChanged() 函數(注意 utilities.h 文件中加上函數聲明):
static int32 g_main_day = 0; bool DayHasChanged() { time_t raw_time; struct tm* tm_info; time(&raw_time); tm_info = localtime(&raw_time); if (tm_info->tm_mday != g_main_day) { g_main_day = tm_info->tm_mday; return true; } return false; }
再修改 void LogFileObject::Write 函數中的判斷條件即可:
if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() || PidHasChanged() || DayHasChanged()) {
(注:參考 http://www.cppfans.org/1566.html)