分享一个简易的程序日志记录C++类

最近一直在忙实验室的项目开发,很少上博客了,现在总算有一定时间空出来做些总结,今天起陆续更新博客,总结一下半年多来的学习心得。

今天先分享一个简易的程序日志记录类,参考自《一个简单又高效的日志系统》,针对项目本身做了一定的修改和补充,编程水平有限,仅供参考,呵呵 。

头文件:

#ifndef _INFO_LOGGER_H_
#define _INFO_LOGGER_H_

/********************************************************************
 *	created:	8/7/2012   13:41
 *	filename: 	CinfoLogger.h
 *	author:		ZouYuhua @ BUAA
 *	
 *	purpose:	这是一个简易的程序运行日志记录类
 *				主要功能是 
 *				1. 将带参数的格式化字符串写入日志缓存字符串
 *				2. 当缓存字符串长度大于阈值 或者 定时时间到 时,将缓存写入到本地log文件
 *				3. 将带参数的格式化字符串转换为 std::string 字符串
 *				4. 将当前系统时间转换为指定格式的字符串
 *				5. 获取高精度(微秒级)的系统时间
 *				
 *	sample:		使用示例
 *	
 *	 // 包含头文件,可以放在 stdafx.h 中
 *	 
 *	 #include "CinfoLogger.h"

 *	 #define	WRITE_LOG			CinfoLogger::instance()->LogInfo			
 *	 #define	STRING_FORMAT		CinfoLogger::instance()->LogInfo
 *	 #define	ENTER_FUNC			CinfoLogger::instance()->EnterFunction
 *	 #define	LEAVE_FUNC			CinfoLogger::instance()->LeaveFunction
 *	 #define	GET_TIME_STRING		CinfoLogger::instance()->GetCurTimeToString
 *	 #define	GET_TIME_ELAPSED	CinfoLogger::instance()->GetTimeElapsed
 *	 #define	GET_TIME_NOW		CinfoLogger::instance()->GetSystemTimerCount()

 *	 #define	LOG_LEVEL_FORMAT	-1
 *	 #define	LOG_LEVEL_MAIN		0
 *	 #define	LOG_LEVEL_SUB_1		1
 *	 #define	LOG_LEVEL_SUB_2		2
 *	 #define	LOG_LEVEL_SUB_3		3
 *
 *	 //具体调用方式
 *	 
 *	 //1.写入日志
 *	 WRITE_LOG(LOG_LEVEL_MAIN, "正在获取数据。");
 *	 WRITE_LOG(LOG_LEVEL_SUB_1, "The value is %d", value);
 *	 cout << WRITE_LOG(LOG_LEVEL_MAIN, "写入日志缓存,并在控制台窗口显示信息。") << endl;
 *	 	 
 *	 //2.格式化字符串转换
 *	 std::string info = "The ";
 *	 info += STRING_FORMAT(LOG_LEVEL_FORMAT, "value is %d", value);
 *	 
 *	 //3.将当前时间转换为字符串,作为文件名的一部分
 *	 std::string imgFileName = "img_" + GET_TIME_STRING() + ".jpg";
 *	 
 *	 //4.程序运行时间计时
 *	 LONGLONG t_start = GET_TIME_NOW;
 *	 // do something ...
 *	 double t_elapsed = GET_TIME_ELAPSED( t_start );
 *	 WRITE_LOG(LOG_LEVEL_MAIN, "[Main Process] 用时: %.3f ms", t_elapsed);
 *				
 *	 //5.记录函数进出事件
 *	 function func
 *	 {
 *		ENTER_FUNC("func");
 *		...
 *		LEAVE_FUNC("func");
 *	 }
 ********************************************************************/


#pragma once

//-------------------------------------------------------------------------
// Includes
//-------------------------------------------------------------------------

/***
 * 在 stdafx.h 还包含了其它的头文件,也可能在本类中用到
 * 

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

// TODO: 在此处引用程序需要的其他头文件
#include <Windows.h>
#include <iostream>
#include <vector>
#include <time.h>
#include <stdlib.h>
#include <conio.h>

 */

#include <fstream>
//-------------------------------------------------------------------------
// 本类使用 boost 库的 thread 类进行线程管理
// 其在 VC6 中的应用和设置请参考以下资料:
//    《Boost 1_37_0 的安装以及在VC6.0中的使用》http://www.cnblogs.com/cy163/archive/2010/03/27/1698156.html
// 另外一种C++多线程方法,是利用 _beginthreadex,参考以下资料:
//	  《C/C++ Runtime 多线程函数》http://www.cppblog.com/mzty/archive/2007/07/25/28756.html
//-------------------------------------------------------------------------
#include <boost/thread.hpp>

//-------------------------------------------------------------------------
// Defines
//-------------------------------------------------------------------------
#define		MAX_STR_BUF_LEN		30000	//日志缓存最大长度,超过则将日志缓存写入到本地文件
#define		TIME_TO_WRITE_LOG	5000	//定时时间(毫秒),超过则将日志缓存写入到本地文件

using namespace std;

//-------------------------------------------------------------------------
// Class
//-------------------------------------------------------------------------
class CinfoLogger
{
public:

	/*----------------------------
	 * 功能 : 获取类实例静态指针
	 *----------------------------
	 * 函数 : CinfoLogger::instance
	 * 访问 : public static 
	 * 返回 : CinfoLogger*
	 */
	static CinfoLogger* instance();

	/*----------------------------
	 * 功能 : 将 带参数的日志信息 输出到 IDE 输出窗口,并保存到 日志文件缓存 中
	 *----------------------------
	 * 函数 : CinfoLogger::LogInfo
	 * 访问 : public
	 * 返回 : string				[ret]	格式化后的字符串
	 *
	 * 参数 : int		level	[in]	日志级别
	 *									 按照程序分支级别定义,主程序 Kinect_NI 的级别为 0
	 *									 当 level < 0 时,功能相当于 sprintf 函数
	 *									 仅生成 string 字符串,不在输出窗口显示、也不写入缓存
	 * 参数 : char *		fmt		[in]	格式化字符串
	 * 参数 : ...				[in]	具体参数
	 */
	string LogInfo(int level, char *fmt, ...);

	/***
	 *	LogInfo 重载,日志信息 为 字符串常量
	 *
	 * 参数 : int		level	[in]	日志级别
	 * 参数 : string		info	[in]	字符串常量
	 */
	string LogInfo(int level, string info);
	
	/*----------------------------
	 * 功能 : 记录进入某一功能函数的事件
	 *----------------------------
	 * 函数 : CinfoLogger::EnterFunction
	 * 访问 : public 
	 * 返回 : void
	 *
	 * 参数 : string			funcName	[in]	函数名称
	 * 或者
	 * 参数 : const char*	funcName	[in]	函数名称
	 */
	void EnterFunction(string funcName)
	{
		LogInfo(1, "Enter function: " + funcName);
	}
	//重载1
	void EnterFunction(const char* funcName)
	{
		LogInfo(1, "Enter function: %s", funcName);
	}


	/*----------------------------
	 * 功能 : 记录离开某一功能函数的事件
	 *----------------------------
	 * 函数 : CinfoLogger::LeaveFunction
	 * 访问 : public 
	 * 返回 : void
	 *
	 * 参数 : string			funcName	[in]	函数名称
	 * 或者
	 * 参数 : const char*	funcName	[in]	函数名称
	 */
	void LeaveFunction(string funcName)
	{
		LogInfo(1, "Leave function: " + funcName);
	}
	//重载1
	void LeaveFunction(const char* funcName)
	{
		LogInfo(1, "Leave function: %s", funcName);
	}

	/*----------------------------
	 * 功能 : 将当前系统时间(年月日时分秒)转换为 string 字符串
	 *----------------------------
	 * 函数 : CinfoLogger::GetCurTimeToString
	 * 访问 : public 
	 * 返回 : std::string	[ret]	系统时间字符串
	 *
	 * 参数 : TIME_STRING_MODE	mode	[in] 时间字符串的输出格式,分两种:文件名模式 和 文本模式
	 */
	enum TIME_STRING_MODE { 
		FOR_FILE_NAME,	// 按 文件名格式 获取时间字符串
		FOR_TEXT		// 按 文本格式 获取时间字符串
	};
	string GetCurTimeToString(TIME_STRING_MODE mode = FOR_TEXT);

	/*----------------------------
	 * 功能 : 获取计算机内部定时器的当前值
	 *----------------------------
	 * 函数 : CinfoLogger::GetSystemTimerCount
	 * 访问 : public 
	 * 返回 : LONGLONG	[ret]	频率计数
	 *
	 */
	LONGLONG GetSystemTimerCount() 
	{ 
		LARGE_INTEGER liTmp;
		QueryPerformanceCounter( &liTmp ); 

		return liTmp.QuadPart;
	}

	/*----------------------------
	 * 功能 : 获取微秒级的精确时间
	 *----------------------------
	 * 函数 : CinfoLogger::GetTimeElapsed
	 * 访问 : public 
	 * 返回 : double				[ret]	整数位表示毫秒、小数位表示微秒的时间值
	 *
	 * 参数 : LONGLONG tStart	[in]	定时器计数 2
	 */
	double GetTimeElapsed(LONGLONG tStart)
	{
		double dfTime, dfFreq, dfDiff;
		LARGE_INTEGER liTmp;
		LONGLONG tNow;

		QueryPerformanceCounter( &liTmp );	//获得当前计数值
		tNow = liTmp.QuadPart;

		QueryPerformanceFrequency(&liTmp);	//获得时钟频率
		dfFreq = (double)liTmp.QuadPart;

		dfDiff = (double)(tNow - tStart);
		dfTime = dfDiff / dfFreq;	//获得对应的时间值(秒)
		dfTime *= 1000;				//转换为毫秒值

		return dfTime;
	}


	/*----------------------------
	 * 功能 : 将内部变量 m_bRun 置为 false,以退出日志循环线程
	 *----------------------------
	 * 函数 : CinfoLogger::SetExit
	 * 访问 : public 
	 * 返回 : void
	 */
	void SetExit() { m_bRun = false; }

private:

	//-------------------------------------------------------------------------
	// 内部变量
	//-------------------------------------------------------------------------
	static CinfoLogger* _instance;	// 日志类实例静态指针,只创建一次,其它类通过此指针来访问类内函数

	boost::thread m_hThread;		// 线程指针
	boost::mutex m_logMutex;		// 线程对象
	bool m_bRun;					// 线程循环使能标志

	string m_strWriteStrInfo;		// 日志文件缓存字符串
	string m_strLogFileName;		// 日志文件名,每次启动时按照系统时间自动生成

	//-------------------------------------------------------------------------
	// 内部函数
	//-------------------------------------------------------------------------
	
	/*----------------------------
	 * 功能 : 类实例初始化
	 *----------------------------
	 * 函数 : CinfoLogger::CinfoLogger
	 * 访问 : private 
	 * 返回 : 
	 */
	CinfoLogger(void);

	/*----------------------------
	 * 功能 : 更新日志缓存
	 *----------------------------
	 * 函数 : CinfoLogger::WriteLog
	 * 访问 : private 
	 * 返回 : void
	 *
	 * 参数 : string		strLog		[in]	日志信息
	 */
	void WriteLog(string strLog);

	/*----------------------------
	 * 功能 : 日志循环线程
	 * 每隔五秒定时、或者 日志缓存字符串长度大于阈值时
	 * 将缓存的日志信息强制写入到本地文件
	 *----------------------------
	 * 函数 : CinfoLogger::LogProcStart
	 * 访问 : private 
	 * 返回 : void
	 */
	void LogProcStart();

	/*----------------------------
	 * 功能 : 将缓存的日志信息写入到本地日志文件
	 *----------------------------
	 * 函数 : CinfoLogger::WriteLogToFile
	 * 访问 : private 
	 * 返回 : void
	 */
	void WriteLogToFile();

};

#endif

实现代码:

#include "StdAfx.h"
#include "CinfoLogger.h"


//-------------------------------------------------------------------------
//
// ------ 内部变量与函数 ------
// 
//-------------------------------------------------------------------------


// 类实例静态指针(初始为空)
CinfoLogger* CinfoLogger::_instance = NULL;


/*----------------------------
 * 功能 : 类实例初始化
 *----------------------------
 * 函数 : CinfoLogger::CinfoLogger
 * 访问 : private 
 * 返回 : 
 */
CinfoLogger::CinfoLogger(void)
{	
	// 启动信息
	m_strWriteStrInfo = "";

	// 日志文件名称
	m_strLogFileName = "Run_" + GetCurTimeToString(FOR_FILE_NAME) + ".log";

	// 创建日志线程
	m_bRun = true;
	m_hThread = boost::thread( boost::bind( &CinfoLogger::LogProcStart, this ));
}


/*----------------------------
 * 功能 : 日志循环线程
 * 每隔五秒定时、或者 日志缓存字符串长度大于阈值时
 * 将缓存的日志信息强制写入到本地文件
 *----------------------------
 * 函数 : CinfoLogger::LogProcStart
 * 访问 : private 
 * 返回 : void
 */
void
CinfoLogger::LogProcStart()
{
	int nCount = 1;
	clock_t preSaveTime = clock();
	do 
	{
		Sleep(250);

		if ( (clock() - preSaveTime) > TIME_TO_WRITE_LOG )
		{
			preSaveTime = clock();
			WriteLogToFile();	// 每隔五秒写一次日志
		}

	} while (m_bRun);

	if (!m_strWriteStrInfo.empty())
		WriteLogToFile();	// 退出循环后如果缓存非空,则将缓存写入日志 再退出线程

	cout << LogInfo(LOG_LEVEL_SUB_1, "[- Info Logger] 正在退出日志线程!") << endl;
}


/*----------------------------
 * 功能 : 更新日志缓存
 *----------------------------
 * 函数 : CinfoLogger::WriteLog
 * 访问 : private
 * 返回 : void
 *
 * 参数 : string		strLog		[in]	日志信息
 */
void
CinfoLogger::WriteLog(string strLog)
{
	if ( !strLog.empty() )
	{
		m_strWriteStrInfo += "[" + GetCurTimeToString() + "] ";
		m_strWriteStrInfo += strLog;
		m_strWriteStrInfo += _T("\r\n");
	}

	if ( m_strWriteStrInfo.length() > MAX_STR_BUF_LEN )
	{			
		WriteLogToFile();	// 达到一定量时才写入到文件中
	}
}


/*----------------------------
 * 功能 : 将缓存的日志信息写入到本地日志文件
 *----------------------------
 * 函数 : CinfoLogger::WriteLogToFile
 * 访问 : private
 * 返回 : void
 */
void
CinfoLogger::WriteLogToFile()
{
	if (m_logMutex.try_lock())
	{
		try
		{
			// 以 Append 方式打开日志文件
			ofstream osWriter( m_strLogFileName.c_str(),  ios::out | ios_base::app );

			if (osWriter.is_open())
			{
				// 写入日志缓存
				osWriter << m_strWriteStrInfo.c_str();

				// 清空日志缓存
				m_strWriteStrInfo = "";

				// 关闭文件写入器
				osWriter.close();
			}
		}
		catch (std::exception e)
		{
			cout << LogInfo(LOG_LEVEL_FORMAT, "写入文件失败:%s", e.what()) << endl;
		}

		m_logMutex.unlock();
	}
}


//-------------------------------------------------------------------------
// 
// ------- 访问接口 ------
// 
//-------------------------------------------------------------------------


/*----------------------------
 * 功能 : 创建类实例静态指针
 *----------------------------
 * 函数 : CinfoLogger::instance
 * 访问 : public static 
 * 返回 : CinfoLogger*
 */
CinfoLogger* 
CinfoLogger::instance()
{
	// 若静态指针为空,则创建类实例,并获取指针
	if (_instance == NULL)
	{
		_instance = new CinfoLogger();
	}
	// 返回静态指针
	return _instance;
}


/*----------------------------
 * 功能 : 将 日志信息 输出到 IDE 输出窗口,并保存到 日志文件缓存 中
 *----------------------------
 * 函数 : CinfoLogger::LogInfo
 * 访问 : public
 * 返回 : string				[ret]	格式化后的字符串
 *
 * 参数 : int		level	[in]	日志级别
 *									 按照程序分支级别定义,主程序 Kinect_NI 的级别为 0
 *									 当 level < 0 时,功能相当于 sprintf 函数
 *									 仅生成 string 字符串,不在输出窗口显示、也不写入缓存
 * 参数 : char *		fmt		[in]	格式化字符串
 * 参数 : ...				[in]	具体参数
 */
string 
CinfoLogger::LogInfo(int level,char *fmt, ...)
{
	// 格式化输入的字符串 fmt 到输出字符串 out
	char out[1024];
	va_list body;
	va_start(body, fmt);
	vsprintf_s(out, fmt, body);    
	va_end(body);    

	// 将日志信息转换为 string 字符串,存入缓存变量中
	string strTemp(out);	              

	//日志级别需要大于设定级别 或者 小于 0,则只返回字符串,不作记录
	if(level > CparamServer::instance()->LogLevel() || level < 0)
	{
		return strTemp;
	}

	// 在输出窗口显示日志信息
	OutputDebugStringA(out); 
	OutputDebugStringA("\n");

	// 将信息存入日志缓存中
	WriteLog( strTemp );

	return strTemp;
}


/*----------------------------
 *	LogInfo 重载,日志信息 为 字符串常量
 *----------------------------
 * 参数 : int		level	[in]	日志级别,按照程序分支级别定义,主程序 Kinect_NI 的级别为 0
 * 参数 : string		info	[in]	字符串常量
 */
string 
CinfoLogger::LogInfo(int level, string info)
{
	//日志级别需要小于或等于设定级别,否则不记录
	if(level > CparamServer::instance()->LogLevel())
	{
		return info;
	}

	if (level >= 0)
	{
		// 在输出窗口显示日志信息
		OutputDebugStringA(info.c_str()); 
		// 将信息存入日志缓存中
		WriteLog( info );
	}

	return info;
}


/*----------------------------
 * 功能 : 将当前系统时间转换为 string 字符串
 *----------------------------
 * 函数 : CinfoLogger::GetCurTimeStr
 * 访问 : public
 * 返回 : std::string	[ret]	系统时间字符串
 *
 * 参数 : TIME_STRING_MODE	mode	[in] 时间字符串的输出格式,分两种:文件名模式 和 文本模式
 */
string 
CinfoLogger::GetCurTimeToString(TIME_STRING_MODE mode /* = FOR_TEXT */)
{
	int strLen = 20;
	string retStr = "0";

	if (mode == FOR_TEXT)
	{
		char *pszCurrTime1 = (char*)malloc(sizeof(char)*strLen);
		memset(pszCurrTime1, 0, sizeof(char)*strLen);
		time_t now;
		time(&now);
		strftime(pszCurrTime1, strLen , "%Y-%m-%d %H:%M:%S", localtime(&now));

		retStr = pszCurrTime1;
	} 	
	else if (mode == FOR_FILE_NAME)
	{
		char *pszCurrTime2 = (char*)malloc(sizeof(char)*strLen);
		memset(pszCurrTime2, 0, sizeof(char)*strLen);
		time_t now;
		time(&now);
		strftime(pszCurrTime2, strLen , "%Y_%m_%d_%H_%M_%S", localtime(&now));

		retStr = pszCurrTime2;
	}

	return retStr;	
}



猜你喜欢

转载自blog.csdn.net/chenyusiyuan/article/details/7726614