一、需求
每天的日志按照一天四份的分割,即每天的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了解不深,猜想应该有更完美的解决方案,希望各位不吝赐教和指正