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")
语句的方式在实际开发中用起来是很麻烦的。
为了少敲点代码,我基于vsnprintf
和 vprintf
实现了带时间戳和源码信息(__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__)的格式化打印输出的主要内容,如果未能解决你的问题,请参考以下文章