在游戏程序中日志显得非常重要,例如在调试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 );
}
#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 ;
}
#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 ;
}