之前用的日志记录是阻塞式的,会影响主程序的运行。于是我决定自己撸一个用消息机制来记录日志的日志类。
既然是要用消息机制,肯定要有一个消息队列,还要有一个线程来不断接收消息,写日志。另外还需要使用单例类来管理日志的记录
首先来看主体代码,Run:
int CLogByMsg::Run(){
int iSize;
while (m_bExist){
Sleep(10);
iSize = m_QLogMsg.size();
if (m_QLogMsg.empty()){
continue;
}
CLogParam logMsg = m_QLogMsg.front();
m_QLogMsg.pop();
fprintf(m_pLogFile, "[%s][%s-%d] [%04d-%02d-%02d %02d:%02d:%02d.%d]\t\t%s \n",
logMsg.strFunction.c_str(),
logMsg.strFile.c_str(),
logMsg.lLine,
logMsg.sysTime.wYear,
logMsg.sysTime.wMonth,
logMsg.sysTime.wDay,
logMsg.sysTime.wHour,
logMsg.sysTime.wMinute,
logMsg.sysTime.wSecond,
logMsg.sysTime.wMilliseconds,
logMsg.strLog.c_str());
m_lCnt++;
if (m_lCnt % 20 == 0){
fclose(m_pLogFile);
m_pLogFile = NULL;
Sleep(100);
string strLogWithTime;
GetLogFileNameWithTime(m_strLogFilePath, strLogWithTime);
m_pLogFile = fopen(strLogWithTime.c_str(), "a+");
}
}
return eSucess;
}
代码很简单,就循环判断消息队列是否为空,如果不为空则往文件里写日志。
如果日志记录已经超过了20条,则将文件关闭一次,然后重新打开。之所以要这样是因为fclose文件后,缓存中的数据才会真正写到硬盘中,这样在程序运行期时,我们仍然看到日志。
那如何往队列里添加消息呢?用这两个方法:
CLogByMsg::eResult CLogByMsg::WriteLog(const char* lpFile, const char * lpFunction ,long lLine, const char* lpLog){
SYSTEMTIME sys;
GetLocalTime(&sys);
SendLog(sys, lpFile, lpFunction, lLine, lpLog);
return eSucess;
}
int CLogByMsg::SendLog(SYSTEMTIME sys, const char* lpFile, const char * lpFunction, long lLine, const char* lpLog){
EnterCriticalSection(&g_QueueECS);
m_QLogMsg.push(CLogParam(sys,lpLog, lpFile, lpFunction, lLine));
LeaveCriticalSection(&g_QueueECS);
return eSucess;
}
WriteLog是公开的方法,而SendLog是私有方法。SendLog则是往消息队列中添加CLogParam类型的消息。因为日志常常会在线程中使用,所以还要为消息队列加锁
CLogParam定义如下 :
class CLogParam
{
public:
CLogParam();
CLogParam(SYSTEMTIME sys,const char * pcLog, const char * pcFile, const char * pcFunction, long lLine);
~CLogParam();
//系统时间
SYSTEMTIME sysTime;
// 日志信息
string strLog;
// 日志记录时所处的文件位置
string strFile;
// 日志记录时所处的函数
string strFunction;
// 日志记录时,所处代码中的行号
long lLine;
};
包括记录日志的时间,文件位置 ,函数等。
最后是调用。在这里我们用宏来完成日志的初始化和记录,原因我们后面讲。
首先是日志类的初始化,要调用:
#define LOG_CREATE(pcPath) CLogByMsg::GetInstance()->Create(pcPath)
pcPath就是日志要存储的位置。而Create文件要做的事也很简单,将pcPath文件打开
释放时调用
#define LOG_RELEASE() CLogByMsg::GetInstance()->Release();
LOG_RELEASE会把日志文件关闭
如果要记录日志就调用
#define LOG_INFO(pcLog) CLogByMsg::GetInstance()->WriteLog(__FILE__, __FUNCTION__, __LINE__, pcLog)
利用LOG_INFO来记录,__FILE__, __FUNCTION__, __LINE__,分别表示该行代码所在的文件,所属于的函数,和该代码在本代码页中的行数。这些数据将会在编译时将这些数据作为char*类型参数输入到WriteLog中。
而利用LOG_INFO宏就避免了每次调用WriteLog时,都要输入__FILE__, __FUNCTION__, __LINE__这几个参数,只要这样使用就行了。
LOG_INFO("begin connect");
该日志类还可以写成dll,直接被调用。
具体可以看我的工程:基于消息机制的日志类
在里面已经直接做成了dll,使用时,只要有LogByMsg.dll和LogByMsg.lib就行了。