天天看點

glog

一、安裝配置

1、簡介

  google 出的一個C++輕量級日志庫,支援以下功能:

glog

◆ 參數設定,以指令行參數的方式設定标志參數來控制日志記錄行為;
◆ 嚴重性分級,根據日志嚴重性分級記錄日志;
◆ 可有條件地記錄日志資訊;
◆ 條件中止程式。豐富的條件判定宏,可預設程式終止條件;
◆ 異常信号處理。程式異常情況,可自定義異常處理過程;
◆ 支援debug功能;
◆ 自定義日志資訊;
◆ 線程安全日志記錄方式;
◆ 系統級日志記錄;
◆ google perror風格日志資訊;
◆ 精簡日志字元串資訊      
glog

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 

1 #include <iostream>
2 #include "glog/logging.h"   // glog 頭檔案
3 using namespace std;
4 
5 int main(int argc, char** argv) {
6   google::InitGoogleLogging(argv[0]);    // 初始化
7   // FLAGS_log_dir=".";   
8   LOG(INFO) << "hello glog";     // 列印log:“hello glog.  類似于C++ stream。
9 }      

  連結時,需要 -lglog ,也可能會需要 -lunwind -lpthread (有一次遇到的,記不起來了,一般不需要)

  Makefile:

  LIB=$(HOME)/install/glog/lib    #glog 安裝路徑

  INCLUDE=$(HOME)/install/glog/include

  test_glog : main.o

          g++ -o $@ $^ -L$(LIB) -lglog –lpthread   #-lpthread 因為glog在多線程中需要一些鎖機制。

  main.o: main.cpp

          g++ -c -o $@ $^ -I$(INCLUDE)

  執行時,可使用 ./glogdemo 将日志輸出到 stderr,可使用 valgrind 檢測,未發現記憶體洩漏。

二、使用方法

1、錯誤級别

  GLOG 有四個錯誤級别,枚舉如下:

glog
enum SeverityLevel
{
  google::INFO = 0,
  google::WARNING = 1,
  google::ERROR = 2,
  google::FATAL = 3,
};      
glog

說明:

glog 預設對log分為4級: INFO,  WARNING,  ERROR,  FATAL.  列印log語句類似于C++中的stream,實際上LOG(INFO) 宏傳回的是一個繼承自std::ostrstream類的對象。

編譯運作上述demo, glog預設會在/tmp/目錄下生成log日志檔案:test_glog.search-x2.username.log.INFO.20111003-161341.2083

檔案名各字段對應含義為:<program name>.<hostname>.<user name>.log.<severity level>.<date>.<time>.<pid>

其中:

1)<program name> 其實對應google::InitGoogleLogging(argv[0]);中的argv[0],即通過改變google::InitGoogleLogging的參數可以修改日志檔案的名稱。

2)每個級别的日志會輸出到不同的檔案中。并且進階别日志檔案會同樣輸入到低級别的日志檔案中。 即:FATAL的資訊會同時記錄在INFO,WARNING,ERROR,FATAL日志檔案中。預設情況下,glog還會将會将FATAL的日志發送到stderr中。

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。(log_dir配置log目錄,GLOG_log_dir和FLAG_log_dir相同)

  另外,glog 使用了 gflags 庫,如果已經安裝好了 gflags 庫(./configure && make && make install 然後再配置編譯GLOG庫)還可以通過 ./your_application --logtostderr=1 來指定運作參數,但需要在使用時加上如下代碼:

google::ParseCommandLineFlags(&argc, &argv, true);
/*
GLOG代碼
*/
google::ShutDownCommandLineFlags();      

  但此方法會使 valgrind 檢測有記憶體洩漏(截止 valgrind 3.9.0 依然如此),這是由于使用 gflags 庫造成的。(即使不使用此方法,但隻要在編譯 glog 庫之前安裝好了 gflags 庫,就會使 libglog.so 有記憶體洩漏,介意者可進入 gflags 目錄 make uninstall 之後,再對 glog 進行 ./configure && make )。

  常用的運作參數如下:

glog
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 選項。      
glog

  更多運作參數見 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 次都輸出日志,然後不再輸出      

  示範代碼如下:

glog
#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();
}      
glog

   輸出結果如下:

glog
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      
glog

4、日志類型

glog
LOG    //内置日志
VLOG    //自定義日志
DLOG    //DEBUG模式可輸出的日志
DVLOG    //DEBUG模式可輸出的自定義日志
SYSLOG    //系統日志,同時通過 syslog() 函數寫入到 /var/log/message 檔案
PLOG    //perror風格日志,設定errno狀态并輸出到日志中
RAW_LOG        //線程安全的日志,需要#include <glog/raw_logging.h>      
glog

  前六種的日志使用方法完全相同(包括條件日志輸出),而 RAW_LOG 使用方法比較特殊,且不支援條件日志輸出,另外不接受  colorlogtostderr 的顔色設定。自定義日志也不接受 colorlogtostderr 的顔色設定,另外其日志嚴重級别也為自定義數字,且與預設日志嚴重級别相反,數字越小嚴重級别越高。如:

glog
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 }      
glog

  輸出結果如下:

glog
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      
glog

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,如:

glog
#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();
}      
glog

  輸出的錯誤報告如下,可定位錯誤于 fun() 函數内:

glog

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)

glog

  如果不使用  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 如下:

glog
#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();
};      
glog

  GlogHelper.cpp 如下:

glog
#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();
}      
glog

  測試檔案 test.cpp 如下:

glog
#include "GLogHelper.h"

int main(int argc,char* argv[])
{
    //要使用 GLOG ,隻需要在 main 函數開始處添加這句即可
    GLogHelper gh(argv[0]);

    LOG(INFO)<<"INFO";
    LOG(ERROR)<<"ERROR";
}      
glog

三、自定義修改

參考: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 檔案中加上函數聲明):

glog
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;
}      
glog

再修改 void LogFileObject::Write 函數中的判斷條件即可:

if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() ||
 PidHasChanged() || DayHasChanged()) {