在使用日志功能时,选择了目前比较常见的log4cxx,配置过程完全按照部分网上的经验进行配置。后续在使用过程中发现,配置的maxBackUpIndex属性并未生效,日志文件的数量还是在不停的增加。
使用的日志生成模式是DailyRollingFileAppender。
解决此问题时,摒弃了网上的经验,通过阅读源码发现。
log4cxx 日志新增模式有两种:
一种是按照文件大小生成:RollingFileAppender,此模式下,日志按照文件大小新增,日志文件大小在达到设定的阈值后会进行回滚,生成新文件,并且maxBackUpIndex属性正常生效,即日志文件数量达到设定的阈值后会自动进行删除,使日志文件始终保存在设定的阈值范围之内。
另一种是按照日期生成:DailyRollingFileAppender,此模式下,日志文件按照生成的配置生成文件,并且该模式下并没有maxBackUpIndex属性,即不会维护日志文件的数量。
鉴于此原因,对源码进行修改,增加类似RollingFileAppender模式下maxBackUpIndex属性,进行日志文件的管理,新增属性,maxBackUpDay,保持日志文件的数量为设定的天数之内的文件,超过次天数的日志在自动回滚时进行删除。
代码比较简单,模仿maxBackUpIndex属性的实现。
在dailyrollingfileappender.h文件中,新增成员变量:maxBackupDay
class LOG4CXX_EXPORT DailyRollingFileAppender : public log4cxx::rolling::RollingFileAppenderSkeleton {
private:
int maxBackupDay; //表示保留至少该天数之内的文件
};
在dailyrollingfileappender.cpp文件中,首先进行变量的初始化。
DailyRollingFileAppender::DailyRollingFileAppender() :
maxBackupDay(0)
{
}
然后从配置文件中读取配置数据:
void DailyRollingFileAppender::setOption(const LogString& option,
const LogString& value) {
if (StringHelper::equalsIgnoreCase(option,
LOG4CXX_STR("DATEPATTERN"), LOG4CXX_STR("datepattern")))
{
setDatePattern(value);
}
else if (StringHelper::equalsIgnoreCase(option,
LOG4CXX_STR("MAXBACKUPDAY"), LOG4CXX_STR("maxbackupday"))
|| StringHelper::equalsIgnoreCase(option,
LOG4CXX_STR("MAXIMUMBACKUPDAY"), LOG4CXX_STR("maximumbackupday")))
{
maxBackupDay = StringHelper::toInt(value); //读取配置文件的配置
}
else {
RollingFileAppenderSkeleton::setOption(option, value);
}
}
然后在设置回滚策略时设置该参数:
void DailyRollingFileAppender::activateOptions(log4cxx::helpers::Pool& pool) {
policy->setFileNamePattern(pattern);
policy->setMaxDay(maxBackupDay);
policy->activateOptions(pool);
setTriggeringPolicy(policy);
setRollingPolicy(policy);
RollingFileAppenderSkeleton::activateOptions(pool);
}
在基于时间的回滚策略中,进行该配置的处理与实现。
在timebasedrollingpolicy.h文件中,新增成员函数:
class LOG4CXX_EXPORT TimeBasedRollingPolicy : public RollingPolicyBase, public TriggeringPolicy
{
private:
int maxBackupDay;
void deleteOldFiles(const LogString& currentActiveFile, log4cxx::helpers::Pool& pool);
LogString extractFileDir(const LogString& In); //获取当前日志目录
LogString extractFileName(const LogString& In); //获取当前正在记录的日志文件名称
public:
void setMaxDay(int newVal);
};
在timebasedrollingpolicy.cpp文件中,实现文件的回滚删除。
void TimeBasedRollingPolicy::setMaxDay(int newVal)
{
maxBackupDay = newVal;
}
LogString TimeBasedRollingPolicy::extractFileDir(const LogString& In)
{
LogString strtemp = In;
for (int i = strtemp.size() - 1; i >= 0; i--)
{
if (strtemp[i] == L'\\' || strtemp[i] == L'/')
{
strtemp.erase(i + 1);
return(strtemp);
}
}
return (L"");
}
LogString TimeBasedRollingPolicy::extractFileName(const LogString& In)
{
const LogString& strtemp = In;
for (int i = strtemp.size() - 1; i >= 0; i--)
{
if (strtemp[i] == L'\\' || strtemp[i] == L'/')
{
return strtemp.substr(i + 1);
}
}
return(strtemp);
}
oid TimeBasedRollingPolicy::deleteOldFiles(const LogString& currentActiveFile, log4cxx::helpers::Pool& pool)
{
if (maxBackupDay <= 0)
{
return;
}
if (currentActiveFile.size() == 0)
{
return;
}
LogString curLogFileDir = extractFileDir(currentActiveFile); //获取到当前日志的目录
LogString curLogFileName = extractFileName(currentActiveFile); //获取到当前日志文件名
/**获取到当前日志目录下面的所有文件列表,起初自己写了比较挫的使用WINAPI获取的代码,后面发现工程中已经有了比较简单的获取方法**/
File toRenameBase;
toRenameBase.setPath(curLogFileDir);
std::vector<LogString> filename;
std::vector<LogString> aryDelFiles;
filename = toRenameBase.list(pool);
__int64 sec1 = time(NULL);
logchar t[] = { '.'};
logchar tm[] = { '..' };
//遍历获取到的文件列表,将最后一次修改时间与当前比较,时间差大于设置的天数的文件路径存入待删除的容器中,然后删除。
for (int nIndex = 0; nIndex < filename.size(); ++nIndex)
{
if (filename[nIndex] == LogString(t) || filename[nIndex] == LogString(tm) || filename[nIndex] == curLogFileName)
{
continue;
}
File file;
file.setPath(curLogFileDir + filename[nIndex]);
log4cxx_time_t time = file.lastModified(pool) / 1000000;
if (sec1 - time > maxBackupDay * 24 * 3600)
{
aryDelFiles.push_back(curLogFileDir + filename[nIndex]);
}
}
//删除文件
for (int i = 0; i < aryDelFiles.size(); i++)
{
DeleteFileW(aryDelFiles[i].c_str());
}
}
回滚函数中有一点是需要注意的,就是我们删除文件的函数调用位置,在调试的时候,发现有一些代码在单步调试的时候不执行。
RolloverDescriptionPtr TimeBasedRollingPolicy::rollover( const LogString& currentActiveFile, Pool& pool)
{
...
LogString buf;
ObjectPtr obj(new Date(n));
formatFileName(obj, buf, pool);
deleteOldFiles(currentActiveFile, pool);
LogString newFileName(buf);
//
// if file names haven't changed, no rollover
//
if (newFileName == lastFileName) {
RolloverDescriptionPtr desc;
return desc;
}
// deleteOldFiles(currentActiveFile, pool); 调用放在此处时,发现单步调试时,不会被调用
...
if (currentActiveFile != lastBaseName) {
renameAction =
new FileRenameAction(
File().setPath(currentActiveFile), File().setPath(lastBaseName), true);
nextActiveFile = currentActiveFile;
}
...
}
道听途说,可能只是获取了一半的信息,另一半还在向你奔来的路上,你却直接转身返回了。找一找根源。更方便解决问题。站在系统的角度,而不是问题的角度。