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 SimpleInvocationGate(); 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 SimpleInvocationGate(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 = tbrp.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 (tbrp.fileNamePattern.getIntegerTokenConverter() == null) { 119 inError = true; 120 addError(MISSING_INT_TOKEN + tbrp.fileNamePatternStr + "]"); 121 addError(CoreConstants.SEE_MISSING_INTEGER_TOKEN); 122 } 123 if (tbrp.fileNamePattern.getPrimaryDateTokenConverter() == null) { 124 inError = true; 125 addError(MISSING_DATE_TOKEN + tbrp.fileNamePatternStr + "]"); 126 } 127 128 return !inError; 129 } 130 131 protected ArchiveRemover createArchiveRemover() { 132 return new SizeAndTimeBasedArchiveRemover(tbrp.fileNamePattern, rc); 133 } 134 135 void computeCurrentPeriodsHighestCounterValue(final String stemRegex) { 136 File file = new File(getCurrentPeriodsFileNameWithoutCompressionSuffix()); 137 File parentDir = file.getParentFile(); 138 139 File[] matchingFileArray = FileFilterUtil.filesInFolderMatchingStemRegex(parentDir, stemRegex); 140 141 if (matchingFileArray == null || matchingFileArray.length == 0) { 142 currentPeriodsCounter = 0; 143 return; 144 } 145 currentPeriodsCounter = FileFilterUtil.findHighestCounter(matchingFileArray, stemRegex); 146 147 // if parent raw file property is not null, then the next 148 // counter is max found counter+1 149 if (tbrp.getParentsRawFileProperty() != null || (tbrp.compressionMode != CompressionMode.NONE)) { 150 // TODO test me 151 currentPeriodsCounter++; 152 } 153 } 154 155 @Override 156 public boolean isTriggeringEvent(File activeFile, final E event) { 157 158 long currentTime = getCurrentTime(); 159 long localNextCheck = atomicNextCheck.get(); 160 161 // first check for roll-over based on time 162 if (currentTime >= localNextCheck) { 163 long nextCheckCandidate = computeNextCheck(currentTime); 164 atomicNextCheck.set(nextCheckCandidate); 165 Instant instantInElapsedPeriod = dateInCurrentPeriod; 166 elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments( 167 instantInElapsedPeriod, currentPeriodsCounter); 168 currentPeriodsCounter = 0; 169 setDateInCurrentPeriod(currentTime); 170 lengthCounter.reset(); 171 return true; 172 } 173 174 boolean result = checkSizeBasedTrigger(activeFile, currentTime); 175 if(result) 176 lengthCounter.reset(); 177 return result; 178 } 179 180 private boolean checkSizeBasedTrigger(File activeFile, long currentTime) { 181 // next check for roll-over based on size 182 //if (invocationGate.isTooSoon(currentTime)) { 183 // return false; 184 //} 185 186 if (activeFile == null) { 187 addWarn("activeFile == null"); 188 return false; 189 } 190 if (maxFileSize == null) { 191 addWarn("maxFileSize = null"); 192 return false; 193 } 194 195 196 197 if (lengthCounter.getLength() >= maxFileSize.getSize()) { 198 199 elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, 200 currentPeriodsCounter); 201 currentPeriodsCounter++; 202 203 return true; 204 } 205 206 return false; 207 } 208 209 public Duration getCheckIncrement() { 210 return null; 211 } 212 213 public void setCheckIncrement(Duration checkIncrement) { 214 addWarn("Since version 1.5.8, 'checkIncrement' property has no effect"); 215 } 216 217 @Override 218 public String getCurrentPeriodsFileNameWithoutCompressionSuffix() { 219 return tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, 220 currentPeriodsCounter); 221 } 222 223 public void setMaxFileSize(FileSize aMaxFileSize) { 224 this.maxFileSize = aMaxFileSize; 225 } 226 227 @Override 228 public LengthCounter getLengthCounter() { 229 return lengthCounter; 230 } 231}