使用printf增加文件名、函数名、行号的打印

【速览】

最终效果如下,快速查看代码实现可以往下翻至黄底字体

【正文】

在实际工程中,随着项目越来越大,调试信息的作用也越来越重要。尤其是调试多线程程序时,详细的调试信息能够帮助我们更加快速地定位问题,因为此时存在线程调度,单步调试的意义已经不大。

我们希望调试信息包含文件名、函数名、行号以及我们自行添加的信息,于是可以这样实现:

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平台为程序实现高效的日志记打印能力了

  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值