105人参与 • 2026-09-23 • C/C++
#define inf 0
#define dbg 1
#define err 2
#define log_level inf
#define log(level, format, ...) do { \
if (level < log_level) break; \
time_t t = time(null); \
struct tm* ltm = localtime(&t); \
char tmp[32] = {0}; \
strftime(tmp, 31, "%h:%m:%s", ltm); \
fprintf(stdout, "[%s %s:%d] " format "\n", tmp, __file__, __line__, ##__va_args__); \
} while(0)
#define inf_log(format, ...) log(inf, format, ##__va_args__)
#define dbg_log(format, ...) log(dbg, format, ##__va_args__)
#define err_log(format, ...) log(err, format, ##__va_args__)
int main() {
inf_log("s");
return 0;
}这段代码定义了一套简单的日志宏,支持:
inf、dbg、errprintf 一样格式化输出log_level 控制输出级别在服务器开发中,日志是排查问题的重要手段。
但直接写 printf 有几个问题:
日志宏的目标:
#define inf 0 #define dbg 1 #define err 2 #define log_level inf
inf:普通信息,级别最低。dbg:调试信息。err:错误信息,级别最高。log_level:当前编译期日志级别,只有 level >= log_level 的日志才会输出。这里 log_level = inf,表示所有日志都输出。
如果改成 log_level = dbg,则 inf 日志被过滤,只输出 dbg 和 err。
#define log(level, format, ...) do { \
if (level < log_level) break; \
time_t t = time(null); \
struct tm* ltm = localtime(&t); \
char tmp[32] = {0}; \
strftime(tmp, 31, "%h:%m:%s", ltm); \
fprintf(stdout, "[%s %s:%d] " format "\n", tmp, __file__, __line__, ##__va_args__); \
} while(0)逐部分解释:
宏体用 do { ... } while(0) 包裹,是 c/c++ 宏的经典技巧。
好处:
if/else 中。break 提前退出。例如:
if (cond)
log(inf, "hello");
else
...如果没有 do...while(0),展开后可能出错。
if (level < log_level) break;
break 跳出 do...while,不输出。time_t t = time(null);
struct tm* ltm = localtime(&t);
char tmp[32] = {0};
strftime(tmp, 31, "%h:%m:%s", ltm);
time(null):获取当前时间戳。localtime(&t):转换为本地时间结构体 struct tm。strftime:格式化时间为 hh:mm:ss 字符串,存入 tmp。tmp[32]:足够容纳时间字符串。注意:localtime 不是线程安全的,多线程环境建议用 localtime_r。
但在简单日志中,通常可以接受。
fprintf(stdout, "[%s %s:%d] " format "\n", tmp, __file__, __line__, ##__va_args__);
stdout:输出到标准输出。"[%s %s:%d] " format "\n":把固定格式和用户传入的 format 拼接。tmp:时间字符串。__file__:当前源文件名,预定义宏。__line__:当前行号,预定义宏。##__va_args__:gnu 扩展,处理可变参数为空的情况,删除前面的逗号。输出格式类似:
[14:23:45 server.cpp:10] hello
#define inf_log(format, ...) log(inf, format, ##__va_args__) #define dbg_log(format, ...) log(dbg, format, ##__va_args__) #define err_log(format, ...) log(err, format, ##__va_args__)
inf_log:输出普通信息。dbg_log:输出调试信息。err_log:输出错误信息。使用时:
inf_log("server start");
dbg_log("client fd = %d", fd);
err_log("accept failed: %s", strerror(errno));
c++11 起支持可变参数宏:
#define log(format, ...) printf(format, ##__va_args__)
... 表示可变参数。__va_args__ 在宏展开时替换为实际参数。##__va_args__ 是 gnu 扩展,当可变参数为空时删除前面的逗号,避免 printf(format, ) 语法错误。c++20 提供了 __va_opt__,但很多项目仍使用 ##__va_args__。
__file__:当前源文件名。__line__:当前行号。__func__:当前函数名(c++11)。__date__、__time__:编译日期和时间。这些宏由编译器自动提供,非常适合日志。
为什么不用 {} 而用 do...while(0)?
{} 展开后可能和外部 if/else 产生悬空 else 问题。do...while(0) 是一个完整语句,末尾需要分号,行为更一致。break 跳出,相当于提前返回。日志级别数值越大越严重:
inf=0:信息dbg=1:调试err=2:错误log_level 控制最低输出级别。
例如 log_level=dbg,则 inf 不输出,dbg 和 err 输出。
这种设计在编译期决定,低级别日志可以被编译器优化掉,减少运行时开销。
#include <cstdio>
#include <ctime>
#include <cstring>
#define inf 0
#define dbg 1
#define err 2
#define log_level inf
#define log(level, format, ...) do { \
if (level < log_level) break; \
time_t t = time(null); \
struct tm* ltm = localtime(&t); \
char tmp[32] = {0}; \
strftime(tmp, 31, "%h:%m:%s", ltm); \
fprintf(stdout, "[%s %s:%d] " format "\n", tmp, __file__, __line__, ##__va_args__); \
} while(0)
#define inf_log(format, ...) log(inf, format, ##__va_args__)
#define dbg_log(format, ...) log(dbg, format, ##__va_args__)
#define err_log(format, ...) log(err, format, ##__va_args__)
int main() {
inf_log("server start");
dbg_log("client fd = %d", 10);
err_log("accept failed: %s", strerror(2));
return 0;
}输出示例:
[14:23:45 server.cpp:20] server start [14:23:45 server.cpp:21] client fd = 10 [14:23:45 server.cpp:22] accept failed: no such file or directory
这段日志宏虽然简单,但包含了多个 c/c++ 重要知识点:
...、__va_args__、##__va_args____file__、__line__time、localtime、strftimefprintf一句话记忆:
日志宏 = 级别过滤 + 时间/文件/行号 + 可变参数格式化 + do…while(0) 安全包裹。
掌握它,你就理解了服务器开发中最基础的日志模块实现。
到此这篇关于c++ 仿 muduo 高并发服务器:日志模块(实例详解)的文章就介绍到这了,更多相关c++ muduo 高并发服务器内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论