1. 程式人生 > >開源日誌系統 log4c 使用心得+總結

開源日誌系統 log4c 使用心得+總結

本人最近研究了一下開源的日誌系統log4c。簡單總結一下:

一、安裝:

我看網上好多人介紹log4c安裝的時候都說有兩個步驟:先下載expat安裝包並安裝expat,然後下載log4c安裝包並安裝log4c。這麼看來,log4c是依賴expat的。但是有時候我們不想使用的日誌系統還要依賴別的庫,畢竟現在的開源日誌系統很多,這樣一來log4c就沒有那麼大的優勢了。所以我仔細看了log4c的README文件,發現log4c模組預設情況下是使用expat庫來作為XML檔案的解析器(因為log4c的配置檔案預設是一個叫log4crc的XML檔案),我們可以在執行配置檔案的時候加上--without-expat選項就可以不使用expat庫而使用log4c自定義的解析器,該解析器是使用lex/yacc的程式碼進行解析的。
安裝步驟跟很多其他的庫一樣,都是三個步驟:

./configure
 make
 make install


我們可以在configure的時候加一些選項,如果要設定log4c的安裝路徑為/usr/local/log4c,我們就可以加--prefix=/usr/local/log4c,如果不想依賴expat解析器,我們可以加--without-expat。如果我們要指定軟體執行的系統平臺,交叉環境下,我們可以用--host選項來設定,如果執行在arm平臺下就加--host=arm-linux,如果是執行在mips平臺下就加--host=mips-linux。

如果安裝完的時候出現了以下錯誤,不要著急:
../../src/log4c/.libs/liblog4c.so: undefined reference to `rpl_malloc'
../../src/log4c/.libs/liblog4c.so: undefined reference to `rpl_realloc'
解決方法如下:
修改log4c_build/log4c-1.2.1/src/config.h.in檔案:
將201行的#undef malloc註釋掉。
將204行的#undef realloc註釋掉。
然後執行以下命令:

 ./configure(同樣有必要的情況下加上相應的選項)
 make clean
 make
 make install


二、介紹一下log4c的配置檔案log4crc:

log4c中有三個重要的概念, category, appender, layout。
1. category(型別)用於區分不同的logger, 其實它就是個logger。在一個程式中我們可以通過category來指定很多的logger,用於不同的目的。
2. appdender用於描述輸出流,通過為category來指定一個appdender,可以決定將log資訊來輸出到什麼地方去,比如stdout, stderr, 檔案, 或者是socket等等。說說常見的兩種,stdout是輸出到控制檯,檔案當然就是輸出到檔案咯,在log4c中預設的是使用輪詢檔案儲存日誌,假如我們設定的檔名為wlanLog(配置檔案中是appender節點的prefix屬性),檔案的maxsize設定為102400(Bytes),檔案的maxnum為10(檔案的最多個數),那麼日誌會儲存在wlanLog.0檔案中,當該檔案的大小達到102400Bytes是就會自動儲存到wlanLog.1檔案中,依次類推,當檔案的個數達到maxnum且檔案已滿,接下來會自動儲存到wlanLog.0檔案中,這樣迴圈儲存的方式就是輪詢。
3. layout用於指定日誌資訊的格式,通過為appender來指定一個layout,可以決定log資訊以何種格式來輸出,比如是否有帶有時間戳, 是否包含檔案位置資訊等,以及他們在一條log資訊中的輸出格式的等,一般有basic和dated兩種。大家感興趣可以分別去試一下看看日誌有什麼區別。
最後,說一下log4crc檔案放在專案工程生成的目標檔案的那個目錄下。

三、使用:

// 初始化
log4c_init();

// 獲取一個已有的category,這個category(此處為WLAN_Console)必須先配置到配置檔案中。
log4c_category_t* mycat = log4c_category_get("WLAN_Console");

// 用該category進行日誌輸出,日誌的型別為DEBUG,輸出資訊為 "Hello World!",
log4c_category_log(mycat, LOG4C_PRIORITY_DEBUG, "Hello World!");

// 去初始化
log4c_fini();

// log4c_category_log的原型為:
static LOG4C_INLINE void log4c_category_log(const log4c_category_t* a_category,
	int a_priority,
	const char* a_format,
	...)
// 其中後面的日誌輸出的格式化字串a_format跟printf的輸出格式化字串一樣,後面的參量表也和printf一樣。非常方便!


log4c的日誌優先順序有11個,在src/log4c/目錄下的priority.h中。我們常用的也就error、warn、info、debug和trace。
/**
 * Predefined Levels of priorities. These correspond to the priority levels
 * used by syslog(3).
 **/
 typedef enum {
    /** fatal */        LOG4C_PRIORITY_FATAL    = 000, 
    /** alert */        LOG4C_PRIORITY_ALERT    = 100, 
    /** crit */         LOG4C_PRIORITY_CRIT     = 200, 
    /** error */        LOG4C_PRIORITY_ERROR    = 300, 
    /** warn */         LOG4C_PRIORITY_WARN     = 400, 
    /** notice */       LOG4C_PRIORITY_NOTICE   = 500, 
    /** info */         LOG4C_PRIORITY_INFO     = 600, 
    /** debug */        LOG4C_PRIORITY_DEBUG    = 700,
    /** trace */        LOG4C_PRIORITY_TRACE    = 800,
    /** notset */       LOG4C_PRIORITY_NOTSET   = 900,
    /** unknown */      LOG4C_PRIORITY_UNKNOWN  = 1000
} log4c_priority_level_t;



有時候為了方便,我們可以將log4c_category_log用巨集定義封裝起來,這個網上有例子,我給個連結吧:

我們也可以用一個函式封裝起來,這個網上我沒找到,我就把我作的封裝分享一下吧,因為這個牽扯到變參函式的引數傳遞,所以可能好多剛接觸到變參函式的童鞋不是很清楚。

/**********************************************************************
函式名稱          :		  logOut
建立日期          :		  2011-12-27
作者              :  丶小小小威
函式描述          :		  將日誌輸出到控制檯 
輸入引數          :
	const LOG_LEVEL level :	  日誌輸出的級別
	const char *format    :	  日誌輸出的格式化字串
輸出引數          : 
	無
返回值            :  
	無
**********************************************************************/ 
void CLogger::logOut(const LOG_LEVEL level, const char *format, ...)
{
    char temp[MAX_LEN] = {0};
    int ret = 0;

    va_list ap;
    va_start(ap, format);
    ret = vsnprintf(temp, MAX_LEN, format, ap);
    assert((-1<ret) && (MAX_LEN>ret));

    switch (m_logMode)
    {
        case TO_CONSOLE_AND_FILE:    //既輸出到控制檯又輸出到檔案
        {
            if (m_consoleCategory && m_fileCategory)
            {
                log4c_category_log(m_consoleCategory, level, "%s", temp);
                log4c_category_log(m_fileCategory, level, "%s", temp);
            }

            break;
        }

        case TO_CONSOLE:             //輸出到控制檯
        {
            log4c_category_log(m_consoleCategory, level, "%s", temp);

            break;
        }

        case TO_FILE:                //輸出到檔案
        {
            log4c_category_log(m_fileCategory, level, "%s", temp);

            break;
        }

        default:
            break;
    }

    va_end(ap);
}



說明:我這裡的LOG_LEVEL是我自己定義的一個列舉型別,成員為常用的幾種日誌型別(或者說優先順序),m_logMode是我Log模組封裝類的一個成員變數,表示日誌輸出的方式,上面的三種。我想這個我就不用多說了吧,你懂的,= =!

下面呼叫的時候也很簡單:

char c = '\x41';
char s[20];
const char *p = "How do you do";
int a = 1234;
int ha = 12;
int *i;
i = &ha;
float f = 3.141592653589;
double x = 0.12345678987654321;
strcpy(s, "Hello, Comrade");

logger->logOut(ERROR, "a=%d", a);		/*結果輸出十進位制整數a=1234*/
printf("===============================> a=%d\n\n", a);

logger->logOut(ERROR, "a=%6d", a);	/*結果輸出6位十進位制整數a=  1234*/
printf("===============================> a=%6d\n\n", a);

logger->logOut(ERROR, "a=%06d", a);	/*結果輸出6位十進位制整數a=001234*/
printf("===============================> a=%06d\n\n", a);

logger->logOut(ERROR, "a=%2d", a);	/*a超過2位,按實際值輸出a=1234*/
printf("===============================> a=%2d\n\n", a);

logger->logOut(ERROR, "*i=%4d", *i);	/*輸出4位十進位制整數×i=  12*/
printf("===============================> *i=%4d\n\n", *i);

logger->logOut(ERROR, "*i=%-4d", *i);	/*輸出左對齊4位十進位制整數×i=12*/
printf("===============================> *i=%-4d\n\n", *i);

logger->logOut(ERROR, "i=%p", i);		/*輸出地址i=0xbf96538c*/
printf("===============================> i=%p\n\n", i);

logger->logOut(ERROR, "f=%f", f);		/*輸出浮點數f=3.141593*/
printf("===============================> f=%f\n\n", f);

logger->logOut(ERROR, "f=6.4f", f);	/*輸出6位其中小數點後4位的浮點數 f=3.1416*/
printf("===============================> f=6.4f\n", f);

logger->logOut(ERROR, "x=%lf", x);	/*輸出長浮點數x=0.123457*/
printf("===============================> x=%lf\n\n", x);

logger->logOut(ERROR, "x=%18.16lf", x);	/*輸出18位其中小數點後16位的長浮點數 x=0.1234567898765432*/
printf("===============================> x=%18.16lf\n\n", x);

logger->logOut(ERROR, "c=%c", c);		/*輸出字元c=A*/
printf("===============================> c=%c\n\n", c);

logger->logOut(ERROR, "c=%x", c);		/*輸出字元ASCII碼值c=41*/
printf("===============================> c=%x\n\n", c);

logger->logOut(ERROR, "s[]=%s", s);	/*輸出陣列字串s[]=Hello, Comrade*/
printf("===============================> s[]=%s\n\n", s);

logger->logOut(ERROR, "s[]=%6.9s", s);	/*輸出最多9個字元的字串s[]=Hello, Co*/
printf("===============================> s[]=%6.9s\n\n", s);

logger->logOut(ERROR, "s=%p", s);		/*輸出陣列字串首字元地址s=FFBE*/
printf("===============================> s=%p\n\n", s);

logger->logOut(ERROR, "*p=%s", p);	/*輸出指標字串p=How do you do*/
printf("===============================> *p=%s\n\n", p);

logger->logOut(ERROR, "p=%p", p);		/*輸出指標的值p=0x8049a70*/
printf("===============================> p=%p\n\n", p);
說明:logger是我寫的日誌封裝類的一個物件,大家可以把上面的部分程式碼加到自己的程式中,看看輸出結果和printf到底是不是一樣?我測試過,完全一樣,這樣一看,使用log4c日誌模組更讓人一目瞭然,尤其是在一個比較大的專案中,有一個好的日誌模組將是非常重要的!差點忘了還有個編譯執行。

四、編譯執行:

編譯的時候要連結上庫和標頭檔案。編譯的格式如下:

mips-linux-g++  -I /usr/local/include/ -I /usr/local/include/libxml2/  logger.cpp main.cpp  -o capture -L /usr/local/lib/ -lstdc++ -llog4c -lxml2



說明:我用的是mips,所以編譯時使用mips-linux-g++,我的log4c安裝預設的路徑下:/usr/local。這裡用到的libxml2是因為我涉及修改log4c的配置檔案log4crc,是一個XML檔案,我選擇libxml2。在mips上執行的時候要先告訴mips庫所在的位置,使用export LD_LIBRARY_PATH=“庫所在的位置”,建議大家寫個Makefile。

五、log4crc配置檔案:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE log4c SYSTEM "">
<log4c version="1.2.1">
	<config>
		<bufsize>0</bufsize>
		<debug level="2"/>
		<nocleanup>0</nocleanup>
		<reread>1</reread>
	</config>

	<category name="root" priority="notice"/>
    <category name="six13log.log" priority="error" appender="stdout"/>

    <!--輸出到控制檯-->
    <category name="WLAN_Console" priority="trace" appender="stdout"/>

    <!--儲存日誌到檔案-->
    <category name="WLAN_File" priority="trace" appender="myrollingfileappender"/>

    <!--logdir為日誌輸出路徑  prefix為檔名  layout為輸出格式 --> 
    <appender name="myrollingfileappender" type="rollingfile" logdir="." prefix="wlan_log" layout="dated" rollingpolicy="myrollingpolicy"/>
  
    <!--sizewin表示達到最大值後新建日誌檔案  值由maxsize設定,單位Bytes     maxnum為最大檔案數目-->
    <rollingpolicy name="myrollingpolicy" type="sizewin" maxsize="102400" maxnum="10"/>
	
	<appender name="stdout" type="stream" layout="basic"/>
	<appender name="stderr" type="stream" layout="dated"/>
	<appender name="syslog" type="syslog" layout="basic"/>

	<appender name="s13file" type="s13_file" layout="basic"/>
	<appender name="plain_stderr" type="s13_stderr" layout="none"/>
	<appender name="cat_stderr" type="s13_stderr" layout="catlayout"/>
	<appender name="xml_stderr" type="s13_stderr" layout="xmllayout"/>
    <appender name="user_stderr" type="s13_stderr" layout="userlayout"/>

	<layout name="basic" type="basic"/>
	<layout name="dated" type="dated"/>
	
	<layout name="catlayout" type="s13_cat"/>
	<layout name="xmllayout" type="s13_xml"/>
    <layout name="none" type="s13_none"/>
	<layout name="userlayout" type="s13_userloc"/>
	
	<category name="six13log.log.app.application2" priority="debug" appender="cat_stderr"/>
	<category name="six13log.log.app.application3" priority="debug" appender="user_stderr"/>
    <category name="six13log.log.app" priority="debug" appender="myrollingfileappender"/>
	<category name="log4c.examples.helloworld" priority="debug" appender="stdout"/>
</log4c>

 Over~~~
  最後祝大家工作順利!有什麼不準確的地方希望大家提出來,共同探討。謝謝~

博主所有文章已轉自私人部落格 Joe 的個人部落格,謝謝關注!