首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

從零構建高性能C語言日誌庫 sclog:多級別、多線程、跨平台實戰

     編輯:關於C語言

設計目標與核心特性

sclog 是一個輕量級、跨平台的 C 語言日誌庫,支援:

  • 六大日誌等級:TRACE, DEBUG, INFO, NOTICE, WARNING, FATAL
  • 雙文件輸出:sc.log(全量)、sc.log.wf(僅 WARNING/FATAL)
  • 線程私有上下文:請求 ID、客戶端 IP、模組名、耗時統計
  • 跨平台適配:Linux/Windows 統一 API(時間、原子操作、目錄創建)
  • 編譯期控制:_DEBUG 宏決定 TRACE/DEBUG 是否編入發布版

核心數據結構

// 線程私有信息體
struct slinfo {
    unsigned logid;           // 全局唯一請求 ID
    char reqip[_INT_LITTLE];  // 客戶端 IP
    char times[_INT_LITTLE];  // 處理耗時(微秒)
    struct timeval timev;     // 開始時間戳
    char mod[_INT_LITTLE];    // 模組名
};

// 全局單例
static struct {
    pthread_key_t key;        // 線程私有鍵
    pthread_once_t once;      // 單次初始化
    unsigned logid;           // 原子自增 ID
    FILE *log;                // 普通日誌句柄
    FILE *wf;                 // 告警日誌句柄
} _slmain = {0, PTHREAD_ONCE_INIT, 0, NULL, NULL};

關鍵技術點深度解析

1. 線程私有存儲

使用 pthread_key_create + pthread_setspecific/getspecific 實現每線程獨立的 slinfo,避免鎖競爭。鍵創建僅執行一次(pthread_once),銷毀回調 _slinfo_destroy 防止內存洩漏。

2. 跨平台時間與原子操作

// 統一 gettimeofday (Windows 移植)
#if defined(_MSC_VER)
int gettimeofday(struct timeval *tv, void *tz) {
    SYSTEMTIME wtm; GetLocalTime(&wtm);
    struct tm tm = {wtm.wYear-1900, wtm.wMonth-1, wtm.wDay,
                    wtm.wHour, wtm.wMinute, wtm.wSecond, 0, 0, -1};
    time_t clock = mktime(&tm);
    tv->tv_sec = (long)clock;
    tv->tv_usec = wtm.wMilliseconds * 1000;
    return 0;
}
#define localtime_r(t, tm) localtime_s(tm, t)
#else
#include 
#endif

// 原子操作封裝
#if defined(_MSC_VER)
#define ATOM_ADD_FETCH(v, a) InterlockedAdd((LONG*)&(v), (LONG)(a))
#elif defined(__GNUC__)
#define ATOM_ADD_FETCH(v, a) __sync_add_and_fetch(&(v), (a))
#endif

3. 日誌格式與輸出

格式:[LEVEL][YYYY-MM-DD HH:MM:SS.usec][file:line:func][logid:XXX][reqip:IP][mod:NAME] msg

#define SCLOG_PUTS(fstr) "[" fstr "][%s:%d:%s][logid:%u][reqip:%s][mod:%s]"
#define SCLOG_PRINTF(fstr, fmt, ...) \
    sl_printf(SCLOG_PUTS(fstr) fmt "\n", __FILE__, __LINE__, __func__, \
              sl_getlogid(), sl_getreqip(), sl_getmod(), ##__VA_ARGS__)

#define SL_FATAL(fmt, ...)  SCLOG_PRINTF(_STR_SCLOG_FATAL, fmt, ##__VA_ARGS__)
#define SL_INFO(fmt, ...)   SCLOG_PRINTF(_STR_SCLOG_INFO, fmt, ##__VA_ARGS__)
// ... 其他級別宏

// 發布版移除 DEBUG/TRACE
#if defined(_DEBUG)
#define SL_DEBUG(fmt, ...)  SCLOG_PRINTF(_STR_SCLOG_DEBUG, fmt, ##__VA_ARGS__)
#else
#define SL_DEBUG(fmt, ...)  /* nothing */
#endif

4. 高性能寫入策略

  • 棧上緩衝:char logs[_INT_LOG] (8KB) 避免堆分配開銷。
  • 無鎖寫入:fputs 在 POSIX 下線程安全,省去鎖開銷。
  • 條件寫 WF:根據格式串第二字符 'F'/'W' 判斷是否寫入 .wf 文件。

使用示例

// main.c
#include "sclog.h"
#include 

void *worker(void *arg) {
    sl_init("worker", "192.168.1.1", 0);
    for (int i = 0; i < 100; ++i) {
        SL_INFO("處理任務 %d", i);
        SL_WARNING("任務 %d 耗時較長", i);
    }
    return NULL;
}

int main() {
    sl_start();                 // 初始化日誌系統
    pthread_t t1, t2;
    pthread_create(&t1, NULL, worker, NULL);
    pthread_create(&t2, NULL, worker, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;                   // atexit 自動調用 _sl_end 清理
}

編譯與部署

# Linux
gcc -Wall -ggdb -o main.out main.c sclog.c schead.c scatom.c -lpthread

# Windows (需 pthreads-w32)
cl main.c sclog.c schead.c scatom.c /I. /D_MSC_VER /link pthreadVC2.lib

總結

sclog 展示了工業級 C 庫的核心要素:跨平台抽象層、線程本地存儲、原子操作、編譯期特性裁剪、資源 RAII 風格管理。適合作為學習 C 系統編程、或項目中直接集成的輕量日誌組件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved