基于Mailslot技术的日志服务之Client端实现:单子模式,缓存, 支持在动态链接库中输出Log打印信息

来源:互联网 发布:疯狂美工助手打不开 编辑:程序博客网 时间:2024/05/22 23:55
前一篇讲述了基于Mailslot技术的Log Server的简单实现,可以用CreateFile输出Log打印。然而,处处以CreateFile输出打印不是一个好的办法,这里讲述一个模块化的实现,其原理是定义一个Client类,需要输出打印的地方访问这个类的实例来输出Log打印。

首先,我们来实现这个Client类,头文件如下:


namespace giraffe
{


class GIRAFFE_LOG_CLIENT_DLLEXPORT CGiraffeLogClient
{
public:
CGiraffeLogClient(const char * pszSubrootName = NULL);
~CGiraffeLogClient(void);


private:
unsigned long m_ulLogsCount;
std::list<std::string> m_listWriting;
std::list<std::string> m_listCaching;


public:
int Output(const char * s);
int Output(std::string & s);
static DWORD  __stdcall WritingProc(void * param);


private:
HANDLE m_hSendMailSlot;
CRITICAL_SECTION m_csCaching;


HANDLE m_hWritingThread;
DWORD m_dwWritingThreadID;
int InitMailslot(void);
int CloseMailslot(void);
std::string m_strRootName;
std::string m_chrIndexAfter;
std::string m_chrDateAfter;


public:
int Writing(void);
HANDLE & GetWaitingHandle(void) { return m_hWaitingEvent; };
int SetPrefixFormat(std::string & format);
private:
HANDLE  m_hWaitingEvent;
};

int GIRAFFE_LOG_CLIENT_DLLEXPORT OutputLogString (const char  * );
int GIRAFFE_LOG_CLIENT_DLLEXPORT OutputLogString (std::string & );
int GIRAFFE_LOG_CLIENT_DLLEXPORT SetPrefixFormat (std::string & );

}


实现cpp文件。代码如下:


#include "singleton.h"

using namespace giraffe;

//构造函数
CGiraffeLogClient::CGiraffeLogClient(const char * pszSubrootName)
: m_hSendMailSlot(INVALID_HANDLE_VALUE)
, m_ulLogsCount(0)
, m_hWritingThread(NULL)
, m_dwWritingThreadID(0)
, m_hWaitingEvent(NULL)
{
m_strRootName = GIRAFFE_LOG_SERVER_NAME;

if (pszSubrootName && *pszSubrootName) {
// 参数化mailslot文件名称
pszSubrootName = pszSubrootName + strspn(pszSubrootName, " \t,;\\/");
if (pszSubrootName && *pszSubrootName) {
std::string subroot = pszSubrootName;
if (subroot.find_first_of("!^$| \t\n\r,;\"'()[]{}/\\?") != std::string::npos) {
subroot.erase(subroot.find_first_of("!^$| \t\n\r,;\"'()[]{}/\\?") + 0);
}
m_strRootName = "\\\\.\\mailslot\\";
m_strRootName += subroot;
}
} else {
// 从环境变量获取mailslot文件名称
char buffer[256] = {0};
GetEnvironmentVariable("girafferoot", buffer, sizeof(buffer) - 1);
if (buffer[0]) {
m_strRootName = "\\\\.\\mailslot\\";
m_strRootName += buffer;
}
}


// 初始化为单子实例
Singleton<CGiraffeLogClient*>::instance();
Singleton<CGiraffeLogClient*>::instance() = NULL;
Singleton<CGiraffeLogClient*>::instance() = this;


::InitializeCriticalSection(&m_csCaching);


m_chrIndexAfter = ",";
m_chrDateAfter  = ".";


// 创建通知写log的事件
m_hWaitingEvent = CreateEvent( 
NULL,               // default security attributes
TRUE,               // manual-reset event
FALSE,              // initial state is nonsignaled
TEXT("WaitingWriteEvent")  // object name
); 


if (m_hWaitingEvent == NULL) 

printf("CreateEvent failed (%d)\n", GetLastError());
}


m_dwWritingThreadID = 0;
m_hWritingThread = CreateThread(NULL, 0, CGiraffeLogClient::WritingProc, this, CREATE_SUSPENDED, &m_dwWritingThreadID);
ASSERT(m_hWritingThread);


if (m_hWritingThread != NULL)
{
Singleton<CGiraffeLogClient*>::instance() = this;
ResumeThread(m_hWritingThread);
}
}


CGiraffeLogClient::~CGiraffeLogClient(void)
{
Singleton<CGiraffeLogClient*>::instance() = NULL;


m_dwWritingThreadID = 0;


if (NULL != m_hWritingThread) {
BOOL bRet = CloseHandle(m_hWritingThread);
m_hWritingThread = NULL;
}


::DeleteCriticalSection(&m_csCaching);


if (INVALID_HANDLE_VALUE != m_hSendMailSlot) {
CloseHandle(m_hSendMailSlot);
}
if (NULL != m_hWaitingEvent) {
CloseHandle(m_hWaitingEvent);
}
}


//像Android的logcat一样格式化输出
int CGiraffeLogClient::Output(const char * s)
{
CTime nowtime;
nowtime = CTime::GetCurrentTime();
if (s) {
std::string log;
char buff[80] = {0};
if (m_ulLogsCount < 0x10000) {
sprintf_s(buff, 80, "[%04lx%s%02d%02d%s%02d%02d%02d] ",
m_ulLogsCount++, 
m_chrIndexAfter.c_str(),
/*nowtime.GetYear(),*/ nowtime.GetMonth(), nowtime.GetDay(),
m_chrDateAfter.c_str(),
nowtime.GetHour(), nowtime.GetMinute(),nowtime.GetSecond());
} else {
sprintf_s(buff, 80, "[%lx%s%02d%02d%s%02d%02d%02d] ",
m_ulLogsCount++, 
m_chrIndexAfter.c_str(),
/*nowtime.GetYear(),*/ nowtime.GetMonth(), nowtime.GetDay(),
m_chrDateAfter.c_str(),
nowtime.GetHour(), nowtime.GetMinute(),nowtime.GetSecond());


}
log = buff;
log+= s;


//DBTIMESTAMP t;
//nowtime.GetAsDBTIMESTAMP(t);


::EnterCriticalSection(&m_csCaching);
m_listCaching.push_back(log);
if (NULL != m_hWaitingEvent) {
SetEvent(m_hWaitingEvent);
}
::LeaveCriticalSection(&m_csCaching);
}
return 0;
}


int CGiraffeLogClient::Output(std::string & s)
{
return Output(s.c_str());
}




DWORD  __stdcall CGiraffeLogClient::WritingProc(void * param)
{
CGiraffeLogClient * mgr = (CGiraffeLogClient *) param;


ASSERT(mgr);


//先停100ms
Sleep(100);


#if(1)
std::string strTitle = _T("[mailslot-log]: ");


DWORD tid = ::GetCurrentThreadId(); //GetThreadId();
if (tid) {
char temp[80] = {0};
sprintf_s(temp, 80, "(0x%04lx) \t[mailslot-log]: ", tid);
strTitle = temp;
} else {
strTitle = _T("[mailslot-log]: ");
}
std::string str;
OutputDebugString((str = strTitle + ",\t --- start ---\r\n").c_str());
#endif
try
{
while (Singleton<CGiraffeLogClient*>::instance())
{
if (0 == mgr->InitMailslot())
{
HANDLE hWaitingEvent = mgr->GetWaitingHandle();
DWORD   dwWaitResult;
if (NULL != hWaitingEvent)
{
dwWaitResult = WaitForSingleObject( 
hWaitingEvent, // event handle
5000);


switch (dwWaitResult) 
{
// Event object was signaled
case WAIT_OBJECT_0: 
ResetEvent(hWaitingEvent);
mgr->Writing();
break; 
case WAIT_TIMEOUT:
break;
// An error occurred
default: 
break; 
}
}
else
{
//mgr->Writing();
Sleep(80);
}
}
else
{
Sleep(800);
}
}
}
catch(.../*CException ex*/)
{
#if(1)
OutputDebugString((str = strTitle + ",\t --- error ---\r\n").c_str());
#endif
return 0;
}


#if(1)
OutputDebugString((str = strTitle + ",\t --- finished ---\r\n").c_str());
#endif
return 0;
}


int CGiraffeLogClient::InitMailslot(void)
{
if (INVALID_HANDLE_VALUE == m_hSendMailSlot) {
//打开由服务端创建的邮件槽
m_hSendMailSlot = CreateFile(m_strRootName.c_str() /*GIRAFFE_LOG_SERVER_NAME*/, 
GENERIC_WRITE, FILE_SHARE_READ, NULL, 
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(INVALID_HANDLE_VALUE == m_hSendMailSlot)
{
OutputDebugString("Open mailslot failed ...\n");
return -1;
}
}
return 0;
}


int CGiraffeLogClient::CloseMailslot(void)
{
if (INVALID_HANDLE_VALUE != m_hSendMailSlot) {
CloseHandle(m_hSendMailSlot);
m_hSendMailSlot = INVALID_HANDLE_VALUE;
}
return 0;
}


int CGiraffeLogClient::Writing(void)
{
std::list<std::string>::iterator iter;


for (iter = m_listWriting.begin(); iter != m_listWriting.end(); iter++) {
std::string & log = *iter;
DWORD  dwWrite = 0;
//通过邮件槽向服务端发送数据
if(!WriteFile(m_hSendMailSlot, log.c_str(), log.size(), &dwWrite, NULL))
{
OutputDebugString("Failed to write data to mailslot ...");
CloseMailslot();
// Delete the nodes which have writen.
m_listWriting.erase(iter, m_listWriting.end());
break;
}
}


if (iter == m_listWriting.end()) {
// Clear writing list (Delete the nodes which have writen).
m_listWriting.erase(m_listWriting.begin(), m_listWriting.end());
}




::EnterCriticalSection(&m_csCaching);


// Copy the strings of caching-list to writing-list
for (iter = m_listCaching.begin(); iter != m_listCaching.end(); iter++) {
m_listWriting.push_back(*iter);
}


// Clear caching-list
m_listCaching.erase(m_listCaching.begin(), m_listCaching.end());


::LeaveCriticalSection(&m_csCaching);


return 0;
}


int CGiraffeLogClient::SetPrefixFormat(std::string & format)
{
char temp[8] = {0};
const char * p = format.c_str();


p = strpbrk(p, "0123456789");
if (NULL == p) {
return -1;
}
p = p + strspn(p, "0123456789");
if ('\0' == *p) {
return -1;
}
temp[0] = *p;
m_chrIndexAfter = temp;


p = strpbrk(p, "0123456789");
if (NULL == p) {
return -1;
}
p = p + strspn(p, "0123456789");
if ('\0' == *p) {
return -1;
}
temp[0] = *p;
m_chrDateAfter = temp;


return 0;
}


//////////////////////////////////////////////////////////////////////////


int giraffe::OutputLogString (const char * s )
{
CGiraffeLogClient* & mgr = Singleton<CGiraffeLogClient*>::instance();
if (NULL != mgr) {
mgr->Output(s);
} else {
OutputDebugString(s);
}
return 0;
}


int giraffe::OutputLogString (std::string & s)
{
CGiraffeLogClient* & mgr = Singleton<CGiraffeLogClient*>::instance();
if (NULL != mgr) {
mgr->Output(s);
} else {
OutputDebugString(s.c_str());
}
return 0;
}


int giraffe::SetPrefixFormat (std::string & s)
{
CGiraffeLogClient* & mgr = Singleton<CGiraffeLogClient*>::instance();
if (NULL != mgr) {
mgr->SetPrefixFormat(s);
}
return 0;
}


显然,在需要打印的地方处处生成这个Client类的实例不是合理的行为,应该为这个Client类生成一个单子实例,在需要打印的地方调用这个实例输出打印即可。以上代码中,我们引入了boost的Singleton模板来实现单子模式(其实Android的单子设计模式的代码也不错,但是我更喜欢boost的一些)。这样,当需要调用Client类的实例时,只要简单地
CGiraffeLogClient* & mgr = Singleton<CGiraffeLogClient*>::instance();
取得指针的引用即可。


这里,OutputDebugString函数是声明为_declspec(dllexport)的,即是DLL的输出函数。请注意OutputDebugString函数的实现方式,函数的开头,它首先调用CGiraffeLogClient* & mgr = Singleton<CGiraffeLogClient*>::instance()获取Client的单子实例,然后输出传入的打印参数。也就是说,无论调用DLL的是exe程式,抑或是其他的DLL库,它们使用同一个Client实例输出Log!这样,相当完美地解决了在DLL中、exe程式中输出log打印的问题。


前一篇:  基于Mailslot技术的日志服务程序的实现,

下一篇:  Windows平台中用WaitForSingleObject API,采用阻塞模型侦听标准文件输入事件

代码:

Server, Client, Dialog和DLL测试程式代码的链接:http://download.csdn.net/detail/hylaking/8010205





0 0