在第一篇中主要由讨论日志的需求以及接口设计,这里阐述一下各个部分的实现过程。在设计过程中我们把整个系统分为三个部分:模块buffer, 线程buffer, 和内存日志系统接口。
首先我们需要明确的是我们的写入是正对线程来说,不同线程需要在不同的内存段进行写入操作,而线程内的写入操作是针对模块而来的。所以正在的写入操作在模块buffer里面,这里我们直接实现 一个前面设计的模块buffer:
//模块buffer template <int nMaxBuffLen = MEMLOGSYS_MAX_BUFFE_LEN> class IModuleBufferImp { public: typedef unsigned int size_t; static const unsigned int m_snMaxBuffLen = nMaxBuffLen; void WriteLog( const char* szBufferCache) //写日志 { WriteLog_Imp(GetTimeSting().c_str()); WriteLog_Imp(szBufferCache); WriteLog_Imp("\n"); } const char* GetMemLog() //获取当前模块的日志信息 { m_Buffer[m_snMaxBuffLen] = 0; return m_Buffer; } const char* GetModuleName()const { return m_szModuleName; } static IModuleBufferImp* CreateModuleBuffer(const char* pszModuleName) { IModuleBufferImp* pModuleBuffer = new IModuleBufferImp(pszModuleName); return pModuleBuffer; } static void DestroyModuleBuffer(IModuleBufferImp* pModuleBuffer) { SAFE_DELETE(pModuleBuffer); } static std::string GetTimeSting() { tm* pTimeStruct = Time::GetLOCALTime(); char szTempBuffer[128] = {0}; sprintf_s(szTempBuffer,"%.2d.%.2d.%.2d:%.2d:%.2d:%.10d ",pTimeStruct->tm_mon,pTimeStruct->tm_mday,pTimeStruct->tm_hour,pTimeStruct->tm_min,pTimeStruct->tm_sec, GetTickCount()); return std::string(szTempBuffer); } protected: explicit IModuleBufferImp(const char* pszModuleName) { if (pszModuleName) { sprintf_s(m_szModuleName, MEMLOGSYS_MAX_MODULENAME_LEN-1, "%s", pszModuleName); m_szModuleName[MEMLOGSYS_MAX_MODULENAME_LEN-1] = 0; }else { memset(m_szModuleName, 0, MEMLOGSYS_MAX_MODULENAME_LEN); sprintf_s(m_szModuleName, MEMLOGSYS_MAX_MODULENAME_LEN-1, "%s", MEMLOGSYS_DEFAULT_MODULENAME); } m_WriteOffSet = 0; memset(m_Buffer, 0, m_snMaxBuffLen); } ~IModuleBufferImp() { ; } bool WriteLog_Imp(const char* pszLog) { if (pszLog) { int len = strlen(pszLog); if (len >= m_snMaxBuffLen) //超过最大值了 { return false; } if (m_WriteOffSet + len > m_snMaxBuffLen) { memcpy(m_Buffer+m_WriteOffSet, pszLog, m_snMaxBuffLen - m_WriteOffSet); memcpy(m_Buffer,pszLog + (m_snMaxBuffLen - m_WriteOffSet), len-m_snMaxBuffLen + m_WriteOffSet); }else { memcpy(m_Buffer+m_WriteOffSet, pszLog, len); } m_WriteOffSet += len; if (m_WriteOffSet >= m_snMaxBuffLen) { m_WriteOffSet -= m_snMaxBuffLen; } return true; } return false; } private: IModuleBufferImp(const IModuleBufferImp& rstIModuleBuffer){;}; //禁用 IModuleBufferImp& operator = (const IModuleBufferImp& rstIModuleBuffer) {;};//禁用 char m_szModuleName[MEMLOGSYS_MAX_MODULENAME_LEN]; int m_WriteOffSet; char m_Buffer[m_snMaxBuffLen + 1]; };
上面我们提供了一个简单的工厂函数来创建模块buffer, 并置拷贝构造函数和赋值构造函数为私有防止恶意拷贝。
在上面的实现过程中,有几个小细节需要注意:
1。 我们是需要一个线程安全的写入类,所以在类的实现中不能出现任何多线程共享的可写字段。
2。 线程Buffer 是一个循环日志,必须保证最后记录的完整性,以及记录的循环可读性,
必须在写入记录的时候保证字符串结尾的0不屏蔽,数组中的有效内容。也就是在Write_Imp()里面用的是memcpy, 当然也可以使用 sprintf 系列,
(读者也可以自行改为使用sprintf系列,如果使用sprintf系列需要仔细阅读相关文档, 比如 sprintf_s 保证最后给你加一个 0, 超过缓冲区会报错获设置错误标志位 : 这里有一个小tips,sprintf_s ,不仅在你的缓冲区里面输入内容,而且在后面加一个0,不仅加一个0,还会对0后面的有效缓冲区拿来做其他用途)
3。 为了保证不破坏任何有效的完整记录,以及循环可读性,申请的缓冲区必须比可写缓冲区多一个字节用来存字符串结束符.
4。 把构造函数定义为保护,提供一个简单工厂函数,这里把缓冲区定义为一个数组,而不是一个动态申请的空间,然后,这个时候工厂函数全部是new出的模块buff,所有你定义任何大小的缓冲区,只要不操作当前系统最大可申请内存数量就不会出现错误。
Sign Clown 2010.7.6 0:59 HDPY
大概实现就是上面了。后面另外两个部分,线程buff,系统管理接口的具体实现,会陆续展示。
[本文原创,转载请注明出处,在文章末尾提供原文链接http://www.cnblogs.com/JefferyZhou/,否则一旦发现,将按字节每人民币收费,绝不论价]