最近一直在看《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个数,计算加,减,乘,除。我们用这个日志库来记录起来。
和预想的一样,当发生发生等级为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();
}
}
实现的代码到此为止。