应使用带 do{...}while(0) 包裹的可变参数宏,自动注入__FILE__、__LINE__、__func__,通过std::vfprintf配合stderr实现编译期开关的日志输出,避免直接调用std::cout。

C++ 怎么打印日志 C++简单日志系统宏定义实现代码【工程】

std::cout 和宏组合实现轻量日志输出

直接用 std::cout 打日志太裸,没时间戳、没文件名行号、关起来麻烦。最常用做法是封装一层宏,编译期控制开关,运行时不拖慢主逻辑。

关键点在于:宏要能自动注入 __FILE____LINE____func__,还要支持像 printf 那样可变参数。C++11 起推荐用 variadic macro + std::ostringstreamfmt::format(若引入第三方),但纯标准库下更稳妥的是用 std::vfprintf 配合 stderr 或文件句柄。

#define LOG_INFO(fmt, ...) \
    do { \
        fprintf(stderr, "[%s:%d %s] " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
    } while(0)

__FILE__ 路径太长?用宏截取文件名部分

默认 __FILE__ 展开为绝对路径(如 /home/user/project/src/log.cpp),日志里塞满冗余路径,既难读又占空间。得在宏里做字符串裁剪 —— 但 C 预处理器不支持字符串操作,所以得靠编译器扩展或运行时处理。

常见解法是用 GCC/Clang 的 __builtin_strrchr 或手动写个 basename 风格函数,但更轻量的是利用预处理器拼接:定义一个包装宏,在源文件开头用 #define THIS_FILE "log.cpp",再在日志宏里用 THIS_FILE。不过这要求每个文件手动加,易漏。

如何关闭日志而不删代码?用 NDEBUG 或自定义开关宏

线上环境通常完全禁用调试日志,但又不能把所有 LOG_DEBUG 行注释掉。最干净的方式是依赖编译宏控制是否展开。

#ifdef ENABLE_LOG
# define LOG_DEBUG(fmt, ...) LOG_INFO(fmt, ##__VA_ARGS__)
#else
# define LOG_DEBUG(fmt, ...) do {} while(0)
#endif

多线程环境下 fprintf 安全吗?要不要加锁?

fprintf(stderr, ...) 在多数 libc 实现中是线程安全的(内部有锁),但不保证原子性 —— 两行日志内容可能交叉(如 A 线程打一半,B 线程插进来,最后输出乱序)。对调试够用,但审计或追踪类日志不行。

真正需要顺序一致,就得自己加锁,或改用线程局部缓冲 + 定期刷出。不过锁本身有开销,尤其高并发场景。

日志宏看着简单,但文件名截取、线程安全、编译开关和参数求值时机这四点,任何一个没压住,上线后都可能变成隐性性能瓶颈或排查盲区。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。