為中小型 C 項目提供**零依賴、單文件、可選系統日志 (syslog)、可變參數格式化**的輕量級日志方案。不引入 glog/log4c 等重型庫,核心代碼 < 100 行,編譯後體積極小。
log.h
#ifndef _LOG_H_
#define _LOG_H_
#ifdef __cplusplus
extern "C" {
#endif
// 設置是否啟用 syslog (1=啟用, 0=控制台)
extern void set_syslog(int syslog);
// 致命錯誤:輸出日志後直接 exit(1)
extern void abort(const char *msg, ...);
// 調試日志:僅 _debug=1 時輸出 (需外部定義 _debug)
extern void log(const char *msg, ...);
// 錯誤日志:輸出到 stderr,不退出
extern void error(const char *msg, ...);
// 信息日志:輸出到 stderr
extern void info(const char *msg, ...);
#ifdef __cplusplus
}
#endif
#endif
log.c
#include
#include
#include
#if HAVE_SYSLOG_H
#include
#endif
// 內部狀態
static int _syslog = 0; // 是否使用 syslog
// 注意:原代碼中 log() 依賴外部 _debug 變量,此處補全為靜態或擴展接口
static int _debug = 0; // 調試開關
void set_syslog(int syslog) {
_syslog = syslog;
}
// 可選:運行時控制調試輸出
void set_debug(int debug) {
_debug = debug;
}
// 通用格式化輸出核心
static void vlog_internal(int syslog_priority, FILE *fallback_stream, const char *fmt, va_list ap) {
#if HAVE_VSYSLOG
if (_syslog) {
vsyslog(syslog_priority, fmt, ap);
return;
}
#endif
vfprintf(fallback_stream, fmt, ap);
}
void abort(const char *msg, ...) {
va_list ap;
va_start(ap, msg);
vlog_internal(LOG_ERR, stdout, msg, ap); // abort 信息輸出到 stdout (或 stderr)
va_end(ap);
exit(1);
}
void log(const char *msg, ...) {
if (!_debug) return; // 調試關閉時零開銷
va_list ap;
va_start(ap, msg);
vlog_internal(LOG_DEBUG, stdout, msg, ap);
va_end(ap);
}
void error(const char *msg, ...) {
va_list ap;
va_start(ap, msg);
vlog_internal(LOG_ERR, stderr, msg, ap);
va_end(ap);
}
void info(const char *msg, ...) {
va_list ap;
va_start(ap, msg);
vlog_internal(LOG_INFO, stderr, msg, ap);
va_end(ap);
}
| 特性 | 實現方式 | 優勢 |
|---|---|---|
| 可變參數 | va_list + vfprintf/vsyslog |
支持 printf 風格格式化,類型安全由編譯器 -Wformat 保證 |
| 雙輸出通道 | 編譯期宏 HAVE_VSYSLOG 判斷,運行期 _syslog 開關 |
開發期用控制台,生產期切 syslog,零代碼修改 |
| 級別語義 | abort 致命退出,error/info 輸出 stderr,log 受 _debug 控制 |
語義清晰,符合 syslog 標準級別 (ERR/DEBUG/INFO) |
| 線程安全 | **不保證** (簡單項目通常單線程或自行加鎖) | 極簡實現,無鎖開銷。多線程項目需自行包裝 mutex |
| 內存管理 | 無動態分配,棧上 va_list |
零內存洩漏風險,實時性可預測 |
// main.c
#include "log.h"
int _debug = 1; // 開啟調試日志 (若 log.c 中 _debug 為外部變量)
int main() {
set_syslog(0); // 控制台模式
// set_syslog(1); // 生產環境啟用 syslog,需編譯時定義 HAVE_SYSLOG_H
info("Server started on port %d", 8080);
log("Debug: connection from %s:%d", "192.168.1.1", 12345);
int ret = connect_db();
if (ret != 0) {
error("DB connect failed: errno=%d", ret);
}
if (critical_failure()) {
abort("FATAL: %s", "core dump triggered");
}
return 0;
}
# GCC/Clang
gcc -c log.c -o log.o -DHAVE_SYSLOG_H=1 # 啟用 syslog 支持
# 或
gcc -c log.c -o log.o # 純控制台模式
# 鏈接
gcc main.c log.o -o myapp
# CMake 集成
add_library(log STATIC log.c)
target_compile_definitions(log PRIVATE HAVE_SYSLOG_H=1) # 可選
target_include_directories(log PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
vlog_internal 中調用 strftime 格式化時間前綴。fallback_stream 指向 fopen 的文件句柄,定期 fclose/fopen 或按大小切分。#define LOG_LEVEL LOG_INFO,宏層面消除低級別調用開銷。本實現以 標準 C (C89/C99)、零第三方依賴、雙通道 (控制台/syslog)、四級語義、可變參數格式化 為核心特性,代碼量極小 (<100 行),完美適配嵌入式、單體服務、CLI 工具等輕量級場景。遵循「做一件事,做好它」的 Unix 哲學,是 C 項目引入日志系統的**最佳起步方案**。