應用層log列印
應用層一般log列印函式都經過封裝了,這樣可以設定log等級,方便除錯,可以直接螢幕輸出,也可以儲存到檔案或者傳送到雲端。現只看螢幕輸出部分,跟進這些函式,最終就是輸出到 stderr 或者 stdout 或者呼叫printf。
先看一個函式:
void std_err_out(void)
{
fprintf(stdout,"Hello ");
fprintf(stderr,"World!");
}
列印結果是:World!Hello 。原因是在預設情況下,stdout是行緩衝的,他的輸出會放在一個buffer裡面,只有到換行的時候,才會輸出到螢幕,而stderr是無緩衝的,會直接輸出。
第二個函式:
void simple_out(void)
{
fprintf(stderr, "%s_%d,stderr output!\n",__FUNCTION__,__LINE__);
fprintf(stdout, "%s_%d,stdout output!\n",__FUNCTION__,__LINE__);
printf("%s_%d,printf output!\n",__FUNCTION__,__LINE__);
}
列印結果是:
simple_out_27,stderr output! simple_out_28,stdout output! simple_out_29,printf output!
看來加了換行符後, stderr ,stdout ,printf沒有任何區別。但是生成的可執行檔案重定向輸出到檔案呢?
$ ./stdtest > tmp.txt
simple_out_11,stderr output!
那麼tmp.txt裡面的內容是:
simple_out_12,stdout output!
simple_out_13,printf output!
這樣看差異就出來了,原因是因為:
stdout -- 標準輸出裝置, printf(".."))同 stdout。
stderr -- 標準錯誤輸出裝置
兩者預設向螢幕輸出。
但如果用轉向標準輸出到磁碟檔案,則可看出兩者區別。
stdout輸出到磁碟檔案,stderr在螢幕。
第三個函式:
#define print_case_frist(fmt,...) \
do { if (1) fprintf(stderr, "print_frist_case: %s:%s():%d " fmt, __FILE__, __func__,__LINE__, ##__VA_ARGS__); } while (0)
這個巨集定義結構在程式碼裡經常出現,關於‘##’運算子可以用於類函式巨集的替換部分, __VA_ARGS__ 是一個可變引數的巨集,替換省略號所代表的字串。
這樣呼叫 print_case_frist("|case %d\n",one);,列印結果如下:
print_frist_case: std-printf.c:main():110 |case 1
關於巨集定義中do while(0) 這個結構是必須,說明如下:
do while(0) 作用:
1. 替代{},實現區域性作用域..
2. 避免使用goto,用break做跳出.
例子:
#define AB1 a; b; // 下面語句b不能被執行: if (cond) AB1;
#define AB2 { a; b; } // 下面語句編譯出錯:if (cond) AB2; else ...;
#define AB3 a, b // 有運算子優先順序問題
#define AB4 do { a; b; } while (0)
第四個函式:
#define print_case_second(fmt, args...) second_log_error("[%s:%d] "fmt, __func__, __LINE__, ##args)
void second_log_error(const char *fmt, ...)
{
va_list ap; //指向當前引數的一個指標
va_start(ap, fmt);//對va_list變數進行初始化,將ap指標指向引數列表中的第一個引數
vfprintf(stdout, fmt, ap);//將ap(通常是字串) 按format格式寫入stdout中
va_end(ap);//回收ap指標
}
這第三個函式區別是輸出到 stdout,沒有用fprintf,而是用vfprintf這個函式,具體函式已經註釋很清楚了。
這樣呼叫 print_case_second("|case %d\n",two);,列印結果如下:
[main:113] |case 2
第五個函式:
#define LOG_DEBUG_MAX_LEN 4096
#define LOG_DEBUG_WAY_ALL 3
#define LOG_DEBUG_LEVEL_ERROR 1
//增加log列印等級
#define print_case_third(fmt, args...)\
third_log_error(LOG_DEBUG_WAY_ALL, "%d [%s:%d] "fmt, LOG_DEBUG_LEVEL_ERROR, __func__, __LINE__, ##args)
void third_log_error(int way, const char* fmt, ...)
{
char buf[LOG_DEBUG_MAX_LEN] = {0};
int len = 0;
char *pos = NULL;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buf, LOG_DEBUG_MAX_LEN - 1, fmt, ap);
if (len <= 0)
{
return ;
}
if (len > LOG_DEBUG_MAX_LEN - 1 )
{
len = LOG_DEBUG_MAX_LEN - 1;
}
if ((LOG_DEBUG_LEVEL_ERROR == 1) && (way > 2))
{
printf("%s\n", buf);
}
va_end(ap);
}
這個函式可以設定log等級了,log是儲存在buf中,意味著可以根據對應引數(bug_level和way)傳送到需要的地方,當然也可以寫入對應系統檔案中。函式三和函式四的log也可以很容易儲存到檔案。
其實還有一種系統log是儲存在/var/log/messages裡,函式介面是syslog():
配置檔案:/etc/syslog.conf;
log檔案:/var/log/messages
syslog():向日志裝置(日誌工具 facility)傳送日誌訊息
#define ALONE_LOG(fmt,args...) \
do { \
snprintf(alone_log_buf, ALONE_LOG_BUF_SIZE, fmt,##args); \
syslog( LOG_INFO, alone_log_buf ); \
} while(0)