最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

资讯:log4cxx配置日期回滚策略中增加MaxFileSize属性

来源:博客园
目录
  • 1、背景
  • 2、实现方式
    • 2.1、DailyRollingFileAppender新增MaxFileSize属性
    • 2.2、TimeBasedRollingPolicy策略新增maxFileSize的判断
  • 3、总结

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 库针对该需要有两个问题:

  1. DailyRollingFileAppender只能针对文件进行回滚,效果是在文件名后增加日期后缀,不能实现日期创建目录;
  2. RollingFileAppender支持文件过大分片(MaxFileSize属性),但是 DailyRollingFileAppender不支持;

针对问题一,已有解决方案,见文章:按照日期回滚不创建新目录的BUG

问题二,log4cxx 库确实不支持,只能自定义实现了

2、实现方式

先使用配置文件如下:

log4j.rootLogger=INFO,Rlog4j.appender.R=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.R.ImmediateFlush=truelog4j.appender.R.Append=truelog4j.appender.R.MaxFileSize=1MBlog4j.appender.R.DatePattern="${LOG_HOMR_DIR}/${LOG_DIR}/"yyyy-MM-dd"/log.log"log4j.appender.R.layout=org.apache.log4j.PatternLayoutlog4j.appender.R.layout.ConversionPattern=%d{yyyy-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/settervoid 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、总结

本次修改的修改综合来说只有以下几步:

  1. appender 解析属性;
  2. appender 在配置active的函数中将属性交给policy;
  3. policy 负责策略,需要判断是否要进行 roll,并构造 roll 需要的action;
  4. action 最后执行,action有多种,rename 是其中一个;

由此可见,log4cxx的框架层次分明,结构合理,便于修改和拓展,设计是相当优秀的,值得深入学习

关键词: 当前记录 文件大小 构造函数