赞
踩
【速览】
最终效果如下,快速查看代码实现可以往下翻至黄底字体
【正文】
在实际工程中,随着项目越来越大,调试信息的作用也越来越重要。尤其是调试多线程程序时,详细的调试信息能够帮助我们更加快速地定位问题,因为此时存在线程调度,单步调试的意义已经不大。
我们希望调试信息包含文件名、函数名、行号以及我们自行添加的信息,于是可以这样实现:
printf("["__FILE__"][Line: %d][%s]: error happened!\n", __LINE__, __func__);
其中,__FILE__, __LINE__, __func__都是C语言的预定义符号,他们都在头文件"stdio.h"中。
__FILE__: 当前源文件名,表示为字符串常量;
__LINE__: 当前行号,表示为十进制整型常量;
__func__: 当前函数名,表示为字符串常量。
这样已经实现了文件名、行号、函数名的打印。那么,我们真的要每次打印都放上这么长一段代码么,有没有更好的实现方式呢?其实我们可以尝试用宏的方式来避免一些重复性的输入,为了方便,甚至可以加一个宏定义,统一控制是否打印:
- #include <stdio.h>
-
- //调试打印开关
- #define __DEBUG
-
- #ifdef __DEBUG
- #define info(format, ...) printf("["__FILE__"][Line: %d][%s]: "format"\n", __LINE__, __func__, ##__VA_ARGS__)
- #else
- #define info(format, ...)
- #endif
- int main()
- {
- info("error happened!");
- return 0;
- }
嗯,到这里好像差不多了,但是想想,我还能再进一步,我希望我的程序能够像linux终端那样输出五颜六色的东西,我用颜色区分打印信息的重要程度,红色表示错误、黄色表示警告、绿色表示正常信息,这样,就不用大海捞针一般地一行一行分析了。
- #include <stdio.h>
-
- //调试打印开关
- #define __DEBUG
-
- #ifdef __DEBUG
- #define normal_info(format, ...) printf("["__FILE__"][Line: %d][%s]: \033[32m"format"\033[32;0m\n", __LINE__, __func__, ##__VA_ARGS__)
- #define warning_info(format, ...) printf("["__FILE__"][Line: %d][%s]: \033[33m"format"\033[32;0m\n", __LINE__, __func__, ##__VA_ARGS__)
- #define error_info(format, ...) printf("["__FILE__"][Line: %d][%s]: \033[31m"format"\033[32;0m\n", __LINE__, __func__, ##__VA_ARGS__)
- #else
- #define normal_info(format, ...)
- #define warn_info(format, ...)
- #define error_info(format, ...)
- #endif
-
- int main(void)
- {
- normal_info("green");
- warning_info("yellow");
- error_info("red");
-
- return 0;
- }
__VA_ARGS__是C99规范中新增的可变参数的宏,如果可变参数被忽略或者为空,也就是只让printf打印字符串而不打印变量,则##操作将使预处理器去除它前面的逗号。
使printf打印带颜色的语法为:
printf("\033[字背景颜色;字体颜色m字符串\033[0m" );
"\033"表示对颜色的调用,与"\e"等价,这里我的背景色没有设置,使用的是默认值,字体颜色31、32、33分别表示红、绿、黄,打印完成之后将字体颜色设置为0,即恢复默认值,防止影响后面的打印颜色。
看一看打印效果吧
咦,Windows terminal上的颜色好像不大对劲,换Windows上的控制台看看
这回颜色正了。
其实"stdio.h"中还有一些其他的预定义符号,比如日期、时间等,我们想要也可以加上。
2023.12.28更新
之前碰到过在Windows控制台(MS-DOS)使用颜色转义符不生效的问题,最近在一个项目中找到了原因并解决。我们写如下一行代码:
printf("\033[32mGreen\033[0m\n");
本意是想让Windows控制台输出绿色的"Green"字符串,但实际效果是下面这个样子:
通过查阅在Microsoft文档发现,Windows的控制台默认是不支持光标移动、颜色/字体模式等ANSI转义序列的,在碰到这些转义序列时无法解析,所以颜色转义失效的问题并不是代码带来的,而是控制终端的问题,网上有如下两种解决办法:
1. 安装ANSICON;
2. 通过代码启用控制台虚拟终端序列。
方法1安装一个软件以启用Windows控制台的ANSI转义序列支持,这种方法可以解决问题,但是每换一台电脑需要重新安装一次,通用性不高。方法2参考微软官方文档,通过如下操作,调用Windows API启用当前终端的ANSI转义序列支持(需要包含Windows.h):
- #ifdef _WIN32
- HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
- DWORD dwMode = 0;
-
- if(!GetConsoleMode(hTerminal, &dwMode))
- {
- printf("error:%d\n", GetLastError());
- return 0;
- }
- dwMode |= 0x0004;
- if (!SetConsoleMode(hTerminal, dwMode))
- {
- printf("error:%d\n", GetLastError());
- return 0;
- }
- #endif
然后颜色转义序列就可以生效了。上述代码中"_Win32"可以用来区分是否是Windows平台,"0x0004"选项表示启用终端控制字符解析,详见SetConsoleMode 函数的传入值"ENABLE_VIRTUAL_TERMINAL_PROCESSING"。
需要注意的是,Windows控制台的转义操作命令是跟在ESC后面的,而不是033,于是,我们的printf代码变成了如下模样(x1b表示十六进制的1B,其实就是ESC的ASCII码):
- #include <stdio.h>
- #include <stdlib.h>
- #include <windows.h>
-
- int main()
- {
-
- #ifdef _WIN32
- HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
- DWORD dwMode = 0;
-
- if(!GetConsoleMode(hTerminal, &dwMode))
- {
- printf("error:%d\n", GetLastError());
- return 0;
- }
- dwMode |= 0x0004;
- if (!SetConsoleMode(hTerminal, dwMode))
- {
- printf("error:%d\n", GetLastError());
- return 0;
- }
- #endif
-
- printf("\x1b[32mGreen\x1b[0m\n");
- return 0;
- }
再配合前面的日期、时间、函数名、行号打印,就可以在Windows平台和Linux平台为程序实现高效的日志记打印能力了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。