C/C++ 语言通过编译宏控制 Release/Debug 版本二进制级别的日志控制

摘要

本文使用 c/cpp 的编译宏特性, 通过编译宏 ## 拼接的技巧,在现有大量存量代码接口不变更的情况下, 完成日志字符串二进制级别的控制.

背景

要求 Release 版本在二进制中去除调试日志,而 debug 版本保持不变;
代码中已存在大量使用旧日志接口的代码, 要做到存量代码零变更.

实现

二进制级别的去除, 不可能使用函数条件判断实现, 只能通过编译宏实现.

现有一日志宏, 通过调用log_func输出日志到某个文件。

1
2
3
4
5
6
7
8
9
10
11
// 定义
#define debug_log(level, format, arg...) \
do { \
log_func((level), __FILE__, __LINE__, format, ##arg); \
} while (0)

// 调用
debug_log(DLOG_DEBUG, "int_var=%d", int_var);
debug_log(DLOG_INFO, "int_var=%d", int_var);
debug_log(DLOG_ERROR, "int_var=%d", int_var);

通过编译宏技巧##, 以及条件编译宏实现日志宏隔离, 和接口的无感知变化.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义不变
#define debug_log(level, format, arg...) \
do \
{ \
log_func((level), __FILE__, __LINE__, format, ##arg); \
} while (0)
// 新增定义宏
#define LOG_MARCO(level, format, arg...) \
do \
{ \
log_func((level), __FILE__, __LINE__, format, ##arg); \
} while (0)

#ifdef RELEASE
// 从level转为宏, 空定义不需要的级别, 再转为level
#undef debug_log
#define debug_log(level, format, arg...) LOG_##level(format, ##arg)
// 空定义, 实现二进制级别剔除
#define LOG_DLOG_DEBUG(format, arg...)
#define LOG_DLOG_MASS(format, arg...)
// 真正调用日志
#define LOG_DLOG_INFO(format, arg...) \
LOG_MARCO(DLOG_INFO, format, ##arg)
#define LOG_DLOG_ERROR(format, arg...) \
LOG_MARCO(DLOG_ERROR, format, ##arg)
#endif

// 调用处不变
debug_log(DEBUG, "int_var=%d", int_var);
debug_log(INFO, "int_var=%d", int_var);
debug_log(ERROR, "int_var=%d", int_var);

其他的部分, 无非是构建脚本,cmake 和编译宏之间的控制配合,不算复杂,按下不表。

验证

二进制级别的验证,可以使用二进制查看器, 或者gnu命令strings execute_bin|grep "some debug log"

Reference

C语言宏定义中#与##的用法