日志输出主要依赖RollingFileAppender、TimeBasedRollingPolicy、SizeAndTimeBasedFNATP。
- RollingFileAppender
主要用于生成日志文件,格式化内容再输出到日志文件 - TimeBasedRollingPolicy
设置回滚策略,如果发现日志输出的时间超过单位时间,则进行回滚,在RollingFileAppender的日志文件添加FileNamePattern后缀,同时清理掉MaxHistory时间之前的日志。例如如果fileNamePattern是%d{yyyy-MM-dd_HH-mm}.%i.gz,最后的时间单位是分钟,则在每一分钟之后进行回滚,将原始日志文件后打成gz压缩包,同时添加yyyy-MM-dd_HH-mm.i.gz作为后缀。如果maxHistory的值是30,则会在回滚时删除30分钟之前的日志。 - SizeAndTimeBasedFNATP
基于文件大小进行回滚,例如maxFileSize的值为1MB,则当文件大小超过1MB时进行回滚。
// RollingFileAppender用于定义日志输出的格式和路径
RollingFileAppenderObject> rollingFileAppender = (RollingFileAppenderObject>) appender;
rollingFileAppender.setContext(getContext());
rollingFileAppender.setLayout(layout);
if (getFileNamePattern() != null) {
// 基于时间的回滚策略
TimeBasedRollingPolicyObject> policy = new TimeBasedRollingPolicyObject>();
policy.setFileNamePattern(rollingFileAppender.rawFileProperty()+"."+getFileNamePattern());
policy.setContext(getContext());
policy.setMaxHistory(getMaxHistory());
policy.setParent(rollingFileAppender);
if (getMaxFileSize() != null) {
//基于文件大小的回滚策略
SizeAndTimeBasedFNATPObject> triggeringPolicy = new SizeAndTimeBasedFNATPObject>();
triggeringPolicy.setMaxFileSize(FileSize.valueOf(getMaxFileSize()));
triggeringPolicy.setTimeBasedRollingPolicy(policy);
triggeringPolicy.setContext(getContext());
policy.setTimeBasedFileNamingAndTriggeringPolicy(triggeringPolicy);
}
policy.start();
rollingFileAppender.setRollingPolicy(policy);
rollingFileAppender.start();
}
在TimeBasedRollingPolicy#start()中,主要是根据FileNamePattern的后缀名生成Compressor压缩对象,用来压缩日志文件,同时启动TimeBasedFileNamingAndTriggeringPolicy,TimeBasedFileNamingAndTriggeringPolicy是用来根据时间找到日志并清理的对象。
public void start() {
// set the LR for our utility object
renameUtil.setContext(this.context);
// find out period from the filename pattern
if (fileNamePatternStr != null) {
fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);
determineCompressionMode();
} else {
addWarn(FNP_NOT_SET);
addWarn(CoreConstants.SEE_FNP_NOT_SET);
throw new IllegalStateException(FNP_NOT_SET + CoreConstants.SEE_FNP_NOT_SET);
}
compressor = new Compressor(compressionMode);
compressor.setContext(context);
// wcs : without compression suffix
fileNamePatternWithoutCompSuffix = new FileNamePattern(Compressor.computeFileNameStrWithoutCompSuffix(fileNamePatternStr, compressionMode), this.context);
addInfo("Will use the pattern " + fileNamePatternWithoutCompSuffix + " for the active file");
if (compressionMode == CompressionMode.ZIP) {
String zipEntryFileNamePatternStr = transformFileNamePattern2ZipEntry(fileNamePatternStr);
zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context);
}
if (timeBasedFileNamingAndTriggeringPolicy == null) {
timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicyE>();
}
timeBasedFileNamingAndTriggeringPolicy.setContext(context);
timeBasedFileNamingAndTriggeringPolicy.setTimeBasedRollingPolicy(this);
timeBasedFileNamingAndTriggeringPolicy.start();
if (!timeBasedFileNamingAndTriggeringPolicy.isStarted()) {
addWarn("Subcomponent did not start. TimeBasedRollingPolicy will not start.");
return;
}
// the maxHistory property is given to TimeBasedRollingPolicy instead of to
// the TimeBasedFileNamingAndTriggeringPolicy. This makes it more convenient
// for the user at the cost of inconsistency here.
if (maxHistory != UNBOUND_HISTORY) {
archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover();
archiveRemover.setMaxHistory(maxHistory);
archiveRemover.setTotalSizeCap(totalSizeCap.getSize());
if (cleanHistoryOnStart) {
addInfo("Cleaning on start up");
Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
cleanUpFuture = archiveRemover.cleanAsynchronously(now);
}
} else if (!isUnboundedTotalSizeCap()) {
addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value ["+totalSizeCap+"]");
}
super.start();
}
TimeBasedFileNamingAndTriggeringPolicy对应的实现是SizeAndTimeBasedFNATP,因此start()方法会被调用,主要就是设置日志文件的起始时间,计算下次回滚的时间computeNextCheck(),当时间超过时就会进行回滚,同时创建ArchiveRemover用于删除日志文件。
// SizeAndTimeBasedFNATP
public void start() {
// we depend on certain fields having been initialized in super class
super.start();
archiveRemover = createArchiveRemover();
archiveRemover.setContext(context);
String regex = tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod);
String stemRegex = FileFilterUtil.afterLastSlash(regex);
computeCurrentPeriodsHighestCounterValue(stemRegex);
}
// TimeBasedFileNamingAndTriggeringPolicyBase
public void start() {
DateTokenConverterObject> dtc = tbrp.fileNamePattern.getPrimaryDateTokenConverter();
if (dtc == null) {
throw new IllegalStateException("FileNamePattern [" + tbrp.fileNamePattern.getPattern() + "] does not contain a valid DateToken");
}
if (dtc.getTimeZone() != null) {
rc = new RollingCalendar(dtc.getDatePattern(), dtc.getTimeZone(), Locale.getDefault());
} else {
rc = new RollingCalendar(dtc.getDatePattern());
}
addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '" + tbrp.fileNamePattern.getPattern() + "'.");
rc.printPeriodicity(this);
if (!rc.isCollisionFree()) {
addError("The date format in FileNamePattern will result in collisions in the names of archived log files.");
addError(CoreConstants.MORE_INFO_PREFIX + COLLIDING_DATE_FORMAT_URL);
withErrors();
return;
}
setDateInCurrentPeriod(new Date(getCurrentTime()));
if (tbrp.getParentsRawFileProperty() != null) {
File currentFile = new File(tbrp.getParentsRawFileProperty());
if (currentFile.exists() && currentFile.canRead()) {
setDateInCurrentPeriod(new Date(currentFile.lastModified()));
}
}
addInfo("Setting initial period to " + dateInCurrentPeriod);
computeNextCheck();
}
到这里,模块启动完毕,开始正式的处理日志,处理日志是从RollingFileAppender#doAppend()方法开始,最终是到RollingFileAppender#subAppend()方法开始进行正式的处理,着重分析这个方法即可。这个方法逻辑很简单,先判断是否能够进行回滚,然后rollover()回滚处理,再调用父类OutputStreamAppender#subAppend()输出内容到日志文件,我们主要关系回滚过程。
protected void subAppend(E event) {
synchronized (triggeringPolicy) {
if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) {
rollover();
}
}
super.subAppend(event);
}
首先根据TimeBasedRollingPolicy#isTriggeringEvent()判断是否能够进行回滚,会调用内置的TimeBasedFileNamingAndTriggeringPolicy对象进行判断,在两种情况下会进行回滚,一是如果时间到了,二是文件超过我们设置的maxFileSize。
// TimeBase服务器托管网dRollingPolicy
public boolean isTriggeringEvent(File activeFile, final E event) {
return timeBasedFileNamingAndTriggeringPolicy.isTriggeringEvent(activeFile, event);
}
// SizeAndTimeBasedFNATP
public boolean isTriggeringEvent(File activeFile, final E event) {
long time = getCurrentTime();
// first check for roll-over based on time
if (time >= nextCheck) {
Date dateInElapsedPeriod = dateInCurrentPeriod;
elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInElapsedPeriod, currentPeriodsCounter);
currentPeriodsCounter = 0;
setDateInCurrentPeriod(time);
computeNextCheck();
return true;
}
......
if (activeFile.length() >= maxFileSize.getSize()) {
elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, currentPeriodsCounter);
currentPeriodsCounter++;
return true;
}
return false;
}
回滚的处理是在RollingFileAppender#rollover()中。
服务器托管网public void rollover() {
lock.lock();
try {
//关闭原始文件流
this.closeOutputStream();
//回滚
attemptRollover();
//新建日志文件
attemptOpenFile();
} finally {
lock.unlock();
}
}
首先获取回滚时生成的文件名,然后进行回滚,如果没有指定压缩策略的就重命名,指定了就进行压缩。
public void rollover() throws RolloverFailure {
String elapsedPeriodsFileName = timeBasedFileNamingAndTriggeringPolicy.getElapsedPeriodsFileName();
String elapsedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName);
if (compressionMode == CompressionMode.NONE) {
if (getParentsRawFileProperty() != null) {
renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName);
} // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty == null }
} else {
if (getParentsRawFileProperty() == null) {
compressionFuture = compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, elapsedPeriodStem);
} else {
compressionFuture = renameRawAndAsyncCompress(elapsedPeriodsFileName, elapsedPeriodStem);
}
}
if (archiveRemover != null) {
Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
this.cleanUpFuture = archiveRemover.cleanAsynchronously(now);
}
}
如果需要进行定制的话,可以考虑重写RollingFileAppender、TimeBasedRollingPolicy,让日志按照我们的期望输出。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
一、什么是路由 SPA指的是一个 web网站只有唯一的一个 HTML页面,所有组 件的展示与切换都在这唯一的一个页面内完成。 此时,不同组服务器托管网件之间的切换需要通过前端路由来实现。 结论:在 SPA项目中,不同功能之间的切换,要依赖于前端路由来 完成! …