C++实现的一个简单的日志库

最近一直在看《Linux多线程服务器编程》这本书。看了里面实现的一个日志库功能,觉得很有意思。于是,就在2周前就准备开始模仿它实现。由于最近比较忙,直到今天才完成,其实也没怎么去验证和测试。毕竟只是作为自己学习之用,来检验是否真看懂了原先的实现。

就目前来看功能算是能用,目前也没有发现什么问题。就先这样,以后有时间的话在去重新设计。

整个日志库相对比较简单,目前只支持2个设置。设置日志等级和输出模式。

日志等级共支持5种等级:

DEBUG Level:指出细粒度信息事件对调试应用程序是非常有帮助的。
INFO Level:消息在粗粒度级别上突出强调应用程序的运行
WARN Level:表明会出现潜在错误的情况。
ERROR Level:指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL Level:指出每个严重的错误事件将会导致应用程序的退出。
日志输出模式共支持3种输出:

//这几个宏是用来设置输出模式的:输出到标准输出 输出到log文件 输出到标准输出和log文件
#define LOGGER_MODE_STDOUT		0x01
#define LOGGER_MODE_LOGFILE		0x02
#define LOGGER_MODE_OUTANDFILE	(LOGGER_MODE_STDOUT | LOGGER_MODE_LOGFILE)
事实上一个完整的日志库会有许多设置供用户选择。具体可以参考一些开源的日志库。

目前由于时间有限,没有继续的添加功能。

好了,废话不多说了,先来看看效果吧!

#include 
#include "Logger.h"

using namespace std;
using namespace Log;

//现在通过一个测试用例来看看怎么使用及效果怎么样

int add(int a, int b)
{
	return a + b;
}

int sub(int a, int b)
{
	return a - b;
}

int mult(int a, int b)
{
	return a * b;
}

int divide(int a, int b)
{
	if (b == 0)
		LOG_FATAL << "Divisor cannot be zero";

	return a / b;
}



int main(void)
{
	//日志的报警等级为INFO
	CLogger::setLogLevel(CLogger::INFO);
	//我们设置日志输出到文件和标准输出
	CLogger::setOutputMode(LOGGER_MODE_OUTANDFILE);

	LOG_INFO << "The test program began to run!";

	LOG_DEBUG << "Debug Mode!";

	int iNum1;
	int iNum2;
	int iSum;
	cout << "Please inout two number:" << endl;

	cin >> iNum1 >> iNum2;
	LOG_INFO << "User input two number: " << iNum1 << "and" << iNum2;

	iSum = add(iNum1, iNum2);
	LOG_INFO << "iNum1 + iNum2 = " << iSum;

	iSum = sub(iNum1, iNum2);
	LOG_INFO << "iNum1 - iNum2 = " << iSum;

	iSum = mult(iNum1, iNum2);
	LOG_INFO << "iNum1 * iNum2 = " << iSum;

	iSum = divide(iNum1, iNum2);
	LOG_INFO << "iNum1 / iNum2 = " << iSum;

	LOG_INFO << "The test program end!";

	return 0;
}
上面的测试代码很简单,就是输入2个数,计算加,减,乘,除。我们用这个日志库来记录起来。

C++实现的一个简单的日志库_第1张图片

我们可以测试下当除数为0时的情况。
C++实现的一个简单的日志库_第2张图片

和预想的一样,当发生发生等级为FATAL错误时结束程序。

测试分析就到此结束,这个日志库分别在VS2013上和Ubuntu上运行通过,Ubuntu上需要定义LINUX。

下面就贴上代码的实现。代码中有比较详细的注释。

1.LogCommon.h

#ifndef __LOG_COMMON_H__
#define __LOG_COMMON_H__

#include 

namespace Log
{
	typedef struct LogDate
	{
		int m_iYear;
		int m_iMon;
		int m_iDay;
	}LOGDATE;

	//下面这个类主要是将__FILE__(包含文件完整路径和文件名)转化为对应的源文件
	//事实上日志中为了简洁去掉完整的路径,只保留文件名
	class CFileName
	{
	public:
		CFileName(const char *pFilePathAndName)
			:m_pFileName(pFilePathAndName)
			, m_iLen(static_cast(strlen(pFilePathAndName)))
		{

			const char *pTmp =
#ifdef LINUX
				strrchr(pFilePathAndName, '/');
#else
				strrchr(pFilePathAndName, '\\');
#endif	//#ifdef LINUX
			if (pTmp != NULL)
			{
				m_pFileName = pTmp + 1;
				m_iLen = static_cast(strlen(m_pFileName));
			}
		}

		const char *m_pFileName;
		int m_iLen;
	};

	//下面这个类用来作为一个固定缓存,这个缓存将输出的日志信息
	//追加到缓存区中,以便最后一条完整日志的输出。
	const int iFixedBuffer = 4096;			//可以使用的正常的缓存大小
	const int iFixedBufferBig = 65535;		//可以使用的最大的缓存大小

	template
	class CFixedBuffer
	{
	public:

		CFixedBuffer()
			:m_pCur(m_arrData)
		{
			memset(m_arrData, 0x00, SIZE);
		}

		~CFixedBuffer()
		{
			//nothing
		}

		void append(const char *pData, int iLen)
		{
			char *pEndBuffer = &m_arrData[SIZE - 1];
			if (static_cast(pEndBuffer - m_pCur) > iLen)
			{
				memcpy(m_pCur, pData, iLen);
				m_pCur += iLen;
			}
		}
		//这个方法是用来获取buffer数据
		const char *data()
		{
			return m_arrData;
		}
		//这个方法是用来获取buffer的长度
		int len()
		{
			return static_cast(m_pCur - m_arrData);
		}
		//这个方法是用来将某字符串追加到buffer中
		void appendString(const char *pString, int iLen)
		{
			memcpy(m_pCur, pString, iLen);
			m_pCur += iLen;
		}
		//这个方法是用来返回buffer当前的指针位置,
		//以便外部能直接操作buffer,提高效率
		char *getCurrent(void)
		{
			return m_pCur;
		}
		//这个方法与getCurrent配套使用
		void appendLen(int iLen)
		{
			m_pCur += iLen;
		}

	private:
		char m_arrData[SIZE];
		char *m_pCur;
	};

	//下面这个函数模板是用来实现整型转换为字符串
	const char NumTab[] = { "9876543210123456789" };
	template
	int NumberToString(char arrBuffer[], T value)
	{
		int iStrNum = 0;
		int iRemainder;
		T TmpValue1 = value;
		T TmpValue2 = value;
		const char *pNumTab = &NumTab[9];

		if (value < 0)
			iStrNum++;

		do
		{
			iStrNum++;
		} while ((TmpValue1 /= 10) != 0);

		char *pTmp = arrBuffer + iStrNum;

		*pTmp-- = '\0';

		do
		{
			iRemainder = static_cast(TmpValue2 % 10);
			*pTmp-- = pNumTab[iRemainder];
			TmpValue2 /= 10;
		} while (TmpValue2 != 0);

		if (value < 0)
			*pTmp = '-';
		return iStrNum;
	}
}

#endif	//#ifndef __LOG_COMMON_H__
2.LogTime.h

#ifndef __LOG_TIME_H__
#define __LOG_TIME_H__

#include 

namespace Log
{
	class CLogTime
	{
	public:
		CLogTime();
		~CLogTime();

		std::string &getLogTime();

	private:
		std::string m_strTimeString;
	};
}

#endif	//#ifndef __LOG_TIME_H__
3.LogTime.cpp

#include 
#include "LogTime.h"
#include "LogFile.h"
#include "LogCommon.h"

#if _MSC_VER
#define snprintf _snprintf
#endif

namespace Log
{
	static const char *arrWeek[] = { "Sunday", "Monday", "Tuesday", 
		"Wednessday","Thursday", "Friday", "Saturday" };

	CLogTime::CLogTime()
	{
		char arrBuffer[32];
		memset(arrBuffer, 0x00, sizeof(arrBuffer));

		time_t timep;
		struct tm *pTm;
		time(&timep);
		pTm = localtime(&timep);

		snprintf(arrBuffer, sizeof(arrBuffer), "%d-%02d-%02d %02d:%02d:%02d %s ",
			pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday,
			pTm->tm_hour, pTm->tm_min, pTm->tm_sec, arrWeek[pTm->tm_wday]);

		m_strTimeString = arrBuffer;

		LOGDATE tDate;
		tDate.m_iYear = pTm->tm_year + 1900;
		tDate.m_iMon = pTm->tm_mon + 1;
		tDate.m_iDay = pTm->tm_mday;
		CLogFile *pLogFile = CLogFile::instance();
		pLogFile->updateLogFile(&tDate);
	}

	CLogTime::~CLogTime()
	{
		//nothing
	}

	std::string &CLogTime::getLogTime()
	{
		return m_strTimeString;
	}
}
4.LogStream.h

#ifndef __LOG_STREAM_H__
#define __LOG_STREAM_H__

#include 
#include "LogCommon.h"

namespace Log
{
	//这个类主要是日志流的实现
	//其实就是重载了操作符<<
	//可能没有考虑到所有情况,就先这样吧
	class CLogStream
	{
	public:
		CLogStream();
		~CLogStream();

		const char *GetStreamBuff(void);
		int GetStreamBuffLen(void);

		CLogStream &operator<<(char);

		CLogStream &operator<<(unsigned char);

		CLogStream &operator<<(short);

		CLogStream &operator<<(unsigned short);

		CLogStream &operator<<(int);

		CLogStream &operator<<(unsigned int);

		CLogStream &operator<<(long);

		CLogStream &operator<<(unsigned long);

		CLogStream &operator<<(long long);

		CLogStream &operator<<(unsigned long long);

		CLogStream &operator<<(float);

		CLogStream &operator<<(double);

		CLogStream &operator<<(char *pChar);

		CLogStream &operator<<(const char *pChar);

		CLogStream &operator<<(unsigned char *pChar);

		CLogStream &operator<<(const unsigned char *pChar);

		CLogStream &operator<<(std::string strString);

	private:
		typedef CFixedBuffer FixedBuffer;
		FixedBuffer m_Buffer;	//这个就是具体的Buffer
	};
}

#endif	//#ifndef __LOG_STREAM_H__
5.LogStream.cpp

#include 
#include "LogStream.h"

#if _MSC_VER
#define snprintf _snprintf
#endif

namespace Log
{
	const int iFloatMaxStrLen = 16;
	const int iDoubleMaxStrLen = 32;

	CLogStream::CLogStream()
	{

	}

	CLogStream::~CLogStream()
	{
	
	}

	const char *CLogStream::GetStreamBuff(void)
	{
		return m_Buffer.data();
	}

	int CLogStream::GetStreamBuffLen(void)
	{
		return m_Buffer.len();
	}

	CLogStream &CLogStream::operator << (char value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (unsigned char value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (short value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (unsigned short value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (int value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (unsigned int value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (long value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (unsigned long value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (long long value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator << (unsigned long long value)
	{
		int iLen = NumberToString(m_Buffer.getCurrent(), value);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator<<(float fValue)
	{
		int iLen = snprintf(m_Buffer.getCurrent(), iFloatMaxStrLen, "%f", fValue);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator<<(double dValue)
	{
		int iLen = snprintf(m_Buffer.getCurrent(), iDoubleMaxStrLen, "%lf", dValue);
		m_Buffer.appendLen(iLen);
		return *this;
	}

	CLogStream &CLogStream::operator<<(char *pChar)
	{
		m_Buffer.appendString(pChar, strlen(pChar));
		return *this;
	}

	CLogStream &CLogStream::operator<<(const char *pChar)
	{
		m_Buffer.appendString(pChar, strlen(pChar));
		return *this;
	}

	CLogStream &CLogStream::operator<<(unsigned char *pChar)
	{
		return operator<<(reinterpret_cast(pChar));
	}

	CLogStream &CLogStream::operator<<(const unsigned char *pChar)
	{
		return operator<<(reinterpret_cast(pChar));
	}

	CLogStream &CLogStream::operator << (std::string strString)
	{
		return operator<<(strString.c_str());
	}
}
6.LogFile.h

#ifndef __LOG_FILE_H__
#define __LOG_FILE_H__

#include 
#include "LogCommon.h"

namespace Log
{
	class CLogFile
	{
		friend class CLogTime;
	public:
		CLogFile();
		~CLogFile();

		static void OutputFunc(const char *pMsg, int iLen);
		static void FlushFunc(void);

		//这个方法是用来将日志写入Log文件
		void OutputLogFile(const char *pMsg, int iLen);
		void FlushLogFile(void);
		//单例模式
		static CLogFile *instance(void);

	private:
		void createLogDir();
		std::string getLogFileName(void);
		void openLogFile(void);
		//这个方法是用来重新创建一个新文件的,
		//我们不希望这个方法暴露出来(在类LogTime使用),声明
		//为私有的,为了使类LogTime能调用,我们声明类LogTime
		//为类CLogFile的友元类
		void updateLogFile(LOGDATE *pDate);

		LOGDATE m_Date;
		std::fstream m_LogFileStream;
	};
}

#endif	//#ifndef __LOG_FILE_H__
7.LogFile.cpp

#include 
#include 
#include 
#include "LogFile.h"

#ifdef LINUX
#include 
#include 
#include 
#else
#include 
#include 
#endif	//#ifdef LINUX

#if _MSC_VER
#define snprintf _snprintf
#endif

namespace Log
{
#ifdef LINUX
	static const char arrLogFileDir[] = "./Logger";
#else
	static const char arrLogFileDir[] = "D:\\Logger";
#endif	//#ifdef LINUX

	CLogFile::CLogFile()
		:m_Mutex()
	{
		time_t timep;
		struct tm *pTm;
		time(&timep);
		pTm = localtime(&timep);

		m_Date.m_iDay = pTm->tm_mday;
		m_Date.m_iMon = pTm->tm_mon + 1;
		m_Date.m_iYear = pTm->tm_year + 1900;

		createLogDir();		//创建存放Log的目录
		openLogFile();		//打开对应日志文件 
	}

	CLogFile::~CLogFile()
	{

	}

	void CLogFile::OutputFunc(const char *pMsg, int iLen)
	{
		CLogFile *pLogFile = instance();
		pLogFile->OutputLogFile(pMsg, iLen);
	}

	void CLogFile::FlushFunc(void)
	{
		CLogFile *pLogFile = instance();
		pLogFile->FlushLogFile();
	}

	void CLogFile::OutputLogFile(const char *pMsg, int iLen)
	{
		boost::mutex::scoped_lock lock(m_Mutex);
		if (m_pFile != NULL)
			::fwrite(pMsg, 1, iLen, m_pFile);
	}

	void CLogFile::FlushLogFile(void)
	{
		boost::mutex::scoped_lock lock(m_Mutex);
		::fflush(m_pFile);
	}

	CLogFile *CLogFile::instance(void)
	{
		static CLogFile tLogFile;

		return &tLogFile;
	}

	void CLogFile::createLogDir(void)
	{	
#ifdef LINUX
		if (access(arrLogFileDir, F_OK) == -1)
#else
		if (_access(arrLogFileDir, 0) == -1)
#endif	//#ifdef LINUX
		{
			//不存在这个目录,创建它 
#ifdef LINUX
			mkdir(arrLogFileDir, 0777);
#else
			mkdir(arrLogFileDir);
#endif
		}
	}

	std::string CLogFile::getLogFileName(void)
	{
		char arrBuffer[64];

		memset(arrBuffer, 0x00, sizeof(arrBuffer));
		snprintf(arrBuffer, sizeof(arrBuffer), "%04d-%02d-%02d-Log.txt",
			m_Date.m_iYear, m_Date.m_iMon, m_Date.m_iDay);

		return arrBuffer;
	}

	void CLogFile::openLogFile(void)
	{
		char arrBuffer[64];
		std::string strLogFileName = getLogFileName();

		memset(arrBuffer, 0x00, sizeof(arrBuffer));
#ifdef LINUX
		snprintf(arrBuffer, sizeof(arrBuffer), "%s/%s", arrLogFileDir, strLogFileName.c_str());
#else
		snprintf(arrBuffer, sizeof(arrBuffer), "%s\\%s", arrLogFileDir, strLogFileName.c_str());
#endif

		boost::mutex::scoped_lock lock(m_Mutex);

		m_pFile = fopen(arrBuffer, "a");
	}

	void CLogFile::updateLogFile(LOGDATE *pDate)
	{
		if (pDate == NULL)
			return;
		
		if (m_Date.m_iYear != pDate->m_iYear || m_Date.m_iMon != pDate->m_iMon || m_Date.m_iDay != pDate->m_iDay)
		{
			//新的一天开始了,那么就,关闭之前的。重新创建个对应日期的日志文件。
			m_Date.m_iYear = pDate->m_iYear;
			m_Date.m_iMon = pDate->m_iMon;
			m_Date.m_iDay = pDate->m_iDay;

			FlushLogFile();
			fclose(m_pFile);
			m_pFile = NULL;

			openLogFile();
		}
	}
}
8.LogRealize.h

#ifndef __LOG_REALIZE_H__
#define __LOG_REALIZE_H__

#include "LogTime.h"
#include "LogCommon.h"
#include "LogStream.h"

namespace Log
{
	class CLogRealize
	{
	public:
		explicit CLogRealize(CFileName &SourceFile, const char *pLevel, int iLine);
		~CLogRealize();

		CLogStream &GetLogStream(void);

		//这个方法是在完成日志流的输出后填充文件名和行号的
		void FinishLog();

		//我们的实现上,日志流是先存储在固定的
		//Buffer里面,等到整条日志流输出完毕。
		//这个方法就是获取获取日志流
		const char *GetLogStreamBuff(void);
		int GetLogStreamBuffLen(void);

	private:
		int m_iLine;				//行号
		CLogTime m_LogTimeStr;		//Log时间类
		CFileName m_SourceFileName;	//文件名
		CLogStream m_Stream;		//LogStream类
	};
}

#endif	//#ifndef __LOG_REALIZE_H__
9.LogRealize.cpp

#include "LogRealize.h"

namespace Log
{
	CLogRealize::CLogRealize(CFileName &SourceFile, const char *pLevel, int iLine)
		:m_SourceFileName(SourceFile)
		, m_iLine(iLine)
		, m_LogTimeStr()
		, m_Stream()
	{
		//在构造的时候会填充日志头(其实就是时间和报警等级)
		//首先,输出打印日志时间到LogStream中的Buffer
		m_Stream << m_LogTimeStr.getLogTime();

		//其次,输出日志等级
		m_Stream << pLevel;
	}

	CLogRealize::~CLogRealize()
	{
		
	}

	CLogStream &CLogRealize::GetLogStream(void)
	{
		return m_Stream;
	}

	void CLogRealize::FinishLog()
	{
		//根据我们的日志格式安排,最后输出的是
		//文件名即行号。这个方法是在Logger析构
		//时候调用的,表示一条日志流的完成,可以
		//输出到标准输出或文件中去。
		m_Stream << " - " << m_SourceFileName.m_pFileName << ":" << m_iLine << "\n";
	}

	const char *CLogRealize::GetLogStreamBuff(void)
	{
		return m_Stream.GetStreamBuff();
	}

	int CLogRealize::GetLogStreamBuffLen(void)
	{
		return m_Stream.GetStreamBuffLen();
	}
}
10.Logger.h

#ifndef __LOGGER_H__
#define __LOGGER_H__

#include 
#include "LogStream.h"
#include "LogCommon.h"

namespace Log
{
	class CLogRealize;	//具体实现的向前声明

	class CLogger
	{
	public:
		//DEBUG Level:指出细粒度信息事件对调试应用程序是非常有帮助的。
		//INFO Level:消息在粗粒度级别上突出强调应用程序的运行
		//WARN Level:表明会出现潜在错误的情况。
		//ERROR Level:指出虽然发生错误事件,但仍然不影响系统的继续运行。
		//FATAL Level:指出每个严重的错误事件将会导致应用程序的退出。
		//当我们定义了日志的优先级之后,应用程序中比设置的优先级别低的
		//都会被输出。
		enum LogLevel {DEBUG, INFO, WARN, ERROR, FATAL};

		typedef void(*OutputFunc)(const char *pMsg, int iLen);
		typedef void(*FlushFunc)(void);

		//设置和获取日志等级
		static void setLogLevel(LogLevel level);
		static LogLevel getLogLevel(void);
		//设置输出模式的,支持3种输出模式
		static void setOutputMode(int iMode);

		CLogger(CFileName SourceFile, LogLevel Level, int iLine);
		~CLogger();

		CLogStream &GetLogStream(void);

	private:
		static void setOutputFunc(OutputFunc Func);
		static void setFlushFunc(FlushFunc Func);

		static void OutputOutAndLog(const char *pMsg, int iLen);
		static void FlushAll(void);

		//使用Pimpl机制,解开类的使用接口和实现的耦合
		std::shared_ptr m_spImpl;	//使用智能指针来管理
		LogLevel m_LogLevel;
	};

//这几个宏是用来设置输出模式的:输出到标准输出 输出到log文件 输出到标准输出和log文件
#define LOGGER_MODE_STDOUT		0x01
#define LOGGER_MODE_LOGFILE		0x02
#define LOGGER_MODE_OUTANDFILE	(LOGGER_MODE_STDOUT | LOGGER_MODE_LOGFILE)

#define LOG_DEBUG	if (CLogger::getLogLevel() <= CLogger::DEBUG)	\
						CLogger(__FILE__, CLogger::DEBUG, __LINE__).GetLogStream()

#define LOG_INFO	if (CLogger::getLogLevel() <= CLogger::INFO)	\
						CLogger(__FILE__, CLogger::INFO, __LINE__).GetLogStream()

#define LOG_WARN	if (CLogger::getLogLevel() <= CLogger::WARN)	\
						CLogger(__FILE__, CLogger::WARN, __LINE__).GetLogStream()

#define LOG_ERROR		CLogger(__FILE__, CLogger::ERROR, __LINE__).GetLogStream()

#define LOG_FATAL		CLogger(__FILE__, CLogger::FATAL, __LINE__).GetLogStream()
}


#endif	//#ifndef __LOGGER_H__
11.Logger.cpp

#include 
#include "Logger.h"
#include "LogRealize.h"
#include "LogFile.h"

namespace Log
{
	const char *arrLevel[] = { "[DEBUG] ", "[INFO] ", "[WARN] ", "[ERROR] ", "[FATAL] " };

	CLogger::LogLevel g_LogLevel = CLogger::INFO;	//默认的日志等级为INFO
	int g_OutputMode = LOGGER_MODE_STDOUT;	//默认的输出模式为输出到标准输出

	void DefauleOutout(const char *pMsg, int iLen)
	{
		::fwrite(pMsg, 1, iLen, stdout);
	}

	void DefauleFlush(void)
	{
		::fflush(stdout);
	}

	CLogger::OutputFunc g_OutputFunc = DefauleOutout;	//默认的日志输出到标准输出
	CLogger::FlushFunc g_FlushFunc = DefauleFlush;

	CLogger::CLogger(CFileName SourceFile, LogLevel Level, int iLine)
		:m_spImpl(new CLogRealize(SourceFile, arrLevel[Level], iLine))
		,m_LogLevel(Level)
	{
	}

	CLogger::~CLogger()
	{
		//当调用析构的时候,日志流填充下文件名和行号,就是完整的一条日志了
		m_spImpl->FinishLog();
		g_OutputFunc(m_spImpl->GetLogStreamBuff(), m_spImpl->GetLogStreamBuffLen());
		if (m_LogLevel == FATAL)
		{
			//如果发生了FATAL错误,那么就终止程序。
			//以便之后重启程序。
			DefauleFlush();		//在此之前先冲刷缓冲区
			abort();
		}
	}

	CLogStream &CLogger::GetLogStream(void)
	{
		return m_spImpl->GetLogStream();
	}

	CLogger::LogLevel CLogger::getLogLevel(void)
	{
		return g_LogLevel;
	}

	void CLogger::setLogLevel(LogLevel level)
	{
		g_LogLevel = level;
	}

	void CLogger::setOutputMode(int iMode)
	{
		g_OutputMode = iMode;

		if (g_OutputMode == LOGGER_MODE_STDOUT)
		{
			//事实上,默认的输出就是输出到标准输出,不做处理
			return;
		}
		else if (g_OutputMode == LOGGER_MODE_LOGFILE)
		{
			//仅输出到日志文件
			setOutputFunc(CLogFile::OutputFunc);
			setFlushFunc(CLogFile::FlushFunc);
		}
		else if (g_OutputMode == (LOGGER_MODE_STDOUT | LOGGER_MODE_LOGFILE))
		{
			//输出到标准输出和log中去
			setOutputFunc(OutputOutAndLog);
			setFlushFunc(FlushAll);
		}
		else
		{
			//nothing
		}
	}

	void CLogger::setOutputFunc(OutputFunc Output)
	{
		g_OutputFunc = Output;
	}

	void CLogger::setFlushFunc(FlushFunc Flush)
	{
		g_FlushFunc = Flush;
	}

	void CLogger::OutputOutAndLog(const char *pMsg, int iLen)
	{
		DefauleOutout(pMsg, iLen);			//输出到标准输出
		CLogFile::OutputFunc(pMsg, iLen);	//输出到日志中
	}

	void CLogger::FlushAll(void)
	{
		DefauleFlush();
		CLogFile::FlushFunc();
	}
}
实现的代码到此为止。

你可能感兴趣的:(c++,Linux,window)