From eea80be1c75b0305fc05e0c0de6f07ad784758e7 Mon Sep 17 00:00:00 2001 From: MyZOOO Date: Wed, 17 Dec 2025 02:33:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96=E8=BE=93=E5=87=BA=20=E9=A2=9C=E8=89=B2=E8=BE=93?= =?UTF-8?q?=E5=87=BA=20=E6=97=A5=E5=BF=97=E7=AD=89=E7=BA=A7=E8=BE=93?= =?UTF-8?q?=E5=87=BA=20=E4=BD=BF=E7=94=A8=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + README.md | 95 +++++++++++++++ log.c | 24 ++++ log.h | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 459 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 log.c create mode 100644 log.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a02a06c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +test.c +*.exe \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8564bd8 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +# Log for C + +基于C23的日志库,支持自动类型识别、格式化输出、颜色和日志等级。 + +## 功能说明 + +使用C23的 `_Generic`特性实现类型自动识别和格式化输出。 + +### log() - 基础日志输出 + +自动判断传入参数类型并输出,支持格式化字符串。 + +**支持的参数类型:** + +- `int` - 整数 +- `long` - 长整数 +- `double` - 双精度浮点数 +- `float` - 单精度浮点数 +- `char*` / `const char*` - 字符串 +- `wchar_t*` / `const wchar_t*` - 宽字符串 +- `char` - 字符 + + > **注意:** C语言中字符字面量(如 `'A'`)的类型是 `int`,会被当作整数打印。如需打印字符,请使用 `char`类型变量或显式转换 `(char)'A'`。 + > + +**使用方式:** + +```c +log(value) // 单个值输出 +log(format, arg1) // 一个占位符 +log(format, arg1, arg2) // 两个占位符 +... // 最多支持10个占位符 +log(format, arg1, ..., arg10) // 十个占位符 +``` + +格式化字符串使用 `{}`作为占位符,会自动替换为对应参数的值。支持最多10个格式化变量参数。 + +### clog() - 颜色输出 + +输出带颜色的日志信息。 + +**参数:** + +- `color` (string) - 颜色名称 +- `message` - 要输出的内容(支持所有log()支持的类型) + +**支持的颜色:** +`red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray` + +### 日志等级函数 + +带时间戳和颜色的日志等级输出。 + +**logerr(message)** - 错误日志,红色输出,格式:`[时间 ERROR] 消息` + +**logok(message)** - 成功日志,绿色输出,格式:`[时间 OK] 消息` + +**loginfo(message)** - 信息日志,青色输出,格式:`[时间 INFO] 消息` + +**logwarn(message)** - 警告日志,黄色输出,格式:`[时间 WARN] 消息` + +**参数:** + +- `message` - 要输出的消息(支持所有log()支持的类型) + +## 使用 + +```c +#include "log.h" + +int main() { + int i = 18; + double f = 1.75; + + log("hello"); + log(i); + log(f); + log("年龄{}岁, 身高{}米", i, f); + + clog("red", "错误信息"); + logerr("MySQL连接失败"); + logok("MySQL连接成功"); + loginfo("已插入数据"); + + return 0; +} +``` + +## 编译 + +```bash +gcc -std=c2x -o main test.c log.c +``` + +需要GCC 13+或其他支持C23标准的编译器。 diff --git a/log.c b/log.c new file mode 100644 index 0000000..fa8db67 --- /dev/null +++ b/log.c @@ -0,0 +1,24 @@ +#include "log.h" +#include +#include +#include + +// 获取颜色代码 +const char* get_color_code(const char* color_name) { + if (strcmp(color_name, "red") == 0) return COLOR_RED; + if (strcmp(color_name, "green") == 0) return COLOR_GREEN; + if (strcmp(color_name, "yellow") == 0) return COLOR_YELLOW; + if (strcmp(color_name, "blue") == 0) return COLOR_BLUE; + if (strcmp(color_name, "magenta") == 0) return COLOR_MAGENTA; + if (strcmp(color_name, "cyan") == 0) return COLOR_CYAN; + if (strcmp(color_name, "white") == 0) return COLOR_WHITE; + if (strcmp(color_name, "gray") == 0) return COLOR_GRAY; + return COLOR_RESET; +} + +// 获取当前时间字符串 +void get_time_string(char* buffer, size_t size) { + time_t now = time(NULL); + struct tm* t = localtime(&now); + strftime(buffer, size, "%Y-%m-%d %H:%M:%S", t); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..ad4ec99 --- /dev/null +++ b/log.h @@ -0,0 +1,338 @@ +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include +#include + +// ANSI颜色代码 +#define COLOR_RESET "\033[0m" +#define COLOR_RED "\033[31m" +#define COLOR_GREEN "\033[32m" +#define COLOR_YELLOW "\033[33m" +#define COLOR_BLUE "\033[34m" +#define COLOR_MAGENTA "\033[35m" +#define COLOR_CYAN "\033[36m" +#define COLOR_WHITE "\033[37m" +#define COLOR_GRAY "\033[90m" + +// 辅助函数声明 +const char* get_color_code(const char* color_name); +void get_time_string(char* buffer, size_t size); + +// 内部打印函数 +static inline void _print_int(int x) { printf("%d", x); } +static inline void _print_double(double x) { printf("%.2f", x); } +static inline void _print_str(const char* x) { printf("%s", x); } +static inline void _print_char(int x) { printf("%c", x); } +static inline void _print_wstr(const wchar_t* x) { + static int locale_set = 0; + if (!locale_set) { + setlocale(LC_ALL, ""); + locale_set = 1; + } + wprintf(L"%ls", x); +} + +// 类型自动打印(使用C23的_Generic) +#define PRINT_VALUE(x) _Generic((x), \ + int: _print_int, \ + long: _print_int, \ + double: _print_double, \ + float: _print_double, \ + char: _print_char, \ + char*: _print_str, \ + const char*: _print_str, \ + wchar_t*: _print_wstr, \ + const wchar_t*: _print_wstr \ +)(x) + +// log函数重载 - 单参数 +static inline void _log_int(int x) { + printf("%d\n", x); +} + +static inline void _log_double(double x) { + printf("%.2f\n", x); +} + +static inline void _log_str(const char* x) { + printf("%s\n", x); +} + +// log函数重载 - 格式化字符串 + 一个参数 +static inline void _log_fmt_int(const char* fmt, int b) { + const char* p = fmt; + int done = 0; + while (*p) { + if (*p == '{' && *(p+1) == '}' && !done) { + printf("%d", b); + done = 1; + p += 2; + } else { + putchar(*p); + p++; + } + } + printf("\n"); +} + +// log函数重载 - 格式化字符串 + 两个参数 +static inline void _log_fmt_2(const char* fmt, int b, double c) { + const char* p = fmt; + int done = 0; + while (*p) { + if (*p == '{' && *(p+1) == '}') { + if (done == 0) { + printf("%d", b); + done = 1; + } else if (done == 1) { + printf("%.2f", c); + done = 2; + } + p += 2; + } else { + putchar(*p); + p++; + } + } + printf("\n"); +} + +// 格式化字符串 + 三个参数 +#define _log_fmt_3(fmt, a, b, c) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 格式化字符串 + 四个参数 +#define _log_fmt_4(fmt, a, b, c, d) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + else if (_done == 3) { PRINT_VALUE(d); _done = 4; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 格式化字符串 + 五个参数 +#define _log_fmt_5(fmt, a, b, c, d, e) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + else if (_done == 3) { PRINT_VALUE(d); _done = 4; } \ + else if (_done == 4) { PRINT_VALUE(e); _done = 5; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 格式化字符串 + 六个参数 +#define _log_fmt_6(fmt, a, b, c, d, e, f) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + else if (_done == 3) { PRINT_VALUE(d); _done = 4; } \ + else if (_done == 4) { PRINT_VALUE(e); _done = 5; } \ + else if (_done == 5) { PRINT_VALUE(f); _done = 6; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 格式化字符串 + 七个参数 +#define _log_fmt_7(fmt, a, b, c, d, e, f, g) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + else if (_done == 3) { PRINT_VALUE(d); _done = 4; } \ + else if (_done == 4) { PRINT_VALUE(e); _done = 5; } \ + else if (_done == 5) { PRINT_VALUE(f); _done = 6; } \ + else if (_done == 6) { PRINT_VALUE(g); _done = 7; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 格式化字符串 + 八个参数 +#define _log_fmt_8(fmt, a, b, c, d, e, f, g, h) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + else if (_done == 3) { PRINT_VALUE(d); _done = 4; } \ + else if (_done == 4) { PRINT_VALUE(e); _done = 5; } \ + else if (_done == 5) { PRINT_VALUE(f); _done = 6; } \ + else if (_done == 6) { PRINT_VALUE(g); _done = 7; } \ + else if (_done == 7) { PRINT_VALUE(h); _done = 8; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 格式化字符串 + 九个参数 +#define _log_fmt_9(fmt, a, b, c, d, e, f, g, h, i) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + else if (_done == 3) { PRINT_VALUE(d); _done = 4; } \ + else if (_done == 4) { PRINT_VALUE(e); _done = 5; } \ + else if (_done == 5) { PRINT_VALUE(f); _done = 6; } \ + else if (_done == 6) { PRINT_VALUE(g); _done = 7; } \ + else if (_done == 7) { PRINT_VALUE(h); _done = 8; } \ + else if (_done == 8) { PRINT_VALUE(i); _done = 9; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 格式化字符串 + 十个参数 +#define _log_fmt_10(fmt, a, b, c, d, e, f, g, h, i, j) do { \ + const char* _p = (fmt); \ + int _done = 0; \ + while (*_p) { \ + if (*_p == '{' && *(_p+1) == '}') { \ + if (_done == 0) { PRINT_VALUE(a); _done = 1; } \ + else if (_done == 1) { PRINT_VALUE(b); _done = 2; } \ + else if (_done == 2) { PRINT_VALUE(c); _done = 3; } \ + else if (_done == 3) { PRINT_VALUE(d); _done = 4; } \ + else if (_done == 4) { PRINT_VALUE(e); _done = 5; } \ + else if (_done == 5) { PRINT_VALUE(f); _done = 6; } \ + else if (_done == 6) { PRINT_VALUE(g); _done = 7; } \ + else if (_done == 7) { PRINT_VALUE(h); _done = 8; } \ + else if (_done == 8) { PRINT_VALUE(i); _done = 9; } \ + else if (_done == 9) { PRINT_VALUE(j); _done = 10; } \ + _p += 2; \ + } else { \ + putchar(*_p); \ + _p++; \ + } \ + } \ + printf("\n"); \ +} while(0) + +// 主log宏 - 支持最多11个参数(格式化字符串 + 10个变量) +#define _GET_11TH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, N, ...) N + +#define log(...) _GET_11TH_ARG(__VA_ARGS__, \ + _log_fmt_10, _log_fmt_9, _log_fmt_8, _log_fmt_7, _log_fmt_6, \ + _log_fmt_5, _log_fmt_4, _log_fmt_3, _log_2, _log_1, _log_1)(__VA_ARGS__) + +#define _log_1(x) _Generic((x), \ + int: _log_int, \ + double: _log_double, \ + char*: _log_str, \ + const char*: _log_str \ +)(x) + +#define _log_2(a, b) _Generic((b), \ + int: _log_fmt_int \ +)(a, b) + +#define _log_3(a, b, c) _Generic((b), \ + int: _log_fmt_2 \ +)(a, b, c) + +// 颜色log +#define clog(color, msg) do { \ + printf("%s", get_color_code(color)); \ + PRINT_VALUE(msg); \ + printf("%s\n", COLOR_RESET); \ +} while(0) + +// 日志等级宏 +#define logerr(msg) do { \ + char time_str[64]; \ + get_time_string(time_str, sizeof(time_str)); \ + printf("%s[%s ERROR]%s ", COLOR_RED, time_str, COLOR_RESET); \ + PRINT_VALUE(msg); \ + printf("\n"); \ +} while(0) + +#define logok(msg) do { \ + char time_str[64]; \ + get_time_string(time_str, sizeof(time_str)); \ + printf("%s[%s OK]%s ", COLOR_GREEN, time_str, COLOR_RESET); \ + PRINT_VALUE(msg); \ + printf("\n"); \ +} while(0) + +#define loginfo(msg) do { \ + char time_str[64]; \ + get_time_string(time_str, sizeof(time_str)); \ + printf("%s[%s INFO]%s ", COLOR_CYAN, time_str, COLOR_RESET); \ + PRINT_VALUE(msg); \ + printf("\n"); \ +} while(0) + +#define logwarn(msg) do { \ + char time_str[64]; \ + get_time_string(time_str, sizeof(time_str)); \ + printf("%s[%s WARN]%s ", COLOR_YELLOW, time_str, COLOR_RESET); \ + PRINT_VALUE(msg); \ + printf("\n"); \ +} while(0) + +#endif