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

这是本人实现的一个简单的软件调试日志。论功能肯定不能和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
//----------------------------------------------------------------------------------
打开控制台和重导向日志到文件函数的实现
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

使用方法如下:

//例子

#ifdef _DEBUG
	NamespaceKun::OpenConsole();	//debug模式下MFC程序打开控制台显示
#else
	//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

InfoPrintf("日志普通输出,数字:%d", 12345678);
	WarnPrintf("日志警告输出,数字:%d", 12345678);
	ErrPrintf("日志错误信息输出,数字:%d", 12345678);
效果如下图:



实现比较简单通道也造成了一些问题。比如长时间运行后会有造成日志文件过大。

你可能感兴趣的:(c++,windows,调试日志,mfc,debug)