一、需求
每天的日志按照一天四份的分割,即每天的6点、12点、18点、24点分割日志。
二、现状
目前的logback只是按照每月、日、半天、时、分切割,没有按照每隔6小时切割的配置支持(我知道的没有,请指教)
三、解决方案
之前使用过SizeAndTimeBasedFNATP来按照时间和文件大小分割日志;需求相似性首先想到通过扩展这个类来满足需求。
四、实施步骤
1、SizeAndTimeBasedFNATP源码理解(这里只看了几个从父类集成来的方法)
start方法:策略初始化的方法,在启动时初始化相关属性和方法; isTriggeringEvent方法:触发器是否触发方法(核心) getCurrentPeriodsFileNameWithoutCompressionSuffix:获取当前时期文件名的方法 比较重要的是第二个isTriggeringEvent方法
@Override public void start() { // 通过父类的start方法初始化相关属性 super.start(); //和压缩相关的API,暂不关注 archiveRemover = createArchiveRemover(); archiveRemover.setContext(context); // we need to get the correct value of currentPeriodsCounter. // usually the value is 0, unless the appender or the application // is stopped and restarted within the same period //主要用于初始化currentPeriodsCounter这个变量,这个变量是用来计算在指定时间间隔内第几个文件 String regex = tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod); String stemRegex = FileFilterUtil.afterLastSlash(regex); computeCurrentPeriodsHighestCounterValue(stemRegex); started = true; }
public boolean isTriggeringEvent(File activeFile, final E event) { //检测当前时间是否超过下一个时间监测点, //如果超过,计算文件名(elapsedPeriodsFileName)、重置当前时期属性(dateInCurrentPeriod)、 //计算下一个时间监测点(nextCheck) long time = getCurrentTime(); if (time >= nextCheck) { Date dateInElapsedPeriod = dateInCurrentPeriod; elapsedPeriodsFileName = tbrp.fileNamePatternWCS .convertMultipleArguments(dateInElapsedPeriod, currentPeriodsCounter); currentPeriodsCounter = 0; setDateInCurrentPeriod(time); computeNextCheck(); return true; } // for performance reasons, check for changes every 16,invocationMask invocations if (((++invocationCounter) & invocationMask) != invocationMask) { return false; } if (invocationMask < 0x0F) { invocationMask = (invocationMask << 1) + 1; } //文件大小检测,如果指定大小,增加currentPeriodsCounter if (activeFile.length() >= maxFileSize.getSize()) { elapsedPeriodsFileName = tbrp.fileNamePatternWCS .convertMultipleArguments(dateInCurrentPeriod, currentPeriodsCounter); currentPeriodsCounter++; return true; } return false; }
2、解决思路
从上面的源码解读中,我很快注意到了两个成员变量: dateInCurrentPeriod:当前所处的时期 nextCheck:下一次检测时间 如果能把nextCheck每次推后6个小时,是否就满足我的需求了呢。
3、具体实施
a、新增一个SizeAndTimeBasedFNATP的子类DemoSizeAndTimeBasedFNATP。 b、添加一个int型成员变量:multiple,用于配制每次添加几个单元时间 c、重写计算nextCheck的方法computeNextCheck如下: protected void computeNextCheck() { nextCheck = rc.getRelativeDate(dateInCurrentPeriod,multiple).getTime(); }
经过上述三步,我的扩展类变成如下:
import java.util.Calendar; import java.util.Date; import ch.qos.logback.core.joran.spi.NoAutoStart; import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP; @NoAutoStart public class DemoSizeAndTimeBasedFNATP<E> extends SizeAndTimeBasedFNATP<E> { private Integer multiple = 1; protected void computeNextCheck() { nextCheck = rc.getRelativeDate(dateInCurrentPeriod,multiple).getTime(); } public Integer getMultiple() { return multiple; } public void setMultiple(Integer multiple) { if(multiple>1){ this.multiple = multiple; } } }
将此类配制到logback.xml中
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${filePrefix}/demo-error.%d{yyyyMMddHHmm}.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="com.demo.utils.MlsPaySizeAndTimeBasedFNATP"> <multiple>10</multiple> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy>
经过以上配置,日志文件变成了每隔6分钟一个分割。
似乎已经完成了我的需求,但是很快我发现了一个问题,那就是:
我中间停机3分钟,再次启动,其切割时间会向后平推。这不符合需求每天6点、12点、18点、24点切割的需求。 很快意识到是在启动时,dateInCurrentPeriod和nextCheck两个变量值初始化的问题。 因此决定在start方法中重新校正一下这两个参数的值,让dateInCurrentPeriod在启动时,初始化为所处时段的初始时间; 让nextCheck初始为所处时段的结束时间。
具体代码如下:
@Override public void start() { super.start(); initCurrentPeriod(multiple); initNextCheck(multiple); } private void initCurrentPeriod(Integer multiple){ Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); switch (rc.getPeriodicityType()) { case TOP_OF_MINUTE: calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); calendar.set(Calendar.MINUTE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple)*multiple); break; case TOP_OF_HOUR: calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); calendar.set(Calendar.HOUR_OF_DAY,(calendar.get(Calendar.HOUR_OF_DAY)/multiple)*multiple); break; case TOP_OF_DAY: calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); calendar.set(Calendar.DATE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple)*multiple); break; default: throw new IllegalStateException("不支持倍数增加的单位"); } dateInCurrentPeriod = calendar.getTime(); } private void initNextCheck(Integer multiple){ Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); switch (rc.getPeriodicityType()) { case TOP_OF_MINUTE: calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); calendar.set(Calendar.MINUTE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple+1)*multiple); break; case TOP_OF_HOUR: calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); calendar.set(Calendar.HOUR_OF_DAY,(calendar.get(Calendar.HOUR_OF_DAY)/multiple+1)*multiple); break; case TOP_OF_DAY: calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); calendar.set(Calendar.DATE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple+1)*multiple); break; default: throw new IllegalStateException("不支持倍数增加的单位"); } nextCheck = calendar.getTime().getTime(); }
经过以上配置,再将logback中的配置改为每2小时分割,部署到测试环境,目前已经跑了一天了,看起来一切正常。
以上只是我个人的解决方法,对于logback了解不深,猜想应该有更完美的解决方案,希望各位不吝赐教和指正