001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.rolling; 015 016import static ch.qos.logback.core.CoreConstants.CODES_URL; 017 018import java.io.File; 019import java.util.Date; 020 021import ch.qos.logback.core.CoreConstants; 022import ch.qos.logback.core.rolling.helper.*; 023 024/** 025 * When rolling over, <code>FixedWindowRollingPolicy</code> renames files 026 * according to a fixed window algorithm. 027 * 028 * For more information about this policy, please refer to the online manual at 029 * http://logback.qos.ch/manual/appenders.html#FixedWindowRollingPolicy 030 * 031 * @author Ceki Gülcü 032 */ 033public class FixedWindowRollingPolicy extends RollingPolicyBase { 034 static final String FNP_NOT_SET = "The \"FileNamePattern\" property must be set before using FixedWindowRollingPolicy. "; 035 static final String PRUDENT_MODE_UNSUPPORTED = "See also " + CODES_URL + "#tbr_fnp_prudent_unsupported"; 036 static final String SEE_PARENT_FN_NOT_SET = "Please refer to " + CODES_URL + "#fwrp_parentFileName_not_set"; 037 int maxIndex; 038 int minIndex; 039 RenameUtil util = new RenameUtil(); 040 Compressor compressor; 041 042 public static final String ZIP_ENTRY_DATE_PATTERN = "yyyy-MM-dd_HHmm"; 043 044 /** 045 * It's almost always a bad idea to have a large window size, say over 20. 046 */ 047 private static int MAX_WINDOW_SIZE = 20; 048 049 public FixedWindowRollingPolicy() { 050 minIndex = 1; 051 maxIndex = 7; 052 } 053 054 public void start() { 055 util.setContext(this.context); 056 057 if (fileNamePatternStr != null) { 058 fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context); 059 determineCompressionMode(); 060 } else { 061 addError(FNP_NOT_SET); 062 addError(CoreConstants.SEE_FNP_NOT_SET); 063 throw new IllegalStateException(FNP_NOT_SET + CoreConstants.SEE_FNP_NOT_SET); 064 } 065 066 if (isParentPrudent()) { 067 addError("Prudent mode is not supported with FixedWindowRollingPolicy."); 068 addError(PRUDENT_MODE_UNSUPPORTED); 069 throw new IllegalStateException("Prudent mode is not supported."); 070 } 071 072 if (getParentsRawFileProperty() == null) { 073 addError("The File name property must be set before using this rolling policy."); 074 addError(SEE_PARENT_FN_NOT_SET); 075 throw new IllegalStateException("The \"File\" option must be set."); 076 } 077 078 if (maxIndex < minIndex) { 079 addWarn("MaxIndex (" + maxIndex + ") cannot be smaller than MinIndex (" + minIndex + ")."); 080 addWarn("Setting maxIndex to equal minIndex."); 081 maxIndex = minIndex; 082 } 083 084 final int maxWindowSize = getMaxWindowSize(); 085 if ((maxIndex - minIndex) > maxWindowSize) { 086 addWarn("Large window sizes are not allowed."); 087 maxIndex = minIndex + maxWindowSize; 088 addWarn("MaxIndex reduced to " + maxIndex); 089 } 090 091 IntegerTokenConverter itc = fileNamePattern.getIntegerTokenConverter(); 092 093 if (itc == null) { 094 throw new IllegalStateException( 095 "FileNamePattern [" + fileNamePattern.getPattern() + "] does not contain a valid IntegerToken"); 096 } 097 098 if (compressionMode == CompressionMode.ZIP) { 099 String zipEntryFileNamePatternStr = transformFileNamePatternFromInt2Date(fileNamePatternStr); 100 zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context); 101 } 102 compressor = new Compressor(compressionMode); 103 compressor.setContext(this.context); 104 super.start(); 105 } 106 107 /** 108 * Subclasses can override this method to increase the max window size, if 109 * required. This is to address LOGBACK-266. 110 * 111 * @return 112 */ 113 protected int getMaxWindowSize() { 114 return MAX_WINDOW_SIZE; 115 } 116 117 private String transformFileNamePatternFromInt2Date(String fileNamePatternStr) { 118 String slashified = FileFilterUtil.slashify(fileNamePatternStr); 119 String stemOfFileNamePattern = FileFilterUtil.afterLastSlash(slashified); 120 return stemOfFileNamePattern.replace("%i", "%d{" + ZIP_ENTRY_DATE_PATTERN + "}"); 121 } 122 123 public void rollover() throws RolloverFailure { 124 125 // Inside this method it is guaranteed that the hereto active log file is 126 // closed. 127 // If maxIndex <= 0, then there is no file renaming to be done. 128 if (maxIndex >= 0) { 129 // Delete the oldest file, to keep Windows happy. 130 File file = new File(fileNamePattern.convertInt(maxIndex)); 131 132 if (file.exists()) { 133 file.delete(); 134 } 135 136 // Map {(maxIndex - 1), ..., minIndex} to {maxIndex, ..., minIndex+1} 137 for (int i = maxIndex - 1; i >= minIndex; i--) { 138 String toRenameStr = fileNamePattern.convertInt(i); 139 File toRename = new File(toRenameStr); 140 // no point in trying to rename a nonexistent file 141 if (toRename.exists()) { 142 util.rename(toRenameStr, fileNamePattern.convertInt(i + 1)); 143 } else { 144 addInfo("Skipping roll-over for inexistent file " + toRenameStr); 145 } 146 } 147 148 // move active file name to min 149 switch (compressionMode) { 150 case NONE: 151 util.rename(getActiveFileName(), fileNamePattern.convertInt(minIndex)); 152 break; 153 case GZ: 154 compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), null); 155 break; 156 case ZIP: 157 compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), 158 zipEntryFileNamePattern.convert(new Date())); 159 break; 160 } 161 } 162 } 163 164 /** 165 * Return the value of the parent's RawFile property. 166 */ 167 public String getActiveFileName() { 168 return getParentsRawFileProperty(); 169 } 170 171 public int getMaxIndex() { 172 return maxIndex; 173 } 174 175 public int getMinIndex() { 176 return minIndex; 177 } 178 179 public void setMaxIndex(int maxIndex) { 180 this.maxIndex = maxIndex; 181 } 182 183 public void setMinIndex(int minIndex) { 184 this.minIndex = minIndex; 185 } 186}