泾渭分明——改造C语言printf启用日志分级输出

不论是在测试阶段或使用阶段,拥有良好的、有秩序的日志输出都是一个明智的选择。

这里简略记录分享下自己对于这一目标的实现方式。

代码实现

代码结合了 前文 提到的彩色输出。可以用宏定义自行开关。

日志等级通过宏定义设置,这样在编译时可以很方便地控制编译选项(比如区分 Release 和 Debug)

print.hdownload
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#ifndef PRINT_H
#define PRINT_H

#include <stdio.h>
#include <windows.h>

DWORD enableColorful(void);

//* 配置 */
#define PRINT_LEVEL LEVEL_DEBUG // 日志等级
#define PRINT_COLORFUL 1 // 是否启用彩色

//* 日志等级定义 */
#define LEVEL_NANO 0
#define LEVEL_ERROR 1
#define LEVEL_WARN 2
#define LEVEL_INFO 3
#define LEVEL_ENTRY 4
#define LEVEL_DEBUG 5

//* 颜色定义 */
#define COLOR_GRAY "\033[37m"
#define COLOR_GREEN "\033[32m"
#define COLOR_YELLOW "\033[33m"
#define COLOR_DARKGRAY "\033[30m"
#define COLOR_BLACK "\033[30m"
#define COLOR_NOCOLOR "\033[0m"
#define COLOR_DEEPBLUE "\033[34m"
#define COLOR_RED "\033[31m"

//* 纯净模式 */
#define PR_CUST(level, ...) \
do \
{ \
if (level <= PRINT_LEVEL) \
printf(__VA_ARGS__); \
} while (0);

//* 分类定义 */
#if PRINT_COLORFUL
#define pr_nano(fmt, ...) PR_CUST(LEVEL_NANO, fmt, ##__VA_ARGS__)
#define pr_err(fmt, ...) PR_CUST(LEVEL_ERROR, COLOR_RED "ERROR:" fmt COLOR_NOCOLOR, ##__VA_ARGS__)
#define pr_warn(fmt, ...) PR_CUST(LEVEL_WARN, COLOR_YELLOW "WARN: " fmt COLOR_NOCOLOR, ##__VA_ARGS__)
#define pr_info(fmt, ...) PR_CUST(LEVEL_INFO, COLOR_GREEN "INFO: " fmt COLOR_NOCOLOR, ##__VA_ARGS__)
#define pr_bug(fmt, ...) PR_CUST(LEVEL_DEBUG, COLOR_DEEPBLUE "DEBUG: " fmt COLOR_NOCOLOR, ##__VA_ARGS__)
#define pr_entry(inout) PR_CUST(LEVEL_ENTRY, "%s() %s", __func__, #inout)
#else
#define pr_nano(fmt, ...) PR_CUST(LEVEL_NANO, fmt, ##__VA_ARGS__)
#define pr_err(fmt, ...) PR_CUST(LEVEL_ERROR, "ERROR:" fmt, ##__VA_ARGS__)
#define pr_warn(fmt, ...) PR_CUST(LEVEL_WARN, "WARN: " fmt, ##__VA_ARGS__)
#define pr_info(fmt, ...) PR_CUST(LEVEL_INFO, "INFO: " fmt, ##__VA_ARGS__)
#define pr_bug(fmt, ...) PR_CUST(LEVEL_DEBUG, "DEBUG: " fmt, ##__VA_ARGS__)
#define pr_entry(inout) PR_CUST(LEVEL_ENTRY, "%s() %s", __func__, #inout)
#endif // PR_COLORFUL

#endif // PRINT_H

print.cdownload
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
#include "print.h"

/*!
* 为传统控制台启用色彩支持
* 本质上是利用了虚拟终端序列
*/
DWORD enableColorful(void)
{
#if PRINT_COLORFUL
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE)
{
return GetLastError();
}

DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode))
{
return GetLastError();
}

dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(hOut, dwMode))
{
return GetLastError();
}
#endif
return 0;
}

调用示例

首先是配置日志等级(print.h),通过宏定义:

1
2
#define PRINT_LEVEL LEVEL_DEBUG // 日志等级
#define PRINT_COLORFUL 1 // 是否启用彩色

其次是启用控制台彩色支持:

1
2
#include "print.h"
enableColorful();

接下来就可以愉快地打印了:

1
2
3
4
5
pr_nano("白色,无论什么情况下都显示的消息\n");
pr_err("红色,ERROR级的消息\n");
pr_warn("黄色,WARNNING级的消息\n");
pr_info("绿色,INFO级的消息\n");
pr_bug("深蓝,DEBUG级的消息\n");