C:_debug_printf,基于vsnprintf 或 vprintf实现带时间戳和源码信息(__FILE__,__FUNCTION__, __LINE__)的格式化打印输出

Posted 10km

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C:_debug_printf,基于vsnprintf 或 vprintf实现带时间戳和源码信息(__FILE__,__FUNCTION__, __LINE__)的格式化打印输出相关的知识,希望对你有一定的参考价值。

写C程序的时候,printf输出调试信息是常态,printf输出调试信息时如果能自动带源码信息(__FILE__,__FUNCTION__, __LINE__),显然更方便查找问题,如果能再加上时间戳就更完美了。
如果到处都用printf("%s:%s:%d, %s\\n",__FILE__,__FUNCTION__, __LINE__,"hello")写起来也太麻烦了;而且有的时候还需要向内存缓冲区打印输出。而且__FILE__提供的是源码的全路径名,打印实可能会很长。
所以这种直接在代码写printf("%s:%s:%d, %s\\n",__FILE__,__FUNCTION__, __LINE__,"hello")语句的方式在实际开发中用起来是很麻烦的。

为了少敲点代码,我基于vsnprintfvprintf实现了带时间戳和源码信息(__FILE__,__FUNCTION__, __LINE__)的格式化打印输出函数_debug_printf 完整代码及调用示例如下,需要的拿去:

_debug_printf.c

/*
 * _debug_printf.c
 *  _debug_printf
 *  基于vsnprintf 或 vprintf实现带时间戳和源码信息(__FILE__,__FUNCTION__, __LINE__)的格式化打印输出
 *  Created on: 2021年10月31日
 *      Author: guyadong
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

//************************************
// 带时间戳和源码信息(__FILE__,__FUNCTION__, __LINE__)的格式化打印输出
// @param    char *       buf    vsnprintf 输出缓冲区,如果为 NULL,则向控制台输出(vprintf)
// @param    size_t       bufsz  vsnprintf 输出缓冲区长度,如果为 0,则向控制台输出(vprintf)
// @param    const char * file   源码文件名,__FILE__
// @param    const char * fun    函数名    ,__FUNCTION__
// @param    int          line   源码行号  ,__LINE__
// @param    const char * fmt    格式字符串,参见 vsnprintf 或 vprintf
// @param    ...                 输出参数 
// @return   int                 调用 vsnprintf 或 vprintf 的返回值
//************************************
int _debug_printf(char* buf, size_t bufsz, const char* file, const char* fun, int line, const char* fmt, ...)
{
	const char*name = NULL;
	if (file) 
	{
		/************************************************************************/
		/* 从路径中提取文件名                                                   */
		/************************************************************************/
		name = strrchr(file, '/');
		if (!name)
		{
#if WIN32
			name = strrchr(file, '\\\\');
			if (!name)
			{
				name = file;
			}
#else
			name = file;
#endif
		}
		if (name < (file + strlen(file) - 1)) 
		{
			/** 跳过分割符  */
			name += 1;
		}
	}
	/************************************************************************/
	/* 生成时间戳字符串                                                     */
	/************************************************************************/
	char time_buf[32] = { 0 };
	time_t _t = time(NULL);
	size_t rc = strftime(time_buf, sizeof(time_buf), "%Y/%m/%d %H:%M:%S", gmtime(&_t));
	/************************************************************************/
	/* 计算输出格式字符串长度                                               */
	/************************************************************************/
	size_t fmtsz;
	if (name && fun)
	{
		/** __FILE__:__FUNCTION__:__LINE__ */
		fmtsz = 1 + snprintf(NULL, 0, "%s %s:%s:%d %s\\n", time_buf, name, fun, line, fmt);
	}
	else if (name)
	{
		/** __FILE__:__LINE__ */
		fmtsz = 1 + snprintf(NULL, 0, "%s %s:%d %s\\n", time_buf, name, line, fmt);
	}
	else
	{
		/** __FUNCTION__:__LINE__ */
		fmtsz = 1 + snprintf(NULL, 0, "%s %s:%d %s\\n", time_buf, fun, line, fmt);
	}
	char* logfmt = (char*)malloc(fmtsz);
	if (logfmt)
	{
		/************************************************************************/
		/* 内存分配成功                                                         */
		/* 重新生成一个包含__FILE__,__FUNCTION__,__LINE__的format字符串         */
		/************************************************************************/
		if (name && fun )
		{ 
			/** __FILE__:__FUNCTION__:__LINE__ */
			snprintf(logfmt, fmtsz, "%s %s:%s:%d %s\\n", time_buf, name, fun, line, fmt);
		}
		else if(name)
		{
			/** __FILE__:__LINE__ */
			snprintf(logfmt, fmtsz, "%s %s:%d %s\\n", time_buf, name, line, fmt);
		}
		else
		{
			/** __FUNCTION__:__LINE__ */
			snprintf(logfmt, fmtsz, "%s %s:%d %s\\n", time_buf, fun, line, fmt);
		}
		va_list args;
		va_start(args, fmt);
		int c;
		if (buf && bufsz)
		{
			/************************************************************************/
			/* 如果指定了输出缓冲区则向缓冲区输出                                   */
			/************************************************************************/
			c = vsnprintf(buf,bufsz,logfmt, args);
		}
		else 
		{
			/************************************************************************/
			/* 如果没有指定输出缓冲区则向标准控制台输出                             */
			/************************************************************************/
			c = vprintf(logfmt, args);
		}
		va_end(args);
		/** 释放申请的内存 */
		free(logfmt);
		/** 返回 vsnprintf 或 vprintf 返回值 */
		return c;
	}
	else
	{
		/************************************************************************/
		/* 内存分配失败则不能输出__FILE__,__FUNCTION__,__LINE__                 */
		/************************************************************************/
		va_list args;
		va_start(args, fmt);
		int c;
		if (buf && bufsz)
		{
			/************************************************************************/
			/* 如果指定了输出缓冲区则向缓冲区输出                                   */
			/************************************************************************/
			c = vsnprintf(buf, bufsz, fmt, args);
		}
		else
		{
			/************************************************************************/
			/* 如果没有指定输出缓冲区则向标准控制台输出                             */
			/************************************************************************/
			c = vprintf(fmt, args);
		}
		va_end(args);
		/** 返回 vsnprintf 或 vprintf 返回值 */
		return c;
	}
}

/** 向控制台输出 */
#define debug_printf(fmt,...) _debug_printf(NULL, 0, __FILE__,__FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)

/** 向内存缓存区输出输出 */
#define debug_snprintf(buf,bufsz,fmt,...) _debug_printf(buf, bufsz, __FILE__,__FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)

/** 调用示例 */
int main()
{

	/** 控制台输出测试 */
	debug_printf("hello %s","tom");

	/** 内存缓冲区输出测试 */
	char output[128];
	int c = debug_snprintf(output,sizeof(output),"welcom to my party %s","jerry");
	printf("output size %d bytes, content:\\n", c);
	printf("%s\\n", output);
}

以上代码MSVC/GCC编译通过可以直接在命令行编译运行。

MSVC cl 编译器编译运行示例

MSVC cl 编译器编译

>cl _debug_printf.c
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.00.24215.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

_debug_printf.c
_debug_printf.c: warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请
将该文件保存为 Unicode 格式以防止数据丢失
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:_debug_printf.exe
_debug_printf.obj

执行生成的_debug_printf.exe

>_debug_printf.exe
2021/11/16 07:35:18 debug_printf.c:main:159 hello tom
output size 69 bytes, content:
2021/11/16 07:35:18 debug_printf.c:main:163 welcom to my party jerry

以上是关于C:_debug_printf,基于vsnprintf 或 vprintf实现带时间戳和源码信息(__FILE__,__FUNCTION__, __LINE__)的格式化打印输出的主要内容,如果未能解决你的问题,请参考以下文章

《Unix网络编程》中的错误处理函数

[Redis Chapter 5] 远程访问 Redis 基于 C#/Python

ARM非对齐操作异常解决过程

基于STM32详解C语言内存分配

一个基于C++11的单例模板类

接口_requests_基于python