001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights 003 * reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License 006 * v1.0 as published by the Eclipse Foundation 007 * 008 * or (per the licensee's choosing) 009 * 010 * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 011 */ 012package ch.qos.logback.core.rolling; 013 014import static ch.qos.logback.core.CoreConstants.MANUAL_URL_PREFIX; 015 016import java.io.File; 017import java.time.Instant; 018 019import ch.qos.logback.core.CoreConstants; 020import ch.qos.logback.core.joran.spi.NoAutoStart; 021import ch.qos.logback.core.rolling.helper.ArchiveRemover; 022import ch.qos.logback.core.rolling.helper.CompressionMode; 023import ch.qos.logback.core.rolling.helper.FileFilterUtil; 024import ch.qos.logback.core.rolling.helper.SizeAndTimeBasedArchiveRemover; 025import ch.qos.logback.core.util.Duration; 026import ch.qos.logback.core.util.FileSize; 027 028/** 029 * This class implement {@link TimeBasedFileNamingAndTriggeringPolicy} 030 * interface extending {@link TimeBasedFileNamingAndTriggeringPolicyBase}. This class is intended to be nested 031 * within a {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy} instance. However, it can also be 032 * instantiated directly for testing purposes. 033 * 034 * <p>{@link SizeAndTimeBasedFNATP} class was renamed as {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy} 035 * in version 1.5.8.</p> 036 * 037 * @author Ceki Gülcü 038 * 039 * @param <E> 040 * @since 1.5.8 041 */ 042@NoAutoStart 043public class SizeAndTimeBasedFileNamingAndTriggeringPolicy<E> extends TimeBasedFileNamingAndTriggeringPolicyBase<E> { 044 045 enum Usage { 046 EMBEDDED, DIRECT 047 } 048 049 volatile int currentPeriodsCounter = 0; 050 FileSize maxFileSize; 051 052 Duration checkIncrement = null; 053 054 static String MISSING_INT_TOKEN = "Missing integer token, that is %i, in FileNamePattern ["; 055 static String MISSING_DATE_TOKEN = "Missing date token, that is %d, in FileNamePattern ["; 056 057 private final Usage usage; 058 059 //InvocationGate invocationGate = new FixedIntervalInvocationGate(); 060 061 public SizeAndTimeBasedFileNamingAndTriggeringPolicy() { 062 this(Usage.DIRECT); 063 } 064 065 public SizeAndTimeBasedFileNamingAndTriggeringPolicy(Usage usage) { 066 this.usage = usage; 067 } 068 069 public LengthCounter lengthCounter = new LengthCounterBase(); 070 071 072 073 @Override 074 public void start() { 075 // we depend on certain fields having been initialized in super class 076 super.start(); 077 078 if (usage == Usage.DIRECT) { 079 addWarn(CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED); 080 addWarn(CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED_BIS); 081 addWarn("For more information see " + MANUAL_URL_PREFIX + "appenders.html#SizeAndTimeBasedRollingPolicy"); 082 } 083 084 if (!super.isErrorFree()) 085 return; 086 087 if (maxFileSize == null) { 088 addError("maxFileSize property is mandatory."); 089 withErrors(); 090 } 091 092 //if (checkIncrement != null) 093 // invocationGate = new FixedIntervalInvocationGate(checkIncrement); 094 095 if (!validateDateAndIntegerTokens()) { 096 withErrors(); 097 return; 098 } 099 100 archiveRemover = createArchiveRemover(); 101 archiveRemover.setContext(context); 102 103 // we need to get the correct value of currentPeriodsCounter. 104 // usually the value is 0, unless the appender or the application 105 // is stopped and restarted within the same period 106 String regex = enclosingTBRP.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod); 107 String stemRegex = FileFilterUtil.afterLastSlash(regex); 108 109 computeCurrentPeriodsHighestCounterValue(stemRegex); 110 111 if (isErrorFree()) { 112 started = true; 113 } 114 } 115 116 private boolean validateDateAndIntegerTokens() { 117 boolean inError = false; 118 if (enclosingTBRP.fileNamePattern.getIntegerTokenConverter() == null) { 119 inError = true; 120 addError(MISSING_INT_TOKEN + enclosingTBRP.fileNamePatternStr + "]"); 121 addError(CoreConstants.SEE_MISSING_INTEGER_TOKEN); 122 } 123 if (enclosingTBRP.fileNamePattern.getPrimaryDateTokenConverter() == null) { 124 inError = true; 125 addError(MISSING_DATE_TOKEN + enclosingTBRP.fileNamePatternStr + "]"); 126 } 127 128 return !inError; 129 } 130 131 protected ArchiveRemover createArchiveRemover() { 132 return new SizeAndTimeBasedArchiveRemover(enclosingTBRP.fileNamePattern, 133 enclosingTBRP.fileNamePatternWithoutCompSuffix, rc); 134 } 135 136 void computeCurrentPeriodsHighestCounterValue(final String stemRegex) { 137 File file = new File(getCurrentPeriodsFileNameWithoutCompressionSuffix()); 138 File parentDir = file.getParentFile(); 139 140 File[] matchingFileArray = FileFilterUtil.filesInFolderMatchingStemRegex(parentDir, stemRegex); 141 142 if (matchingFileArray == null || matchingFileArray.length == 0) { 143 currentPeriodsCounter = 0; 144 return; 145 } 146 currentPeriodsCounter = FileFilterUtil.findHighestCounter(matchingFileArray, stemRegex); 147 148 // if parent raw file property is not null, then the next 149 // counter is max found counter+1 150 if (enclosingTBRP.getParentsRawFileProperty() != null || (enclosingTBRP.compressionMode != CompressionMode.NONE)) { 151 // TODO test me 152 currentPeriodsCounter++; 153 } 154 } 155 156 @Override 157 public boolean isTriggeringEvent(File activeFile, final E event) { 158 159 long currentTime = getCurrentTime(); 160 long localNextCheck = atomicNextCheck.get(); 161 162 // first check for roll-over based on time 163 if (currentTime >= localNextCheck) { 164 long nextCheckCandidate = computeNextCheck(currentTime); 165 atomicNextCheck.set(nextCheckCandidate); 166 Instant instantInElapsedPeriod = dateInCurrentPeriod; 167 elapsedPeriodsFileName = enclosingTBRP.fileNamePatternWithoutCompSuffix.convertMultipleArguments( 168 instantInElapsedPeriod, currentPeriodsCounter); 169 currentPeriodsCounter = 0; 170 setDateInCurrentPeriod(currentTime); 171 lengthCounter.reset(); 172 return true; 173 } 174 175 boolean result = checkSizeBasedTrigger(activeFile, currentTime); 176 if(result) 177 lengthCounter.reset(); 178 return result; 179 } 180 181 private boolean checkSizeBasedTrigger(File activeFile, long currentTime) { 182 // next check for roll-over based on size 183 //if (invocationGate.isTooSoon(currentTime)) { 184 // return false; 185 //} 186 187 if (activeFile == null) { 188 addWarn("activeFile == null"); 189 return false; 190 } 191 if (maxFileSize == null) { 192 addWarn("maxFileSize = null"); 193 return false; 194 } 195 196 197 198 if (lengthCounter.getLength() >= maxFileSize.getSize()) { 199 200 elapsedPeriodsFileName = enclosingTBRP.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, 201 currentPeriodsCounter); 202 currentPeriodsCounter++; 203 204 return true; 205 } 206 207 return false; 208 } 209 210 public Duration getCheckIncrement() { 211 return null; 212 } 213 214 public void setCheckIncrement(Duration checkIncrement) { 215 addWarn("Since version 1.5.8, 'checkIncrement' property has no effect"); 216 } 217 218 @Override 219 public String getCurrentPeriodsFileNameWithoutCompressionSuffix() { 220 return enclosingTBRP.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, 221 currentPeriodsCounter); 222 } 223 224 public void setMaxFileSize(FileSize aMaxFileSize) { 225 this.maxFileSize = aMaxFileSize; 226 } 227 228 @Override 229 public LengthCounter getLengthCounter() { 230 return lengthCounter; 231 } 232}