VC++获取文件的修改时间,定期清理若干天之前的日志文件(附源码)

目录

1、日志文件生成控制策略

2、日志文件清理机制说明

3、清理日志的代码实现

3.1 获取单个文件的修改时间

3.2 遍历文件夹中所有的日志文件,清理若干天之前的文件


       为了方便排查软件运行过程中的问题,很多软件在运行过程中会将运行日志写到指定位置的日志文件中,日志记录已经成为大多数软件的一个标配。随着软件的长时间持续运行,日志文件会越来越多,会占用展会用越来越多的磁盘空间。一般情况下,我们只需要出问题时间点附近的日志,若干天前的运行日志可能就不再需要了。这就需要在软件中增加一个日志清理机制,定时去清除一些老旧的日志文件。本文就来详细地讲述一种清理日志文件的思路和方法。

1、日志文件生成控制策略

       一般情况下,不能将所有生成的日志都写到一个文件中,如果将每天生成的日志都写到同一个文件中,文件会变得越来越大,越来越臃肿,以至于打开文件时都需要很长时间。所以我们在生成日志时,都会做一些控制策略,在需要的时候切换到新的文件去写(切文件)

       切文件的好处,一是每个文件不会太大,二是方便老旧日志文件的清理(我们可以清理指定时间之前的所有日志文件)。

       比如为了方便查看,有的软件每天都会按照当天的日期重新生成一个日志文件,文件的名称中会标记上当天的日期,如下:

VC++获取文件的修改时间,定期清理若干天之前的日志文件(附源码)_第1张图片

这个出问题时,我们就可以查看对应日期的日志去分析就好了。

       再比如有的软件或者模块,他们的日志控制机制是不同的。比如他们在生成日志时,日志文件名称并不会带上日期,他们会控制每个日志文件的大小当文件达到上限值(比如50MB)时,会自动切换文件,如下所示:

VC++获取文件的修改时间,定期清理若干天之前的日志文件(附源码)_第2张图片

虽然文件名称中没有日期,但文件中的每条日志都有带上年月日时分秒的时间戳,甚至精确到毫秒。出问题时,我们通过出问题的时间点,到这些文件中找到对应时间点的文件,去查看去分析就可以了。

2、日志文件清理机制说明

       随着程序的持续运行,日志文件会越来越多,我们要及时地将不要的老旧日志给清理掉,将占用的磁盘给及时地释放掉。此处的清理机制是建立在日志文件自动切换到(自动切换到新的文件中)的基础上的。

       我们可以遍历日志路径下的所有文件,获取这些文件的最后修改时间,与当前时间点做比较,将若干天之前的日志文件都删除掉,只保留最近若干天的日志。当然,这也要求我们出问题时要及时地去拿日志文件,时间拖久了,日志可能就被删除了。

      我们可以在软件启动时开启一个新的线程,去做日志的检测和清理工作,完成后就退出线程。那对于电脑一直没关机、软件一直运行的场景呢?我们可以开启一个定时,如果持续运行了一段时间后,自动开启新的线程去做日志检测与清理工作。

3、清理日志的代码实现

3.1 获取单个文件的修改时间

      我们先调用系统API函数FindFirstFile获取日志文件的最后修改时间,然后调用API函数FileTimeToLocalFileTime将修改时间转换成本地文件时间,接着再调用FileTimeToSystemTime函数将时间转成SYSTEMTIME结构体时间,然后再转成64位整型时间。相关代码如下所示:

// 获取日志文件的最后修改时间
// 参数:strFilePath[in], sysTime[out]
time_t GetFileModifyTime( LPCTSTR strFilePath )
{
	SYSTEMTIME sysTime;
	HANDLE hFile = INVALID_HANDLE_VALUE;
	FILETIME localFileTime;

	WIN32_FIND_DATA wfd;
	memset( &wfd, 0, sizeof(wfd) );

	hFile = FindFirstFile( strFilePath, &wfd );
	if ( hFile == INVALID_HANDLE_VALUE )
	{
		return 0;
	}

	BOOL bRet = FileTimeToLocalFileTime( &wfd.ftLastWriteTime, &localFileTime );
	if ( !bRet )
	{
		return 0;
	}

	memset( &sysTime, 0, sizeof(sysTime) );
	bRet = FileTimeToSystemTime( &localFileTime, &sysTime );
	if ( !bRet )
	{
		return 0;
	}

	FindClose( hFile );

	// 老版本对tm结构体变量的初始化是不科学的,根据之前对本地时间和UTC时间及时区的研究,
	struct tm tmFileTime;
	memset( &tmFileTime, 0xFF, sizeof(tmFileTime) );
	tmFileTime.tm_year = sysTime.wYear - 1900;
	tmFileTime.tm_mon = sysTime.wMonth - 1;
	tmFileTime.tm_mday = sysTime.wDay;
	tmFileTime.tm_hour = sysTime.wHour;
	tmFileTime.tm_min = sysTime.wMinute;
	tmFileTime.tm_sec = sysTime.wSecond;
	return mktime( &tmFileTime );
}

3.2 遍历文件夹中所有的日志文件,清理若干天之前的文件

      假定我们要清理7天前的日志文件,时间差值定为:

#define LOG_DELETE_INTERVAL_SECOND  (7*24*60*60)  // 删除7天之前的日志文件

      我们去遍历文件夹中的所有日志文件(我们假定该文件夹中存放的都是日志文件,没有其他类型的文件),获取每个日志文件的最后修改时间,然后和当前的时间(调用time函数获取)做差值,如果大于差值宏LOG_DELETE_INTERVAL_SECOND的值,就将之删除掉。相关代码如下:

void DeleteLogFile( LPCTSTR strLogPath )
{
	if ( !PathFileExists( strLogPath ) )
	{
		return;
	}

	time_t tCurTime = time( NULL ); // 获取当前时间

	tstring strFindFileName = strLogPath;
	strFindFileName += _T("\\*.*");
	WIN32_FIND_DATA wfd;
	HANDLE hFindFile = FindFirstFile( strFindFileName.c_str(), &wfd ); 
	if ( hFindFile == INVALID_HANDLE_VALUE )
	{ 
		return;
	}

	while ( true )
	{	
		if ( wfd.cFileName[0] != _T('.') )
		{// 非本级或上级目录				
			if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) // 目录
			{
				if ( !FindNextFile( hFindFile, &wfd ) )
				{
					break;
				}
				continue;
			}
			else if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) // 系统文件,不处理
			{
				if ( !FindNextFile( hFindFile, &wfd ) )
				{
					break;
				}
				continue;
			}
			else // 用户日志文件
			{
				tstring strLogFile = strLogPath;
				strLogFile += _T("\\");
				strLogFile += wfd.cFileName;

			    time_t tModifyTime = GetFileModifyTime( strLogFile.c_str() );

				// 如果是指定时间之前的文件,则将之删除掉(拿当前时间和文件的最后修改时间作比较)
				if ( tCurTime - tModifyTime > DELETE_INTERVAL_SECOND )
				{
					DeleteFile( strLogFile.c_str() );
				}
			}
		}

		if ( !FindNextFile( hFindFile, &wfd ) )
		{
			break;
		}
	};

	FindClose( hFindFile );
}

如果文件夹中包含有子文件夹,则需要修改上面的代码,改成递归遍历文件夹的逻辑即可。 

你可能感兴趣的:(VC++常用功能代码封装,日志文件,最后修改时间,日志清理,FileTime,SystemTime)