acl 日志记录方式介绍
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
? ? ? 在使用 acl 庫編寫應(yīng)用過程中,記錄日志是一個非常重要的過程,acl 從幾個層面提供了日志的不同記錄方式。在 acl 的 C 庫部分(lib_acl.a),有三個源文件與日志記錄相關(guān):acl_msg.c/acl_msg.h, acl_mylog.c/acl_mylog.h, acl_debug.c/acl_debug.h。其中,acl_mylog.c 是真正記錄日志的源文件,acl_msg.c 則是在 acl_mylog.c 基礎(chǔ)之上的二次封裝,acl_debug.c 是在 acl_msg.c 基礎(chǔ)之上的再次封裝。下面根據(jù)此三個日志源文件從三個層次描述日志記錄的過程。
?
? ? ? 一、ac_mylog.c/acl_mylog.h
? ? ? 打開 acl_mylog.h 頭文件,可以看到主要有三個函數(shù):acl_open_log(打開日志文件),acl_write_to_log(寫日志)以及acl_close_log(關(guān)閉日志)---(這三個函數(shù)是最基礎(chǔ)的日志記錄過程,當然我們不必直接使用)。該庫支持兩類日志記錄方式:1、本地文件記錄方式,2、與 syslog-ng 結(jié)合的網(wǎng)絡(luò)日志記錄方式。本地文件記錄方式是 acl 日志庫對外提供的最簡單的日志記錄方式,此方式不依賴于第三方日志庫,但不應(yīng)用在生產(chǎn)環(huán)境中,因為該方式不支持日志回滾等高級特性,為了便于生產(chǎn)上使用,所以產(chǎn)生了第二種方式(與 syslog-ng 結(jié)合),查看日志打開接口(如下):
/*** 打開日志文件* @param recipients {const char*} 日志接收器列表,由 "|" 分隔,接收器* 可以是本地文件或遠程套接口,如:* /tmp/test.log|UDP:127.0.0.1:12345|TCP:127.0.0.1:12345|UNIX:/tmp/test.sock* 該配置要求將所有日志同時發(fā)給 /tmp/test.log, UDP:127.0.0.1:12345,* TCP:127.0.0.1:12345 和 UNIX:/tmp/test.sock 四個日志接收器對象* @param plog_pre {const char*} 日志記錄信息前的提示信息,建議用進程* 名填寫此值*/ ACL_API int acl_open_log(const char *recipients, const char *plog_pre);? ? ? ?從上面的函數(shù)聲明可以看出,acl 的日志記錄允許同時輸出至多個日志管道中(最簡單的方式就是直接寫入本地磁盤文件:/tmp/test.log),同時更應(yīng)看到,其中有三個奇怪的日志文件表達方式:UDP:IP:PORT, TCP:IP:PORT, UNIX:/xxx,其實這三種方式均是與 syslog-ng 相關(guān),即分別表示:
? ? ? 1、以 UDP 方式發(fā)送日志至 syslog-ng;
? ? ? 2、以 TCP 方式發(fā)送日志至 syslog-ng;
? ? ? 3、以 UNIX 域套接字方式發(fā)送日志至 syslog-ng。
? ? ? 因為日志管理是一個非常復(fù)雜的過程,所以在 acl 除了提供最簡單的日志文件記錄外,更建議用戶將日志輸出至 syslog-ng 中(作者自己的項目也往往是這樣做的)。
?
? ? ? 二、acl_msg.c/acl_msg.h
? ? ? 該日志庫提供了更為高級的日志記錄方法,不僅提供了靈活的日志記錄函數(shù),同時還允許用戶注冊自己的日志記錄函數(shù)庫,該日志庫主要函數(shù)接口如下:
/*** 日志打開函數(shù)* @param log_file {const char*} 日志接收者集合,由 "|" 分隔,接收器* 可以是本地文件或遠程套接口,如:* /tmp/test.log|UDP:127.0.0.1:12345|TCP:127.0.0.1:12345|UNIX:/tmp/test.sock* 該配置要求將所有日志同時發(fā)給 /tmp/test.log, UDP:127.0.0.1:12345,* TCP:127.0.0.1:12345 和 UNIX:/tmp/test.sock 四個日志接收器對象* @param plog_pre {const char*} 日志記錄信息前的提示信息,建議用進程* @param info_pre {const char*} 日志記錄信息前的提示信息*/ ACL_API void acl_msg_open(const char *log_file, const char *info_pre);/*** 關(guān)閉日志函數(shù)*/ ACL_API void acl_msg_close(void);? ? ? ?上面是日志打開與關(guān)閉的函數(shù),看上去算是相對簡單。下面是幾個日志記錄的函數(shù)接口:
/*** 一般級別日志信息記錄函數(shù)* @param fmt {const char*} 參數(shù)格式* @param ... 變參序列*/ #ifdef WIN32 ACL_API void acl_msg_info(const char *fmt,...); #else ACL_API void __attribute__((format(printf,1,2)))acl_msg_info(const char *fmt,...); #endif/*** 警告級別日志信息記錄函數(shù)* @param fmt {const char*} 參數(shù)格式* @param ... 變參序列*/ #ifdef WIN32 ACL_API void acl_msg_warn(const char *fmt,...); #else ACL_API void __attribute__((format(printf,1,2)))acl_msg_warn(const char *fmt,...); #endif/*** 錯誤級別日志信息記錄函數(shù)* @param fmt {const char*} 參數(shù)格式* @param ... 變參序列*/ #ifdef WIN32 ACL_API void acl_msg_error(const char *fmt,...); #else ACL_API void __attribute__((format(printf,1,2)))acl_msg_error(const char *fmt,...); #endif/*** 致命級別日志信息記錄函數(shù)* @param fmt {const char*} 參數(shù)格式* @param ... 變參序列*/ #ifdef WIN32 ACL_API void acl_msg_fatal(const char *fmt,...); #else ACL_API void __attribute__((format(printf,1,2)))acl_msg_fatal(const char *fmt,...); #endif/*** 恐慌級別日志信息記錄函數(shù)* @param fmt {const char*} 參數(shù)格式* @param ... 變參序列*/ #ifdef WIN32 ACL_API void acl_msg_panic(const char *fmt,...); #else ACL_API void __attribute__((format(printf,1,2)))acl_msg_panic(const char *fmt,...); #endif? ? ? ?可以看到,這些函數(shù)的使用方式與 printf 類似,另外,在 UNIX 下使用 GCC 編譯時前面還有一個修飾符:__attribute__((format(printf,m,n))),這主要是方便 gcc 編譯器針對變參進行語法檢查(大家應(yīng)該知道變參是如此方便靈活而又如此容易出錯)。
? ? ? 為了方便程序開發(fā)過程中的調(diào)試,下面的函數(shù)當用戶未調(diào)用 acl_msg_open 打開日志而直接使用 acl_msg_xxx 寫日志時,決定是否將日志信息輸出至屏幕(這個函數(shù)應(yīng)該在程序初始化時調(diào)用):
/*** 當未調(diào)用 acl_msg_open 方式打開日志時,調(diào)用了 acl_msg_info/error/fatal/warn* 的操作,是否允許信息輸出至標準輸出屏幕上,通過此函數(shù)來設(shè)置該開關(guān),該開關(guān)* 僅影響是否需要將信息輸出至終端屏幕而不影響是否輸出至文件中* @param onoff {int} 非 0 表示允許輸出至屏幕*/ ACL_API void acl_msg_stdout_enable(int onoff);? ? ? ?前面曾說過,acl 的日志庫還允許用戶使用自己的日志記錄過程,但要求用戶必須在程序初始化時注冊自己的日志處理函數(shù),如下:
/*** 在打開日志前調(diào)用此函數(shù)注冊應(yīng)用自己的日志打開函數(shù)、日志關(guān)閉函數(shù)、日志記錄函數(shù)* @param open_fn {ACL_MSG_OPEN_FN} 自定義日志打開函數(shù)* @param close_fn {ACL_MSG_CLOSE_FN} 自定義日志關(guān)閉函數(shù)* @param write_fn {ACL_MSG_WRITE_FN} 自定義日志記錄函數(shù)* @param ctx {void*} 自定義參數(shù)*/ ACL_API void acl_msg_register(ACL_MSG_OPEN_FN open_fn, ACL_MSG_CLOSE_FN close_fn,ACL_MSG_WRITE_FN write_fn, void *ctx);? ? ?調(diào)用此函數(shù)后,以后的日志記錄過程(即當用戶調(diào)用:acl_msg_xxx 相關(guān)過程時)的內(nèi)容便輸出便由用戶的日志庫控制。
?
? ? ? 除了以上主要的日志函數(shù)接口,在 acl_msg 中還提供了以下幾個函數(shù),便于用戶知曉程序出錯原因:
/*** 獲得上次系統(tǒng)調(diào)用出錯時的錯誤描述信息,該函數(shù)內(nèi)部采用了線程局部變量,所以是線程* 安全的,但使用起來更簡單些* @return {const char *} 返回錯誤提示信息 */ ACL_API const char *acl_last_serror(void);/*** 獲得上次系統(tǒng)調(diào)用出錯時的錯誤號* @return {int} 錯誤號*/ ACL_API int acl_last_error(void);?
? ? ? 三、acl_debug.c/acl_debug.h
? ? ? 該日志函數(shù)庫是在 acl_msg 之上的再一次封裝,該庫的思想來源于 squid 的日志記錄方式,可以將日志分成不同的類別,每一個類別又分成不同的級別,這樣用戶就可以非常方便地通過配置文件來記錄不同類別的不同級別的日志信息了。在程序初始化時需先調(diào)用如此函數(shù):
/*** 初始化日志調(diào)試調(diào)用接口* @param pStr {const char*} 調(diào)試類別(建議值在100至1000之間)標簽及級別字符串,* 格式: 1,1; 2,10; 3,8... or 1:1; 2:10; 3:8...*/ ACL_API void acl_debug_init(const char *pStr);/*** 初始化日志調(diào)試調(diào)用接口* @param pStr {const char*} 調(diào)試標簽及級別字符串,* 格式: 1,1; 2,10; 3,8... or 1:1; 2:10; 3:8...* @param max_debug_level {int} 最大調(diào)試標簽值*/ ACL_API void acl_debug_init2(const char *pStr, int max_debug_level);? ? ? ?其中,第一個參數(shù)是一個由日志記錄類別與級別組成的字符串,格式為:類別1:最大記錄級別, 類別2:最大記錄級別, ...。例如:100:2; 102:3; 103:4,其含義是日志將會記錄類別為 100 的所有級別值小于2、類別為 101 的所有級別值小于 3 以及類別為 103 的所有級別值小于 4 的日志信息。關(guān)于記錄類別需要注意:類別值最好是 >= 100,且 < 1000(當使用 acl_debug_init2 初始化時只要類別值 >= 100 即可,因為第二個參數(shù)指定了最大類別值),這是因為 acl 庫內(nèi)部一些保留的類別值都在 0 -- 100 之間。
? ? ? ?那么具體的使用這些類別與級別記錄日志的接口是什么呢?如下所示:
/*** 日志調(diào)試宏接口* @param SECTION {int} 調(diào)試標簽值* @param LEVEL {int} 對應(yīng)于SECTION調(diào)試標簽的級別*/ #define acl_debug(SECTION, LEVEL) \!acl_do_debug((SECTION), (LEVEL)) ? (void) 0 : acl_msg_info?? ? ? ?看到了吧,用戶其實只需要調(diào)用一個宏即可,如下面的例子:?
/* 初始化日志類別記錄 */const char *str = "101:2; 103:4; 105:3";/* 記錄所有類別值為 101 級別小于等于 2、類別值為 102 級別小于等于 4、類別值為 105 級別小于等于 3 的日志內(nèi)容 */acl_debug_init(str);....../* 下面的日志因符合類別值 101 級別值 <= 2 而被記錄 */acl_debug(101, 2)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));/* 下面日志符合類別 105 的記錄級別 */acl_debug(105, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));/* 下面的日志因不符合類別值 103 的記錄級別條件而被忽略 */acl_debug(103, 5)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));/* 下面日志的類別值 102 因不存在而被忽略 */acl_debug(102, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));?
? ? ? 此外,為了方便,還可以傳給 acl_debug_init 的參數(shù)寫為:"all:1",意思是所有類別的級別值 <= 1 的日志都將被記錄,如下面的內(nèi)容都會被記錄:
?
acl_debug_init("all:1"); ......acl_debug(100, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));acl_debug(101, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));acl_debug(101, 0)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));acl_debug(102, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));acl_debug(103, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));acl_debug(104, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));acl_debug(105, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));......?
? ? ? ?ok,有關(guān)日志 ?acl 日志記錄函數(shù)就先寫這些,使用者可以根據(jù)項目需要采用不同的日志記錄方式。
?
? ? ? 參考:
? ? ? 本文地址:http://zsxxsz.iteye.com/blog/1893115
? ? ? 更多文章:http://zsxxsz.iteye.com/
? ? ? 下載:http://sourceforge.net/projects/acl/
? ? ? svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code
? ? ? QQ 群:242722074
?
?
?
轉(zhuǎn)載于:https://my.oschina.net/u/568966/blog/309522
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的acl 日志记录方式介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。