在游戏程序中日志显得非常重要,例如在调试AI的时候需要连续跟踪AI状态的变化,在非正常的函数返回点记录状态信息等等。但是这一系列的log如果没有统一管理那么将变得很混乱,而且在多线程的情况下有可能会出现同步问题。为此专门用单件的模式制作了一个日志类,希望能派的上用场。不多说了,直接贴代码,原理很简单,关键地方有注释。
log.h
#include <windows.h>
#include <fstream>
class CSynObj
{
public:
CSynObj()
{
InitializeCriticalSection(&m_cs);
}
~CSynObj()
{
DeleteCriticalSection(&m_cs);
}
void Lock()
{
EnterCriticalSection(&m_cs);
}
void UnLock()
{
LeaveCriticalSection(&m_cs);
}
private:
CRITICAL_SECTION m_cs;
};
class CLock
{
public:
CLock(CSynObj& synchobject)
:refSynchObject(synchobject)
{
refSynchObject.Lock();
}
virtual ~CLock()
{
refSynchObject.UnLock();
}
protected:
CSynObj& refSynchObject;
};
enum LOG_TARGET_E
{
LOG_TARGET_SCREEN = 1,
LOG_TARGET_FILE = 2,
};
class CLog
{
private:
~CLog();
CLog();
CLog(const CLog& rhs) {}
CLog& operator = (const CLog& rhs) {}
static CLog* m_pLog;
static CSynObj m_Lock;
std::ofstream m_OutFile;
void GetSysTime(SYSTEMTIME* lpSysTime);
void FormatAllTime(const SYSTEMTIME& refSysTime, std::string& strOut);
public:
void Write(char* pMsg, int nTarget);
static CLog* GetInstance();
// 摧毁singleton的入口
static void DestroyInstance();
};
__inline void DEBUG_MSG(LPSTR filename, int lineno, LPSTR linedesc, DWORD dwErrCode, int nOutPutTarget = LOG_TARGET_SCREEN|LOG_TARGET_FILE)
{
char cTemp[256];
sprintf( cTemp, "%lu error on %d line in %s file: %s", dwErrCode, lineno, filename, linedesc );
CLog* pLog = CLog::GetInstance();
pLog->Write( cTemp, nOutPutTarget );
}
log.cpp
#include <iostream>
#include <log.h>
CLog* CLog::m_pLog = NULL;
CSynObj CLog::m_Lock;
CLog::~CLog()
{
m_OutFile.close();
}
CLog::CLog()
{
m_OutFile.open( "Log.txt", std::ios::app );
atexit( CLog::DestroyInstance );
}
void CLog::GetSysTime(SYSTEMTIME* lpSysTime)
{
//获得系统时间
FILETIME CurFileTime;
::GetSystemTimeAsFileTime(&CurFileTime);
::FileTimeToLocalFileTime(&CurFileTime, &CurFileTime);
::FileTimeToSystemTime(&CurFileTime, lpSysTime);
}
void CLog::FormatAllTime(const SYSTEMTIME& refSysTime, std::string& strOut)
{
//系统时间转换成字符串
char cBuffer[MAX_PATH*2];
sprintf(cBuffer,"%d-%02d-%02d %02d:%02d:%02d",
refSysTime.wYear, refSysTime.wMonth, refSysTime.wDay , refSysTime.wHour , refSysTime.wMinute , refSysTime.wSecond);
strOut = cBuffer;
}
void CLog::Write(char* pMsg, int nTarget)
{
// 区间锁,在同一时刻只允许一个线程进行写文件或屏幕
CLock Lock( CLog::m_Lock );
SYSTEMTIME Systime;
std::string strTime;
GetSysTime( &Systime );
FormatAllTime( Systime, strTime );
if( nTarget & LOG_TARGET_FILE )
m_OutFile << strTime.c_str() << ": " << pMsg << std::endl;
if( nTarget & LOG_TARGET_SCREEN )
std::cout << pMsg << std::endl;
}
CLog* CLog::GetInstance()
{
// 双次检测的好处
// 1:防止多线程同时进入new CLog;
// 2:临界区写在第二次判断,提高了效率
if( !m_pLog )
{
CLock Lock( CLog::m_Lock );
if( !m_pLog )
m_pLog = new CLog;
}
return m_pLog;
}
// 与GetInstance对应,给CLog一个delete的机会
void CLog::DestroyInstance()
{
delete m_pLog;
m_pLog = NULL;
}
int main()
{
atexit( CLog::DestroyInstance );
DEBUG_MSG( __FILE__, __LINE__, "main()", 0 );
getchar();
return 0;
}