/* 附属于单个线程的日志缓存 基本思路:1、缓存一定数量时进行一次写,减少IO次数; 2、异步写日志,另开一专门的写线程,避免记日志线程阻塞等待于费时IO; 写条件:缓存到达一定量||到达特定时间||本对象销毁 by dzj, 09.04.14 */ #ifndef DS_LOCK_H #define DS_LOCK_H #include "dslock.h" #include #include using namespace std; const unsigned int INNER_BUF_SIZE = 4096000; //内部缓存大小,应设成足够大,防止IO线程一时没机会执行时,也有足够的空间写日志; const unsigned int LOG_BUF_BIAS = 10240; //写日志缓存大小阈值; enum ELogLev { LOG_LEV_INFO = 0, LOG_LEV_WARNING = 1, LOG_LEV_ERROR = 2, LOG_LEV_DEBUG = 3 }; extern const ELogLev g_logLev;//日志记录等级; //用户调用的记日志函数; class CDsBufLog; void DsLog( ELogLev logLevel/*日志等级*/, CDsBufLog* pLogBuf, const char *fmt, ...); #define NewLog( logLevel, ... ) DsLog( logLevel, GET_TLS_LOG(), __VA_ARGS__ ) #define IS_BUF_LOG false //是否缓存日志(立即输出日志) //执行IO任务线程(写IO线程,或读内部缓存线程); void* LogThread( void* pInParam ); class CDsBufLog { friend void* LogThread( void* pInParam ); public: CDsBufLog() : m_head(0), m_tail(0), m_isstop(false) { BufLogInit(); }; ~CDsBufLog() { StopLog(); }; public: void BufLogInit() { //启动写IO线程; #ifndef WIN32 unsigned long selfid = pthread_self(); int err = pthread_create( &m_iothread, NULL, LogThread, this ); if ( 0 != err ) { //printf( "%x:创建日志IO线程失败\n", selfid ); cout << selfid << ":创建日志IO线程失败\n" << endl; return; } else { //printf( "%x:创建日志IO线程%x成功\n", selfid, (unsigned long)m_iothread ); cout << selfid << "创建日志IO线程" << (unsigned long)m_iothread << "成功\n" << endl; } #else //WIN32 unsigned long selfid = ::GetCurrentThreadId(); unsigned long createid = 0; m_iothread = HXBCBEGINTHREADEX( NULL, 0, LogThread, this, 0, &createid ); if ( NULL == m_iothread ) { //printf( "应用线程%x:创建日志IO线程失败\n", selfid ); cout << "应用线程" << selfid << "创建日志IO线程失败\n" << endl; return; } else { //printf( "应用线程%x:创建日志IO线程%x成功\n", selfid, createid ); cout << "应用线程" << selfid << "创建日志IO线程" << createid << "成功\n" << endl; } #endif //WIN32 return; } ///停止记日志,已经缓存的日志强制输出; void StopLog() { m_isstop = true; //通知写线程将当前日志写完; m_event.SignalEvent(); #ifdef WIN32 ::WaitForSingleObject( m_iothread, INFINITE );//等待IO线程结束; unsigned long selfid = ::GetCurrentThreadId(); #else //WIN32 pthread_join( m_iothread, NULL ); unsigned long selfid = pthread_self(); #endif //WIN32 //printf( "%x:日志IO线程已退出\n", selfid ); cout << selfid << ":日志IO线程已退出\n" << endl; return; } public: ///添加日志; bool AddLog( const char* pLog ) { if ( NULL == pLog ) { return false; } if ( m_isstop ) { return false;//停止写新的日志; } if ( ! IS_BUF_LOG ) { //printf( pLog ); cout << pLog << endl; return true; } //计算剩余空间是否足够放入日志,若足够,则放入; unsigned int curhead = m_head;//当前头,该值可能会由于LogThread而改变,因此需要保存快照,而m_tail只会由写日志线程改变,因此无需保存; curhead = (curhead < INNER_BUF_SIZE) ? curhead:0; unsigned int cursize = ( curhead <= m_tail ) ? (m_tail-curhead) : ( m_tail + (INNER_BUF_SIZE-curhead) ); unsigned int availsize = INNER_BUF_SIZE - cursize - 1;//可用空间; availsize = (availsize < INNER_BUF_SIZE) ? availsize : 0;//由于head与tail之间要留一空格避免写过头,因此实际可用空间小于INNER_BUF_SIZE; unsigned int inlen = (unsigned int)strlen( pLog );//不缓存尾部'\0',以便一次性输出多个字符串; if ( inlen > availsize ) { //可用空间不足以存放输入日志; //printf( "日志缓存空间不足\n" ); cout << "日志缓存空间不足\n" << endl; return false; } //输入日志可以放下,拷贝之 unsigned int availtailsize = INNER_BUF_SIZE - m_tail;//尾部有效大小,拷贝不要越过数组尾部 if ( availtailsize >= inlen ) { //至尾部空间足够存放 memcpy( &(m_pBufqueue[m_tail]), pLog, inlen ); m_tail += inlen; m_tail = (m_tail < INNER_BUF_SIZE) ? m_tail:0; } else { //尾部空间不够存放,则一部放至尾部,一部拷至头部; memcpy( &(m_pBufqueue[m_tail]), pLog, availtailsize ); memcpy( &(m_pBufqueue[0]), pLog+availtailsize, (inlen-availtailsize) ); m_tail = (inlen-availtailsize);//从头开始数起存放的字符个数,即为尾部位置(0起,也就是下一个可写位置); } cursize += inlen;//目前缓冲的日志量; if ( cursize >= LOG_BUF_BIAS ) { //通知写线程可写; m_event.SignalEvent(); } return true; } private: unsigned int m_head; unsigned int m_tail; private: char m_pBufqueue[INNER_BUF_SIZE];//内部缓存 private: bool m_isstop;//是否停止记日志; DsEvent m_event;//内部事件; private: #ifdef WIN32 HANDLE m_iothread; #else //WIN32 pthread_t m_iothread; #endif //WIN32 }; #endif //DS_LOCK_H