it编程 > 编程语言 > C/C++

C++ 仿 muduo 高并发服务器:日志模块(实例详解)

105人参与 • 2026-09-23 • C/C++

1. 代码总览

#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;
}

这段代码定义了一套简单的日志宏,支持:

2. 为什么需要日志宏?

在服务器开发中,日志是排查问题的重要手段。
但直接写 printf 有几个问题:

  1. 无法控制级别:调试信息、错误信息混在一起。
  2. 缺少上下文:不知道日志来自哪个文件、哪一行。
  3. 代码冗余:每次都要手写时间、文件、行号。
  4. 性能问题:低级别日志在发布版本中不应该输出。

日志宏的目标:

3. 逐行拆解

3.1 日志级别定义

#define inf 0
#define dbg 1
#define err 2
#define log_level inf

这里 log_level = inf,表示所有日志都输出。
如果改成 log_level = dbg,则 inf 日志被过滤,只输出 dbg 和 err。

3.2 核心宏 log

#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)

逐部分解释:

3.2.1do { ... } while(0)

宏体用 do { ... } while(0) 包裹,是 c/c++ 宏的经典技巧。
好处:

例如:

if (cond)
    log(inf, "hello");
else
    ...

如果没有 do...while(0),展开后可能出错。

3.2.2 日志级别过滤

if (level < log_level) break;

3.2.3 获取当前时间

time_t t = time(null);
struct tm* ltm = localtime(&t);
char tmp[32] = {0};
strftime(tmp, 31, "%h:%m:%s", ltm);

注意:localtime 不是线程安全的,多线程环境建议用 localtime_r。
但在简单日志中,通常可以接受。

3.2.4 格式化输出

fprintf(stdout, "[%s %s:%d] " format "\n", tmp, __file__, __line__, ##__va_args__);

输出格式类似:

[14:23:45 server.cpp:10] hello

3.3 三个封装宏

#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("server start");
dbg_log("client fd = %d", fd);
err_log("accept failed: %s", strerror(errno));

4. 关键知识点

4.1 可变参数宏

c++11 起支持可变参数宏:

#define log(format, ...) printf(format, ##__va_args__)

c++20 提供了 __va_opt__,但很多项目仍使用 ##__va_args__。

4.2 预定义宏

这些宏由编译器自动提供,非常适合日志。

4.3 do…while(0) 技巧

为什么不用 {} 而用 do...while(0)?

4.4 日志级别过滤

日志级别数值越大越严重:

log_level 控制最低输出级别。
例如 log_level=dbg,则 inf 不输出,dbg 和 err 输出。

这种设计在编译期决定,低级别日志可以被编译器优化掉,减少运行时开销。

5. 使用示例

#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

6. 总结

这段日志宏虽然简单,但包含了多个 c/c++ 重要知识点:

一句话记忆:

日志宏 = 级别过滤 + 时间/文件/行号 + 可变参数格式化 + do…while(0) 安全包裹。

掌握它,你就理解了服务器开发中最基础的日志模块实现。

到此这篇关于c++ 仿 muduo 高并发服务器:日志模块(实例详解)的文章就介绍到这了,更多相关c++ muduo 高并发服务器内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

赞 (0)

您想发表意见!!点此发布评论

推荐阅读

C++模板特化与缺省参数实现

09-23

C++内存管理之new/delete深度解析

09-23

C++非类型模板参数详解之编译期常量与高性能编程的核心技术

09-22

C++从编译期多态到可变参数模板实战指南

09-22

C++类型转换之隐式转换和显式转换方法全解析

09-22

C++行为树BehaviorTree.CPP的使用小结

09-20

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论