Easylogging的封裝使用一

一.封裝的目的和使用範圍
在上一篇文章中介紹了Easylogging的簡單使用:http://blog.csdn.net/woshichenweixian/article/details/77018452 ,一般情況下,在自己的項目中使用時,可以再對其封裝一下,使其適合自己的使用習慣。這博文章主要介紹一下我在最近的一個項目中對Easylogging的封裝使用,該項目是一個C++後臺服務器,對log系統的使用要求如下:
(1)能區分log等級:debug,trace,info,warn,err等等級
(2)能方便控制某個等級的log輸出與否
(3)能方便輸出:char buf[len] , int buf[lend]等數組,而無需區分數組元素是字符類型還是整數類型
(4)能方便輸出數據包

二.初始化和配置
首先是對Easylogging初始化用一個宏進行封裝,若後期需要添加其他初始化代碼,則只需修改該宏就可以:
#define LogSysInit   _INITIALIZE_EASYLOGGINGPP

接着是對Easylogging進行配置,Easylogging的配置和我們最終要使用的log等級有很大關係,所以先介紹一下在該封裝系統中對log等級的定義和使用方式:
對log等級進行封裝和控制,我個人一般對log等級區分如下:
Debug等級:只在在開發階段輸出的log,用於跟蹤開發過程中的調試輸出;
Info等級:輸出程序運行相關的信息,可在上線後選擇是否輸出該等級的log;
Trace等級:輸出一些程序狀態數據,用於跟蹤程序運行狀態,一般在上線後應該一直讓該等級的log輸出,但也可關掉該log。
Warn等級:輸出影響到程序正常運行,但還能繼續運行的數據信息。在上線後也應該一直讓該等級的log輸出。
Error等級:輸出導致程序不能繼續運行,必須馬上終止程序的錯誤信息。在上線後也應該一直讓該等級log輸出。

幾個比較容易混淆的log等級的使用情景如下:
Trace:跟蹤程序的運行分支,例如:
	if(condition 1)
	{
		Log(Trace) << "in condition 1" ;
	}
	else if(condition 2)
	{
		Log(Trace) << "in condition 2" ;
	}

Warn:不會導致程序必須馬上終止的錯誤:
if(valus == InvalidValus)
	{
		log(warn) << "valus is invalid:" << valus ;
		return ;
	} 
	
	DoSomeThing() ;

Error:導致程序必須馬上終止的錯誤:
	char *buf = new(nothrow) char ; 

	if(buf == NULL)
	{
		log(Error) << "new is failed" ;
		ExitProcess() ;
	}

	DoSomeThing() ;

爲了達成上面所說的log等級配置,可以抽象出一個配置接口出來:
#define LogSysCfg(cfgInfo , debugSwitch)   LogCfg::initLog(cfgInfo,debugSwitch) ;
  LogSysCfg宏傳入兩個配置參數,一個是cfgInfo,用來控制Info和Trace等級的log輸出;一個是debugSwitch,用來控制Debug等級的輸出。
真正實現配置的地方在LogCfg類的靜態方法中:
class LogCfg{
	public:
		LogCfg(){}

		/*@param cfgInfo,控制trace,info等級的log輸出:
		err和warn等級任何情況下都輸出,trace和info等級的根據配置信息開啓:
		0:不開啓trace,info
		1:開啓trace,不開info
		2:開啓trace和info
		@param debugSwitch,控制debug等級的log輸出
		*/ 
		static bool initLog(int cfgInfo,int debugSwitch)
		{
			easyloggingpp::Configurations cfg;
			cfg.setToDefault();

			std::string cfgStr = GetCfgStr(cfgInfo,debugSwitch) ;
			
			cfg.parseFromText(cfgStr.c_str()) ;
			easyloggingpp::Loggers::reconfigureAllLoggers(cfg) ;
			cfg.clear() ;

			return true ;
		}

		static bool reCfgLog(){return true ;}


	private:
		static std::string GetCfgStr(int cfgInfo,int debugSwitch)
		{
			std::string cfgStr("*ALL:\n                                                     \
						  FORMAT = [%datetime] :%log\n										\
						  ENABLED = false \n                                                \
						  TO_FILE = true \n                                                 \
						  TO_STANDARD_OUTPUT = false \n                                     \
						  ROLL_OUT_SIZE = 2097152    \n                                     \
						  FILENAME = logs/ErrWarnLog.log \n                                 \
						  *ERROR:\n                                                         \
						  ENABLED = true\n                                                  \
						  *WARNING:\n                                                       \
						  ENABLED = true\n                                                  \
						  *TRACE:\n                                                         \
						  FILENAME = logs/TraceLog.log \n                                   \
						  ROLL_OUT_SIZE = 10485760 \n                                       \
						  ");

			if((1 == cfgInfo) || (2== cfgInfo))
				cfgStr += "ENABLED = true\n" ;

			cfgStr += " *INFO:\n                                                      \
					  FILENAME = logs/InfoLog.log \n                                  \
					  ROLL_OUT_SIZE = 10485760 \n                                     \
					  " ;

			if(2 == cfgInfo)
				cfgStr += "ENABLED = true \n" ;

			cfgStr += " *DEBUG:\n                                                       \
					  FILENAME = logs/DebugLog.log \n                                   \
					  ROLL_OUT_SIZE = 10485760 \n                                       \
					  " ;

			if(1 == debugSwitch)
				cfgStr += "ENABLED = true" ;

			return cfgStr ;
		}

	};  

經過上面的配置後,Debug,Info,Trace等級可配置,而Warn和Error等級的log會一直輸出。log文件在當前目錄下的logs文件夾裏面,每個等級的log存放在各自的文件裏面。

三.對log輸出的封裝
我個人的習慣是不直接使用Easylogging給出的宏,而是自己再封一層,因爲若以後需要改動的時候,直接改自己封的宏就好了。一些常見的封裝:
//普通log輸出
#define LogInfo			LINFO
#define LogTrace		LTRACE
#define LogWarn			LWARNING
#define LogError	    LERROR

#define LogBreak(fmt)                                                                                             \
	    {                                                                                                            \
			LogWarn<<fmt;                                                                                        \
			break ;                                                                                                  \
		}                                                                                                            \

//如果斷言失敗,則把該log打印出來,不會結束程序
#define LogAssert(condition)  LWARNING_IF(!(condition))<<"[Log Assert]:"

//用於服務端發給客戶端時的log輸出
#define LogSTC    LogDebug<<"[S To C]:  "
//用於客戶端發給服務端端時的log輸出
#define LogCTS    LogDebug<<"[C To S]:  " 

如果之後不想用Easylogging的LINFO,則只需修改宏就好,例如,把它改成用Easylogging的BINFO。
LogBreak宏可以方便的用來跳出循環之前打印一條log;
LogAssert宏則實現類似於斷言的功能;
LogSTC和LogCTS適用於CS架構中區分服務器發給客戶端huo客戶端發給服務器的消息。

接着是輸出數組的一個封裝:
enum LEVEL{
	MY_ERR = 0 ,
	MY_Warn  ,
	MY_Debug ,
	MY_Trace ,
	MY_Info 
};

#define LogInfoDump(info , size,buff)             logDump(info , buff , size , LEVEL::MY_Info)       
#define LogTraceDump(info , size,buff)            logDump(info , buff , size , LEVEL::MY_Trace)
#define LogWarnDump(info , size,buff)             logDump(info , buff , size , LEVEL::MY_Warn)
#define LogErrorDump(info , size,buff)            logDump(info , buff , size , LEVEL::MY_ERR)
#define LogDebugDump(info , size,buff)            logDump(info , buff , size , LEVEL::MY_Debug)

template<class _TYPE>
void logDump(char *info,_TYPE *buff ,int size,LEVEL logLevel)                                                                                       
{                                                                                                                             
	std::string str(info) ;                                                                                                        
	str += ": " ;                                                                                                             
	if(buff == NULL || size <=0)                                                                                                                                                                                                                     
		str+="buff is null!" ;                                                                                                                                                                                                                        
	else                                                                                                                      
	{                                                                                                                         
		std::stringstream ss ;                                                                                                     
		for(int _dumpArrSize = 0; _dumpArrSize < size ; ++_dumpArrSize)                                                                                                                                                                           
			ss << buff[_dumpArrSize] << "," ;                                                                                                                                                                                    
		str += ss.str() ;                                                                                                     
	}                                                                                                                         
	logStrOut(size , str ,logLevel) ;                                     
}

void logStrOut(int size , std::string str , LEVEL logLevel)
{
	switch(logLevel){
	case MY_ERR:
		LogError<<"[Arr Count:"<<size <<"]" <<str ; 
		break;
	case MY_Debug:
		LogDebug<<"[Arr Count:"<<size <<"]" <<str ;
		break;
	case MY_Info:
		LogInfo<<"[Arr Count:"<<size <<"]" <<str ;
		break;
	case MY_Trace:
		LogTrace<<"[Arr Count:"<<size <<"]" <<str ;
		break;
	case MY_Warn:
		LogWarn<<"[Arr Count:"<<size <<"]" <<str ;
		break;
	default:break ;
	}
}

在上面的封裝中,存在一個問題,即:當輸出char類型或者char數組時,可能出現亂碼。例如輸出:
char aa =1 ;        char arr[3] = {1,2,3} ;
	char aa = 1 ;
	LogDebug<<"logDebug:" << aa ;
	char buf[3] = {1,2,3} ;
	LogDebugDump("logdebugdump:",3,buf) ;
本意是要輸出1和1,2,3的,但因爲在easylogging中,log是通過標準輸出流輸出的,所以如果輸出一個char類型數據,是其字符,而不是數值來的。而ASSCII碼的1,2,3對應的字符都是亂碼來的。
下一篇文章《Easylogging的封裝使用二》將講講如何解決這個問題,並講一下如何封裝自己的數據包(struct ,class類型)進行輸出。













發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章