您的位置:首页 > 编程语言 > C语言/C++

c++ windows下的简单的日志输出DebugPrintf

2014-12-12 01:20 267 查看
这是本人实现的一个简单的软件调试日志。论功能肯定不能和glog、log4cplus等日志相提并论,但是我也觉得这个两个日志库太强大,也有点庞大,个人想法。

实现的这个日志只包含一个头文件和一个cpp文件。

日志使用平台为windows,主要测试环境为win32和MFC。

是将日志直接输出到标准输出stdout和stderr。通过宏实现可变参数以及输出日志时间和文件名函数名行号。要输出什么内容可以通过_PRINTF_LOCATION_自行配置。当然很多是将日志写入文件中的,这里通过重导向标准输出流(__iob_func()[1]、__iob_func()[2])的方式输出到文件,重导向了标准输出流有个意外的收获,就是有时同时把程序中一些其他库的错误输出也保存了下来。通过_fsopen()函数实现日志文件的读共享写独占。通过_snprintf_s()格式化字符串在信息长度超过buff大小时会自动截断而不会像其他格式化函数会崩溃。

这里贴出部分源码

namespace NamespaceKun
{
// 创建并打开日志文件,当文件名为空,表示关闭对应的日志文件,两个日志文件名最好不相同
// infoOutFileName替换的是标准输出流stdout
// errOutFileName替换的标准错误输出流stderr
bool LogInit( const char *infoOutFileName, const char * errOutFileName,
const char * infoOutFileMode = "w+", const char * errOutFileMode = "w+" );

//打开控制台
void OpenConsole();
};

//信息输出
#define _INFO_PRINTF_
//警告输出
#define _WARNING_PRINTF_
//错误输出
#define _ERROR_PRINTF_

//输出方式:
//0: //是否显示输出的位置,无附加信息
//1://是否显示输出的位置,行号
//2://是否显示输出的位置,函数,行号
//3://是否显示输出的位置,包括文件,函数,行号
#ifdef _DEBUG
#define _PRINTF_LOCATION_ 0
#else
#define _PRINTF_LOCATION_ 3
#endif

//----------------------------------------------------------------------------------
//是否显示输出的位置
#if _PRINTF_LOCATION_ == 0
#define _FPRINTF_PARAMETERS_ "%s  %s  %s\n",datetimebuff,pszLevel,szOutBuff
#elif _PRINTF_LOCATION_ == 1
#define _FPRINTF_PARAMETERS_ "%s  %s  %s\tLine:%d\n",datetimebuff,pszLevel,szOutBuff,__LINE__
#elif _PRINTF_LOCATION_ == 2
#define _FPRINTF_PARAMETERS_ "%s  %s  %s\tFunction:%s, Line:%d\n",datetimebuff,pszLevel,szOutBuff,__FUNCTION__,__LINE__
#elif _PRINTF_LOCATION_ == 3
#define _FPRINTF_PARAMETERS_ "%s  %s  %s\tFilePath:%s, Function:%s, Line:%d\n",datetimebuff,pszLevel,szOutBuff,__FILE__,__FUNCTION__,__LINE__
#else
#define _FPRINTF_PARAMETERS_ "%s  %s  %-500s\tFilePath:%s, Function:%s, Line:%d\n",datetimebuff,pszLevel,szOutBuff,__FILE__,__FUNCTION__,__LINE__
#endif

//----------------------------------------------------------------------------------
//构造输出字数串
#define _PRINTF_FORMATE_CONTENT_(szLevel, format, ...)	char datetimebuff[32] = {0};\
const char *pszLevel = szLevel;\
time_t nowtime = time(NULL);\
tm tmdatetime;\
localtime_s(&tmdatetime, &nowtime);\
strftime(datetimebuff,sizeof(datetimebuff),"%Y-%m-%d %H:%M:%S",&tmdatetime);\
char szOutBuff[10240] = {0};\
_snprintf_s(szOutBuff, sizeof(szOutBuff)-1, _TRUNCATE, format, ##__VA_ARGS__);

//调试输出----------------------------------------------------------------------------------
#ifdef _INFO_PRINTF_
#define InfoPrintf(format, ...);	{\
_PRINTF_FORMATE_CONTENT_("INFO ", format, ##__VA_ARGS__)	\
fprintf(stdout, _FPRINTF_PARAMETERS_);\
fflush(stdout);\
};
#else
#define InfoPrintf(...); do{ }while(0);
#endif
//----------------------------------------------------------------------------------
//调试输出----------------------------------------------------------------------------------
#ifdef _WARNING_PRINTF_
#define WarnPrintf(format, ...);	{\
_PRINTF_FORMATE_CONTENT_("WARN ", format, ##__VA_ARGS__)	\
fprintf(stdout, _FPRINTF_PARAMETERS_);\
fflush(stdout);\
};
#else
#define WarnPrintf(...); do{ }while(0);
#endif
//----------------------------------------------------------------------------------
//错误输出----------------------------------------------------------------------------------
#ifdef _ERROR_PRINTF_
#define ErrPrintf(format, ...); {\
_PRINTF_FORMATE_CONTENT_("ERROR", format, ##__VA_ARGS__)	\
fprintf(stderr, _FPRINTF_PARAMETERS_);\
fflush(stderr);\
};
#else
#define ErrPrintf(...); do{ }while(0);
#endif
//----------------------------------------------------------------------------------
打开控制台和重导向日志到文件函数的实现
<span style="font-size:18px;">namespace NamespaceKun
{
//
bool LogInit( const char *infoOutFileName, const char * errOutFileName,
const char * infoOutFileMode, const char * errOutFileMode )
{
//保存stdout,stderr句柄
static FILE stdoutHandle = __iob_func()[1];
static FILE stderrHandle = __iob_func()[2];
static FILE * logInfoFile = NULL;
static FILE * logErrFile = NULL;
bool bRet = true;	//返回值

if ( NULL != logInfoFile )
{//关闭文件句柄
if (logErrFile == logInfoFile)
{//上次打开的是同一个文件
logErrFile = NULL;
__iob_func()[2] = stderrHandle;
}
fclose(logInfoFile);
logInfoFile = NULL;
__iob_func()[1] = stdoutHandle;
}
if ( NULL != logErrFile )
{//关闭文件句柄
fclose(logErrFile);
logErrFile = NULL;
__iob_func()[2] = stderrHandle;
}
//替换标准输出流
if ( infoOutFileName )
{
//打开文件
logInfoFile = _fsopen(infoOutFileName, infoOutFileMode, _SH_DENYWR);
if (NULL != logInfoFile)
{
__iob_func()[1] = *logInfoFile;	//stdout
}
else
{
char errbuff[1024] = {0};
strerror_s(errbuff, sizeof(errbuff), errno);
fprintf(stderr, "%s\n", errbuff);
bRet = false;
}
}
//错误输出流
if (errOutFileName)
{
if ( 0 == strcmp(errOutFileName, infoOutFileName) )
{//当错误和信息文件是同一个文件,则错误输出也使用信息输出的句柄
if (NULL != logInfoFile)
{
__iob_func()[2] = *logInfoFile;	//stderr
logErrFile = logInfoFile;
}
}
else
{
//打开文件
logErrFile = _fsopen(errOutFileName, errOutFileMode, _SH_DENYWR);
if (NULL != logErrFile)
{
__iob_func()[2] = *logErrFile;	//stderr
}
else
{
char errbuff[1024] = {0};
strerror_s(errbuff, sizeof(errbuff), errno);
fprintf(stderr, "%s\n", errbuff);
bRet = false;
}
}
}
return bRet;
}

void OpenConsole()
{//开启控制台
BOOL re = ::AllocConsole();
FILE *consoleStdout, *consoleStderr, *consoleStdin;
freopen_s(&consoleStdout,"CONOUT$","w+t", stdout);
freopen_s(&consoleStderr, "CONOUT$","w+t", stderr);
freopen_s(&consoleStdin, "CONIN$", "r+t", stdin);
}
};//namespace NamespaceKun</span>

使用方法如下:

<span style="font-size:18px;">//例子

#ifdef _DEBUG
NamespaceKun::OpenConsole();<span style="white-space: pre;">	</
8dd3
span>//debug模式下MFC程序打开控制台显示
#else
<span style="white-space: pre;">	</span>//release模式下直接将日志输出到文件
time_t nowtime = time(NULL);
tm tmdatetime;
localtime_s(&tmdatetime, &nowtime);
char szInfoLogNameBuff[256] = {0};
char szErrLogNameBuff[256] = {0};
strftime(szInfoLogNameBuff,sizeof(szInfoLogNameBuff),"%Y-%m-%d_%H:%M:%S日志INFO.txt",&tmdatetime);
strftime(szErrLogNameBuff,sizeof(szErrLogNameBuff),"%Y-%m-%d_%H:%M:%S日志ERR.txt",&tmdatetime);
NamespaceKun::LogInit(szInfoLogNameBuff, szErrLogNameBuff);//stdout和stderr可以输出到同一个文件
#endif</span>


<span style="font-size:18px;">InfoPrintf("日志普通输出,数字:%d", 12345678);
WarnPrintf("日志警告输出,数字:%d", 12345678);
ErrPrintf("日志错误信息输出,数字:%d", 12345678);</span>效果如下图:

实现比较简单通道也造成了一些问题。比如长时间运行后会有造成日志文件过大。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息