windows系统日志中记录了系统中硬件、软件和系统问题的相关信息,同时可监控系统中发生的事件。因此当系统崩溃导致一些异常现象发生时可通过系统日志来找出错误发生的原因。那么对于应用软件来说,如果也提供类似的日志文件,帮助我们监控软件运行过程中的各种状态信息,这样当软件出现异常时也便于查找原因。而且在软件开发过程中可以结合日志解决软件调试过程中出现的各种问题,从而提高开发效率。基于此本文对vs下写日志的各种方法进行了总结。
日志中无非记录了两个主要内容:一个是事件发生的时间,一个是事件本身。而将内容写入文件的方式无非有以下几种:
1、windows 有关文件操作的API:fopen(),fwrite(),fputs(),fprintf(),fclose()等;
2、c++中的IO库:fstream,ifstream,ofstream;
3、c++中的文件操作类CFile,CStdioFile等;
4、用户自定义文件类。
我们主要采用两种方式来实现写日志功能,一个是将写日志功能通过封装到一个函数中,当需要将数据写入日志文件时就调用此函数。另一个是自己实现日志文件类,在程序中一开始就构建日志文件对象,当需要写入日志时通过调用类的成员函数实现日志写入功能。
(一)写日志函数logFile
根据前面所述,此函数实现的功能应为:将当前时间及事件信息写入日志。函数声明如下:
void logFile(char* logfileName, char* data);
参数说明:第一个参数为日志文件名称,第二个参数为写入的信息数据(如错误,警告等)。
函数实现:
FILE *fp;
SYSTEMTIME st;
GetLocalTime(&st);
fp=fopen(logfileName,"a");
if(fp==0)
{
return;
}
fprintf(fp, "%d-%d-%d %d:%d:%d :%s\r\n", st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond, str);
fclose(fp);
说明:函数中先获取系统时间,然后利用fprintf将信息格式化写入日志中。
(二)日志文件类
可基于CFile等已有的文件类编写,也可完全自己编写。本文中采用后者。
文件类声明(头文件):
#pragma once
class LogFile
{
protected:
CRITICAL_SECTION _csLock;
char * _szFileName;
HANDLE _hFile;
bool OpenFile();//打开文件, 指针到文件尾
DWORD Write(LPCVOID lpBuffer, DWORD dwLength);
virtual void WriteLog( LPCVOID lpBuffer, DWORD dwLength);//写日志, 可以扩展修改
void Lock() {::EnterCriticalSection(&_csLock); }
void Unlock() {::LeaveCriticalSection(&_csLock);}
char szFileSuffix[32]; //文件后缀
public:
LogFile(char*szFileName);
virtual ~LogFile();
const char * GetFileName()
{
return _szFileName;
}
void SetFileName(const char *szName);//修改文件名, 同时关闭上一个日志文件
bool IsOpen()
{
return _hFile != INVALID_HANDLE_VALUE;
}
void Close();
void Log(LPCVOID lpBuffer, DWORD dwLength);//追加日志内容
void Log(const char *szText){
Log(szText, strlen(szText));}
void Log(CString str);
void Log(TCHAR *buffer);
private://屏蔽函数
LogFile(const LogFile&);
LogFile&operator = (const LogFile&);
};
类实现(源文件):
#include "StdAfx.h"
#include "LogFile.h"
LogFile::LogFile(char*szFileNameParam)
{
memset(szFileSuffix,0,sizeof(szFileSuffix));
strcat_s(szFileSuffix,szFileNameParam); //文件后辍,不带日期
_szFileName = NULL;
_hFile = INVALID_HANDLE_VALUE;
::InitializeCriticalSection(&_csLock);
//求出当前日期
time_t now;
time(&now);
char szCurDate[256] = {0};
struct tm newtime;
localtime_s(&newtime,&now);
strftime(szCurDate,19,"%Y%m",&newtime);
char szFileName[64] = {0};
strcat_s(szFileName,10,szCurDate); //日期在前面
strcat_s(szFileName,szFileSuffix); //追加文件名
SetFileName(szFileName);
}
//-------------------------------------------------------------------------
LogFile::~LogFile()
{
::DeleteCriticalSection(&_csLock);
Close();
if(_szFileName)
delete [] _szFileName;
}
//-------------------------------------------------------------------------
bool LogFile::OpenFile()
{
if(IsOpen())
return true;
if(!_szFileName)
return false;
_hFile = CreateFileA(
_szFileName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(!IsOpen() && GetLastError() == 2)//打开不成功, 且因为文件不存在, 创建文件
_hFile = CreateFileA(
_szFileName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(IsOpen())
SetFilePointer(_hFile, 0, NULL, FILE_END);
return IsOpen();
}
//-------------------------------------------------------------------------
DWORD LogFile::Write(LPCVOID lpBuffer, DWORD dwLength)
{
DWORD dwWriteLength = 0;
if(IsOpen())
WriteFile(_hFile, lpBuffer, dwLength, &dwWriteLength, NULL);
return dwWriteLength;
}
//-------------------------------------------------------------------------
void LogFile::WriteLog( LPCVOID lpBuffer, DWORD dwLength)
{
DWORD dwWriteLength;
char buf[1024]={0};
strcat_s(buf,1000,(char*)lpBuffer);
if(IsOpen())
{
time_t now;
time(&now);
char temp[1024];
struct tm newtime;
localtime_s(&newtime,&now);
memset(temp,0,sizeof(temp));
strftime(temp, 64, "%Y-%m-%d %H:%M:%S :", &newtime);
strcat_s(temp,128," :");
strcat_s(temp,1024,buf);
WriteFile(_hFile, temp,strlen(temp), &dwWriteLength, NULL);
WriteFile(_hFile, "\r\n",2, &dwWriteLength, NULL);
FlushFileBuffers(_hFile); //存硬盘
}
else
{
;
}
}
//-------------------------------------------------------------------------
void LogFile::SetFileName(const char *szName)
{
//assert(szName);
if(_szFileName != NULL)
{
//AfxMessageBox(_T("_szFileName is NULL"));
delete []_szFileName;
}
Close();
int len = strlen(szName);
_szFileName = new char[32];
// assert(_szFileName);
strcpy_s(_szFileName,30,szName);
}
//-------------------------------------------------------------------------
void LogFile::Close()
{
if(IsOpen())
{
CloseHandle(_hFile);
_hFile = INVALID_HANDLE_VALUE;
}
}
//-------------------------------------------------------------------------
void LogFile::Log(LPCVOID lpBuffer, DWORD dwLength)
{
// assert(lpBuffer);
__try
{
Lock();
//算出现在应该写日志的文件名
time_t now;
time(&now);
char szCurDate[256] = {0};
struct tm newtime;
localtime_s(&newtime,&now);
strftime(szCurDate,19,"%Y%m",&newtime);
char szCurFileName[64] = {0};
strcat_s(szCurFileName,10,szCurDate); //日期在前面
strcat_s(szCurFileName,szFileSuffix); //追加文件名
if(!PathFileExistsA(szCurFileName))
SetFileName(szCurFileName);
if(OpenFile())
WriteLog(lpBuffer, dwLength);
}
__finally
{
Unlock();
}
}
void LogFile::Log(TCHAR *buffer)
{
CT2A buf_str(buffer);
Log(buf_str);
}
void LogFile::Log(CString str)
{
Log(str.GetBuffer());
}
(出处:http://www.cnblogs.com/zhangdongsheng/)
日志文件类的使用:程序初始化时构建日志文件对象,需要将某些信息写入日志时调用Log()方法实现写入。