程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 日志模塊的C語言實現

日志模塊的C語言實現

編輯:關於C
模塊接口
模塊接口比較簡單,因為主要只有寫日志的操作。
創建日志模塊變量
[cpp] 
log_t log_new(log_type_t type,const char *file, const char *facility); 
log_type_t用於控制日志的類型,可以標識為系統日志,文件以及標准輸出,是一個枚舉類型,定義如下:
[cpp]
typedef enum {   
    log_STDOUT, 
    log_SYSLOG, 
    log_FILE 
} log_type_t; 
file:這個字段有兩種意思,如果日志類型為文件時,該字段表示文件名。如果日志類型為系統日志,該字段表示ident值。
facility:這個參數用於指明記錄日志的程序的類型,我們傳遞的格式都是系統保留的,並且以這種形式傳遞local3。
寫日志
[cpp] 
void  log_write(log_t log, int level, const char *msgfmt, ...); 
level表示寫入日志的級別,如:通告,錯誤,警告之類的,可以按照通用的錯誤類型劃分。分別定義字符串如下:
[html]
static const char *_log_level[] = 

    "emergency", 
    "alert", 
    "critical", 
    "error", 
    "warning", 
    "notice", 
    "info", 
    "debug" 
}; 
釋放日志模塊變量
[cpp] 
void  log_free(log_t log); 
二,數據結構
定義數據結構如下:
[cpp] 
typedef struct log_st 

    log_type_t  type; 
    FILE        *file; 
} *log_t; 
 只有日志類型和一個文件結構指針。
下面說一下記錄日志的程序類型,主要有以下幾種日志程序的類型:
[html] 
LOG_AUTH :安全/授權消息 
LOG_AUTHPRIV:安全/授權消息 
LOG_CRON:時間守護進程(cron和at)專用 
LOG_DAEMON:其它系統守護進程 
LOG_KERN:核心消息 
LOG_LOCAL0到LOG_LOCAL7:系統保留 
LOG_LPR:printer子系統 
LOG_MAIL:mail子系統 
LOG_NEWS:USENET新聞子系統 
LOG_SYSLOG:syslogd進程內部所產生的消息 
LOG_USER(缺省):一般使用者缺省使用消息 
LOG_UUCP:UUCP子系統 
LOG_FTP:FTP子系統使用 
我們在這裡用的是系統保留,也就是只有LOG_LOCAL0到LOG_LOCAL7,那就要求必須重新封裝,以做到以下兩點:
如果傳遞的facility不屬於local0~local7,就默認local0~local7中的一個。
需要將傳遞的local0轉換成LOG_LOCAL0,以此類推。
這裡為什麼傳遞local0,而不直接傳遞LOG_LOCAL0?直接傳遞也是可以的,只所以傳遞字符串,是因為大多數的日志都是在配置文件中,而配置文件以字符串的形式存取。而LOG_LOCAL0,並不是一個固定的整數值。
[cpp] 
typedef struct log_facility_st 

    const char  *facility; 
    int         number; 
} log_facility_t; 
 
static log_facility_t _log_facilities[] = { 
    { "local0", LOG_LOCAL0 }, 
    { "local1", LOG_LOCAL1 }, 
    { "local2", LOG_LOCAL2 }, 
    { "local3", LOG_LOCAL3 }, 
    { "local4", LOG_LOCAL4 }, 
    { "local5", LOG_LOCAL5 }, 
    { "local6", LOG_LOCAL6 }, 
    { "local7", LOG_LOCAL7 }, 
    { NULL, -1 } 
}; 
 這是定義的一個常量數組,分別和系統日志設備對映起來。在操作的日志文件中,可以看到和這相關的配置,位於/etc/syslog.conf文件中,如:
[cpp] 
# Log all kernel messages to the console. 
# Logging much else clutters up the screen. 
#kern.*                                                 /dev/console 
 
# Log anything (except mail) of level info or higher. 
# Don't log private authentication messages! 
*.info;mail.none;authpriv.none;cron.none                /var/log/messages 
 
# The authpriv file has restricted access. 
authpriv.*                                              /var/log/secure 
 
# Log all the mail messages in one place. 
mail.*                                                  -/var/log/maillog 
 
 
# Log cron stuff 
cron.*                                                  /var/log/cron 
 
# Everybody gets emergency messages 
*.emerg                                                 * 
 
# Save news errors of level crit and higher in a special file. 
uucp,news.crit                                          /var/log/spooler 
 
# Save boot messages also to boot.log 
local7.*                                                /var/log/boot.log 
local5.*                                                /var/log/client.log 
 這上面就配置了local7和local5這兩個本地日志設備,其實就是兩個日志文件。其實在系統的頭文件中就是<sys/syslog.h>中,也定義了這樣的一個數組:
[cpp]
#ifdef SYSLOG_NAMES 
CODE facilitynames[] = 
  { 
    { "auth", LOG_AUTH }, 
    { "authpriv", LOG_AUTHPRIV }, 
    { "cron", LOG_CRON }, 
    { "daemon", LOG_DAEMON }, 
    { "ftp", LOG_FTP }, 
    { "kern", LOG_KERN }, 
    { "lpr", LOG_LPR }, 
    { "mail", LOG_MAIL }, 
    { "mark", INTERNAL_MARK },          /* INTERNAL */ 
    { "news", LOG_NEWS }, 
    { "security", LOG_AUTH },           /* DEPRECATED */ 
    { "syslog", LOG_SYSLOG }, 
    { "user", LOG_USER }, 
    { "uucp", LOG_UUCP }, 
    { "local0", LOG_LOCAL0 }, 
    { "local1", LOG_LOCAL1 }, 
    { "local2", LOG_LOCAL2 }, 
    { "local3", LOG_LOCAL3 }, 
    { "local4", LOG_LOCAL4 }, 
    { "local5", LOG_LOCAL5 }, 
    { "local6", LOG_LOCAL6 }, 
    { "local7", LOG_LOCAL7 }, 
    { NULL, -1 } 
  }; 
#endif 
 前面說為了便宜傳遞參數,我們假設我們傳遞的系統日志設備為local5這種形式,但要求是LOG_LOCAL5這個宏,因此我們根據上面的數組,作個轉換:
[cpp]
static int _log_facility(const char *facility) { 
    log_facility_t *lp; 
 
    if (facility == NULL) { 
        return -1; 
    } 
    for (lp = _log_facilities; lp->facility; lp++) { 
        if (!strcasecmp(lp->facility, facility)) { 
            break; 
        } 
    } 
    return lp->number; 

這個函數會根據local5,找到並返回LOG_LOCAL5。
三,創建日志模塊
代碼如下:
[cpp] 
log_t log_new(log_type_t type,const char *ident, const char *facility) 

    log_t log; 
    int fnum = 0; 
 
    log = (log_t) calloc(1, sizeof(struct log_st)); 
 
    log->type = type; 
 
    if(type == log_SYSLOG) {   // 系統日志  
        fnum = _log_facility(facility); 
        if (fnum < 0) 
            fnum = LOG_LOCAL7; 
        openlog(ident, LOG_PID, fnum); //  下面會對openlog系統調用進行說明。 
        return log; 
    } 
    else if(type == log_STDOUT) {   // 標准輸出。 
        log->file = stdout; 
        return log; 
    } 
    log->file = fopen(ident, "a+");   
    if(log->file == NULL)   //  文件,如果打開文件失敗,會將其定義為標准輸出。 
    {  
        log->type = log_STDOUT; 
        log->file = stdout; 
    } 
    return log; 

四,關於openlog調用
在寫系統日志時,不是必須要調用openlog,如果沒有調用openlog,那麼在第一次調用syslog時,會自動調用openlog,此函數原型如下:
[cpp] 
void openlog(const char *ident, int option, int facility); 
 這個函數用來打開一個到系統日志記錄程序的連接,打開之後就可以用syslog或vsyslog函數向系統日志裡記錄日志。
ident:這個參數是一個標記,在寫入日志時,每行都會在前面自動的加上這個標記,通常可以寫成當前程序的名稱或者是同一程序不同的端口調用。
option:該參數可以取值LOG_CONS, LOG_NDELAY, LOG_NOWAIT, LOG_ODELAY, LOG_PERROR, LOG_PID。都表示不同的意思,我們這裡取LOG_PID表示每一行日志都包含當前程序的進程ID號。
facility:表示往哪個日志裡寫,其實也是表示由哪個具體的系統日志類型來記錄日志。
五,寫日志
在調用log_new之後,此時log_st有兩種可能:
類型為系統日志類型,那麼已經打開了系統日志。接下來只要直接調用syslog函數就OK了。
日志類型不是系統日志,而是文件或者標准輸出,此時log_st結構體的域FILE *file,已經指向了具體的文件,後面只要調用fwrite類函數就可以了。
代碼如下:
[cpp]
#define MAX_LOG_LINE (1024) 
void log_write(log_t log, int level, const char *msgfmt, ...) 

    va_list ap; 
    char *pos, message[MAX_LOG_LINE+1]; 
    int sz, len; 
    time_t t; 
 
    if(log->type == log_SYSLOG) { //  寫入系統日志  
        va_start(ap, msgfmt); 
        len = vsnprintf(message, MAX_LOG_LINE, msgfmt, ap); 
        if (len > MAX_LOG_LINE) 
            message[MAX_LOG_LINE] = '\0'; 
        else 
            message[len] = '\0'; 
        syslog(level, "%s", message); // 下面會說明syslog調用。 
        va_end(ap); 
        return; 
    } 
 
    t = time(NULL);   // 時間戳 
    pos = ctime(&t); 
    sz = strlen(pos); 
    pos[sz-1]=' '; 
 
    len = snprintf(message, MAX_LOG_LINE, "%s[%s] ", pos, _log_level[level]); 
    if (len > MAX_LOG_LINE) 
        message[MAX_LOG_LINE] = '\0'; 
    else 
        message[len] = '\0';  
 
    for (pos = message; *pos != '\0'; pos++); /*empty statement */ 
    sz = pos - message; 
    va_start(ap, msgfmt); 
    vsnprintf(pos, MAX_LOG_LINE - sz, msgfmt, ap);   
    va_end(ap);   // 根據傳入參數,組織文本信息 
 
    fprintf(log->file,"%s", message);  // 寫入文件。 
    fprintf(log->file, "\n"); 
    fflush(log->file); 

六,syslog調用
函數的聲明如下:
[cpp] 
void syslog(int priority, const char * message, ...); 
priority:消息的緊急級別。
message:第二個參數是消息及其格式,之後是格式對應的參數,如同C語言裡面printf輸出函數一樣使用。
第一個參數priority,它是由severity level和facility組成的,Facility已經在上面介紹了,下面介紹一下severity level,也就是消息的重要級別,它主要包括:
[html]
LOG_EMERG:緊急狀況 
LOG_ALERT:高優先級問題,比如說數據庫崩潰等,必須要立即采取反應行動 
LOG_CRIT:重要狀況發生,比如硬件故障 
LOG_ERR:錯誤發生 
LOG_WARNING:警告發生 
LOG_NOTICE:一般狀況,需要引起注意 
LOG_INFO:信息狀況 
LOG_DEBUG:調試消息 
七,釋放
這個操作就很簡單了,
[cpp] 
void log_free(log_t log) { 
    if(log->type == log_SYSLOG) 
        closelog(); 
    else if(log->type == log_FILE) 
        fclose(log->file); 
 
    free(log); 

八,使用的例子
這個使用也是很簡單的,分成三部:創建,寫入,釋放
[cpp] 
log_t log = log_new(log_SYSLOG, "Example", "local5"); // 創建日志模塊變量。 
log_write(log, LOG_NOTICE, "應用程序正在啟動中"); 
log_free(log); 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved