一个多线程的日志记录DLL
作者:
日志记录对于应用程序来说是很重要的。本文就简单实现的实现了这样一个模块。该模块实现对程序预期的信息进行记录的功能。该模块为每一个向它进行注册的模块开启一个线程并同时创建或者打开一个同名但扩展名为.log的文本文件,此文件位于程序可执行文件目录下的/Log子目录下。这样也具有了一定的灵活性,比如可以再Debug版本中用此库输出调试信息到文件,而在Release版本中真正用于记录日志信息。
下面对其中的两个类的接口介绍一下:
CInfoReport:
该类在实现时已经用AFX_EXT_CLASS关键字指明,是一个DLL导出类,继承自CObject。
数据成员:
// 存放线程的指针链表,所有对象公用一份,故声明为static
static CPtrList m_lstpThreads;
成员函数:
// 注册模块
DWORD Register(LPCTSTR ModuleNameToReg);
// 写入信息,其中的第一个参数MoudleID必须是Register函数的返回值,
// 如果为NULL则将信息记录到FatalErr.log文件中。
void WriteInfo(DWORD MoudleID, CString InfoDescribe);
// 写入信息,其中的第一个参数MoudleID必须是Register函数的返回值,
// 如果为NULL则将信息记录到FatalErr.log文件中。
void WriteInfo(DWORD MoudleID, CString InfoFrom, CString InfoDescribe);
CWriteInfo:
该类继承自CWinThread,用于为每一个已注册的模块开启一个线程并且维护一个扩展名为log的日志文件。因为可能有多个线程同时进行同一文件操作,故此类还提供了互斥机制来确保文件I/O无冲突的进行。
数据成员:
// 注册的模块名,日志文件与它同名但扩展名不同
CString m_strModuleName;
CFile m_file; // 日志文件对象
// 为实现文件互斥操作的事件句柄
HANDLE m_hEventBusy;
成员函数:
// GetName()和SetModuleFileName()对私有成员m_strModuleName进行存取。
CString GetName();
void SetModuleFileName(CString strFileName);
消息处理函数:
// 处理由CInfoReport::WriteInfo()发来的消息TM_WRITE_INFO
afx_msg void OnWriteInfo(WPARAM wParam, LPARAM lParam);
注:未尽事项请参考源代码,包括库本身以及一个测试程序InfoReportTest。简单起见,测试程序(一个文档/视的SDI程序)的视图类的OnDraw()函数里调用了该库的日志记录函数。
程序需要完善的地方
该程序在匆忙之间完成,还有问题没有解决:
1. 安全性检查,比如可执行文件的目录下没有/Log子目录,程序将产生异常。
2. 该程序的导出类在EXE文件中被作为全局变量没有问题,而在DLL中作为全局变量应用则会导致没有反应,跟踪发现是由于创建新线程时在::WaitForSingleObject()处僵死,作为局部变量(类的成员变量使用则没有问题)。
欢迎朋友们共同讨论编程问题,我的邮件地址:qneu@yahoo.com.cn
--------------------next---------------------
不明白,在注册程序运行实例的时候已经得到CWriteInfo的指针,为什么还要用线程列表存,然后又取。 ( Rambo 发表于 2006-12-29 19:26:00)
顺便说一句。。
程序的注释太多了。。其实一个好的程序不一定要很多注释。一个好的程序应该是没有注释也能让人看懂。不知道你有没有看过《C++程序设计陷阱》这本书?里面有介绍。呵呵。 ( mybios 发表于 2004-11-7 0:40:00)
其中这个会导致内存泄漏。我已经改好,如下:
void CWriteInfo::OnWriteInfo(WPARAM wParam, LPARAM lParam)
{
char* pChar = (char*)wParam;
// 回车换行符
char changeLine[2] = {'\r','\n'};
// ::AfxMessageBox(strInfo);
// 防止文件操作冲突
if(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEventBusy,360))
{
CTime time = CTime::GetCurrentTime();
CString strTime = time.Format("%Y-%m-%d %H:%M:%S");
::ResetEvent(m_hEventBusy); // 进“临界区”
// 写入时间信息
m_file.Write(strTime, strTime.GetLength());
m_file.Write(changeLine,2);
m_file.Write(pChar,lParam);
// 在此处释放由发送消息TM_WRITE_INFO的函数new出的内存
m_file.Write(changeLine,2);
m_file.SeekToEnd();
::SetEvent(m_hEventBusy); // 出“临界区”
}
else
{
#ifdef _DEBUG
::AfxMessageBox("File Operation Ignored!");
#endif
}
delete[] pChar;//这句防止了内存泄漏!原来的代码中,如果访问冲突,就没有释放pChar的内存。
}
( mybios 发表于 2004-11-7 0:38:00)
思想好,就是测试程序没有体现你的思想,好象在测试一个普通的动态库一样 ( shakesky 发表于 2004-5-27 17:13:00)
注意:存在内存泄漏。 ( bestblue 发表于 2004-2-2 14:32:00)
发现你的dll是加入静态lib连接,并且加入.h文件来使用的。
有个问题,怎么用loadlibrary来使用?希望别见笑
( feifei2001 发表于 2003-12-3 10:17:00)
不错,编程风格好,写的很严谨、规范,C++功底可见一斑。 ( gzg 发表于 2003-11-28 17:08:00)
我想的是一进入系统就开始读数据库,进度条就会显示其读写情况.读完后进度条显示完成,然后
其数据就添加在LIST控件中.如果没有进度条显示其进度要读7万条的记录程序就好象死机
能否给个例子!
我做个例子不成功,请教!!
void CNEWADODlg::OnBUTTONProgressCtrl()
{
int ii;
int BB;
BB=m_pRecordset->RecordCount;
m_ProgressCtrl.SetRange(0,BB);
for(ii=0;ii<=BB;ii++)
{
m_ProgressCtrl.SetPos(ii);
}
}
( hjw21 发表于 2003-11-27 16:39:00)
检查log目录是否存在:
access("./log");
如果不存在大的话创建它:
_mkdir("./log"); ( darkay 发表于 2003-6-22 19:49:00)
.......................................................
--------------------next---------------------