開源日誌系統 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
的個人部落格,謝謝關注!