1. 程式人生 > >應用層log列印

應用層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)