log4cxx配置日期回滚策略中增加MaxFileSize属性
Posted Sherlock的程序人生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4cxx配置日期回滚策略中增加MaxFileSize属性相关的知识,希望对你有一定的参考价值。
1、背景
C++ 项目,使用 log4cxx 日志库,需要按照日期生成日志目录,目录下存放日志文件,且日志文件不能过大,过大需要分片,例如,文件目录可以是:
.
|-- log
|---|-- 2022-10-02
|---|---|-- log.log
|---|---|-- log.log.1
|---|---|-- log.log.2
|---|---|-- log.log.3
|---|-- 2022-10-03
|---|---|-- log.log
|---|---|-- log.log.1
|---|---|-- log.log.2
log4cxx 库针对该需要有两个问题:
- DailyRollingFileAppender 只能针对文件进行回滚,效果是在文件名后增加日期后缀,不能实现日期创建目录;
- RollingFileAppender 支持文件过大分片(MaxFileSize 属性),但是 DailyRollingFileAppender 不支持;
针对问题一,已有解决方案,见文章:按照日期回滚不创建新目录的BUG
问题二,log4cxx 库确实不支持,只能自定义实现了
2、实现方式
先使用配置文件如下:
log4j.rootLogger=INFO,R
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.ImmediateFlush=true
log4j.appender.R.Append=true
log4j.appender.R.MaxFileSize=1MB
log4j.appender.R.DatePattern=\'$LOG_HOMR_DIR/$LOG_DIR/\'yyyy-MM-dd\'/log.log\'
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%dyyyy-MM-dd hh:mm:ss.SSS %-5p [%c] %m %n
正常情况下,MaxFileSize 因为不是 DailyRollingFileAppender 的属性,所以不会生效,我们需要进行如下修改
2.1、DailyRollingFileAppender新增MaxFileSize属性
配置中的属性直接对应 appender 的属性,所以添加属性的最外层应该是 appender
DailyRollingFileAppender 类是配置文件中指定的 appender,所以属性先加在这里
代码文件 dailyrollingfileappender.h,增加属性和getter/setter
namespace log4cxx
...
class LOG4CXX_EXPORT DailyRollingFileAppender : public log4cxx::rolling::RollingFileAppenderSkeleton
DECLARE_LOG4CXX_OBJECT(DailyRollingFileAppender)
BEGIN_LOG4CXX_CAST_MAP()
LOG4CXX_CAST_ENTRY(DailyRollingFileAppender)
LOG4CXX_CAST_ENTRY_CHAIN(FileAppender)
END_LOG4CXX_CAST_MAP()
/**
The date pattern used to initiate rollover.
*/
LogString datePattern;
int maxFileSize;//新增属性,用于控制单个文件大小
public:
DailyRollingFileAppender();
//属性的getter/setter
void setMaxFileSize( const LogString & value);
long getMaxFileSize() const;
...
;
LOG4CXX_PTR_DEF(DailyRollingFileAppender);
...
dailyrollingfileappender.cpp 实现属性的获取和设置
DailyRollingFileAppender::DailyRollingFileAppender(
const LayoutPtr& layout,
const LogString& filename,
const LogString& datePattern1)
: datePattern(datePattern1), maxFileSize(0) //构造函数进行maxFileSize属性初始化
setLayout(layout);
setFile(filename);
Pool p;
activateOptions(p);
//getter/setter
void DailyRollingFileAppender::setMaxFileSize( const LogString & value)
//这里调用log4cxx的接口,进行单位转换
maxFileSize = OptionConverter::toFileSize(value, maxFileSize + 1);
long DailyRollingFileAppender::getMaxFileSize() const
return maxFileSize;
//activateOptions方法作用是激活配置,即将配置塞到policy中
void DailyRollingFileAppender::activateOptions(log4cxx::helpers::Pool& pool)
TimeBasedRollingPolicyPtr policy = new TimeBasedRollingPolicy();
LogString pattern(getFile());
bool inLiteral = false;
bool inPattern = false;
for (size_t i = 0; i < datePattern.length(); i++)
if (datePattern[i] == 0x27 /* \'\\\'\' */)
inLiteral = !inLiteral;
if (inLiteral && inPattern)
pattern.append(1, (logchar) 0x7D /* \'\' */);
inPattern = false;
else
if (!inLiteral && !inPattern)
const logchar dbrace[] = 0x25, 0x64, 0x7B, 0 ; // "%d"
pattern.append(dbrace);
inPattern = true;
pattern.append(1, datePattern[i]);
if (inPattern)
pattern.append(1, (logchar) 0x7D /* \'\' */);
policy->setFileNamePattern(pattern);
policy->setMaxFileSize(maxFileSize);//新增,将maxFileSize属性塞给policy
policy->activateOptions(pool);
setTriggeringPolicy(policy);
setRollingPolicy(policy);
RollingFileAppenderSkeleton::activateOptions(pool);
2.2、TimeBasedRollingPolicy策略新增maxFileSize的判断
appender 把 maxFileSize 属性塞给了 policy,那么 policy 需要新增对应接口来接收,并做策略的判断
同时 policy 只是策略,roll 的具体操作在 action 中,policy 根据需要创建 action,然后由 action 来执行,具体见下面代码
DailyRollingFileAppender 只使用了 TimeBasedRollingPolicy ,所以我们只需要看 TimeBasedRollingPolicy 的代码
timebasedrollingpolicy.h 修改如下:
namespace log4cxx
namespace rolling
class LOG4CXX_EXPORT TimeBasedRollingPolicy : public RollingPolicyBase,
public TriggeringPolicy
DECLARE_LOG4CXX_OBJECT(TimeBasedRollingPolicy)
BEGIN_LOG4CXX_CAST_MAP()
LOG4CXX_CAST_ENTRY(TimeBasedRollingPolicy)
LOG4CXX_CAST_ENTRY_CHAIN(RollingPolicyBase)
LOG4CXX_CAST_ENTRY_CHAIN(TriggeringPolicy)
END_LOG4CXX_CAST_MAP()
private:
//新增以下三个属性
size_t maxFileSize;//单个文件的大小上限
bool fileSizeChanged;//当前记录的文件是否已经超出上限
int backupIndex;//rollover的index,即需要给备份文件增加的后缀
public:
TimeBasedRollingPolicy();
//maxFileSize属性的getter/setter
void setMaxFileSize(size_t size);
size_t getMaxFileSize() const;
;
LOG4CXX_PTR_DEF(TimeBasedRollingPolicy);
timebasedrollingpolicy.cpp 修改如下:
构造函数进行属性初始化,index从1开始,maxFileSize为0表示没有大小限制
TimeBasedRollingPolicy::TimeBasedRollingPolicy()
: maxFileSize(0), fileSizeChanged(false), backupIndex(1)
新增 maxFileSize 属性的 getter/setter
void TimeBasedRollingPolicy::setMaxFileSize(size_t size)
maxFileSize = size;
size_t TimeBasedRollingPolicy::getMaxFileSize() const
return maxFileSize;
isTriggeringEvent() 方法用于判断是否需要进行 rollover,修改如下:
lastFileName 变量存放备份的日志的文件的绝对路径,不需要备份,则它应该和当前记录的日志文件一直,若我们需要备份,则该变量就是备份的文件
这里先检查日期是否变化,如果日志变化了,则无需判断文件大小
bool TimeBasedRollingPolicy::isTriggeringEvent(
Appender* /* appender */,
const log4cxx::spi::LoggingEventPtr& /* event */,
const LogString& /* filename */,
size_t fileLength)
if (apr_time_now() > nextCheck)
return true;
//maxFileSize <= 0 则不检查大小,当前日志文件超出限额,则需要rollover
if (maxFileSize > 0 && fileLength >= maxFileSize)
fileSizeChanged = true;//置true,表示需要分片
//备份的文件增加后缀index,同时index加一
lastFileName = lastFileName + "." + std::to_string(backupIndex++);
return true;
return false;
rollover() 方法是日志回滚的具体实现,修改如下:
RolloverDescriptionPtr TimeBasedRollingPolicy::rollover(
const LogString& currentActiveFile,
Pool& pool)
apr_time_t n = apr_time_now();
nextCheck = ((n / APR_USEC_PER_SEC) + 1) * APR_USEC_PER_SEC;
LogString buf;
ObjectPtr obj(new Date(n));
formatFileName(obj, buf, pool);
LogString newFileName(buf);
//
// if file names haven\'t changed, no rollover
//
//新增fileSizeChanged的判断,日志变化或者文件超限都需要执行rollover
if (newFileName == lastFileName && !fileSizeChanged)
RolloverDescriptionPtr desc;
return desc;
if (fileSizeChanged)
//如果是文件超限,表示不是日期变化的roll,重置flag
fileSizeChanged = false;
else
//日期变化,则备份的文件后缀index重新从1开始
backupIndex = 1;
ActionPtr renameAction;
ActionPtr compressAction;
LogString lastBaseName(
lastFileName.substr(0, lastFileName.length() - suffixLength));
LogString nextActiveFile(
newFileName.substr(0, newFileName.length() - suffixLength));
//
// if currentActiveFile is not lastBaseName then
// active file name is not following file pattern
// and requires a rename plus maintaining the same name
if (currentActiveFile != lastBaseName)
//创建rename action,用于将当前的日志文件重命名,实现日志的备份
renameAction =
new FileRenameAction(
File().setPath(currentActiveFile), File().setPath(lastBaseName), true);
nextActiveFile = currentActiveFile;
if (suffixLength == 3)
compressAction =
new GZCompressAction(
File().setPath(lastBaseName), File().setPath(lastFileName), true);
if (suffixLength == 4)
compressAction =
new ZipCompressAction(
File().setPath(lastBaseName), File().setPath(lastFileName), true);
lastFileName = newFileName;
return new RolloverDescription(
nextActiveFile, false, renameAction, compressAction);
这里我们只需要创建 FileRenameAction,告诉他需要 rename 的文件和新的文件即可
3、总结
本次修改的修改综合来说只有以下几步:
- appender 解析属性;
- appender 在配置active的函数中将属性交给policy;
- policy 负责策略,需要判断是否要进行 roll,并构造 roll 需要的action;
- action 最后执行,action有多种,rename 是其中一个;
由此可见,log4cxx 的框架层次分明,结构合理,便于修改和拓展,设计是相当优秀的,值得深入学习
以上是关于log4cxx配置日期回滚策略中增加MaxFileSize属性的主要内容,如果未能解决你的问题,请参考以下文章
我可以在 JNI 项目中使用 Java 中的 log4j 和 C++ 中的 log4cxx 将日志存储在同一个文件中吗?