简单日志宏实现(C++)
意义:
快速定位程序运行逻辑出错的位置。
背景:
项目在运行中可能会遇到各种问题,而出问题是开发过程中不可避免的一部分。关键在于能够有效地找到,并解决问题。
解决问题的方式:
-
GDB调试:
- 适用于:程序崩溃后的定位。
- 局限性:逐步调试过程繁琐且耗时较长。
-
系统运行日志分析:
- 方法:在程序中任何可能存在逻辑错误的地方输出提示信息。
- 目的:通过日志快速定位**
逻辑问题
**的具体位置。 - 优势:相比GDB调试更为高效,能更快地识别和理解问题所在。
1. 宏定义和日志级别
#define LDBG 0
#define LINF 1
#define LERR 2
#define LDEFAULT LINF
LDBG
(Debug): 调试级别的日志,用于记录详细的调试信息。LINF
(Info): 信息级别的日志,用于记录一般性运行信息。LERR
(Error): 错误级别的日志,用于记录严重错误信息。LDEFAULT
: 定义了默认的日志级别,当前设置为LINF
,即仅输出等于或高于LINF
的日志信息。
通过设置不同的日志级别,可以控制日志的输出粒度。
2. 日志宏实现
#define LOG(level, format, ...) {\
if (level >= LDEFAULT){\
time_t t = time(NULL);\
struct tm *lt = localtime(&t);\
char time_tmp[32] = {0};\
strftime(time_tmp, 31, "%m-%d %T", lt);\
fprintf(stdout, "[%s][%s:%d] " format "\n", time_tmp, __FILE__, \
__LINE__, ##__VA_ARGS__);\
}\
}
核心功能解析
-
LOG(level, format, ...)
- 这是一个可变参数的宏,用于生成带有特定级别的日志。
- 参数:
level
:日志级别,决定日志是否输出。format
:格式化字符串,与printf
风格一致。...
:可变参数,与format
匹配。
-
日志级别判断
if (level >= LDEFAULT)
- 只有当日志级别不低于默认级别
LDEFAULT
时,日志才会输出。 - 例如,如果
LDEFAULT
是LINF
,则只有LINF
和LERR
级别的日志会被打印。
- 只有当日志级别不低于默认级别
-
获取当前时间
time_t t = time(NULL); struct tm *lt = localtime(&t); char time_tmp[32] = {0}; strftime(time_tmp, 31, "%m-%d %T", lt);
-
使用
time()
获取当前时间。
-
使用
localtime()
将时间转换为本地时间。
-
使用
strftime()
将时间格式化为字符串,格式为月-日 时:分:秒
(%m-%d %T
)。
-
- 格式化日志输出
fprintf(stdout, "[%s][%s:%d] " format "\n", time_tmp, __FILE__, \ __LINE__, ##__VA_ARGS__);
- 将日志输出到标准输出
stdout
。 - 输出格式:
[时间]
:如[01-19 14:23:45]
。[文件名:行号]
:使用预定义宏__FILE__
和__LINE__
获取当前文件名和行号。format
和__VA_ARGS__
:用户指定的日志内容,支持printf
风格的格式化。- ❗## 处理
__VA_ARGS__
- 将日志输出到标准输出
3. 简化的日志宏
#define DLOG(format, ...) LOG(LDBG, format, ##__VA_ARGS__);
#define ILOG(format, ...) LOG(LINF, format, ##__VA_ARGS__);
#define ELOG(format, ...) LOG(LERR, format, ##__VA_ARGS__);
DLOG
: 调试日志,等价于LOG(LDBG, ...)
。ILOG
: 信息日志,等价于LOG(LINF, ...)
。ELOG
: 错误日志,等价于LOG(LERR, ...)
。
开发者可以直接使用这些宏记录不同级别的日志,就不需要手动指定日志级别啦。
4. 示例代码
#include "log_macro.h"
int main() {
ILOG("This is an info log.");
DLOG("This is a debug log.");
ELOG("This is an error log: %s", "Critical failure!");
return 0;
}
输出结果(假设 LDEFAULT
为 LINF
):
[01-19 14:23:45][main.c:4] This is an info log.
[01-19 14:23:45][main.c:6] This is an error log: Critical failure!
DLOG
的日志没有输出,因为LDBG < LDEFAULT
。
5. 优点和意义
-
快速定位问题:
- 日志输出包括文件名和行号,无需手动查找代码位置。
- 时间戳记录了日志发生的时间,便于分析问题的时间线。
-
简单易用:
- 宏封装了复杂的日志格式和级别控制,开发者只需调用
DLOG
、ILOG
或ELOG
即可。
- 宏封装了复杂的日志格式和级别控制,开发者只需调用
-
灵活性:
- 默认日志级别可以通过修改
LDEFAULT
轻松调整。 - 支持可变参数,适应各种格式的日志内容。
- 默认日志级别可以通过修改
6. 改进建议
-
日志输出到文件:
- 当前日志输出到标准输出,实际应用中可能需要写入日志文件。可以扩展宏,增加输出目的地的选择。
-
线程安全:
- 多线程程序中,日志写入可能会产生竞争。可以结合互斥锁或线程安全的 I/O 函数实现线程安全。
-
日志级别
动态配置
:- 提供运行时修改日志级别的能力,而不是通过编译时宏定义固定。
这种简单的日志系统一般就可以应对大多数小型项目的调试需求啦