001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2026, 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 v2.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 determineCompressionMode(); 059 adjustCompressionModeAndFileNamePatternStrIfNecessary(); 060 fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context); 061 } else { 062 addError(FNP_NOT_SET); 063 addError(CoreConstants.SEE_FNP_NOT_SET); 064 throw new IllegalStateException(FNP_NOT_SET + CoreConstants.SEE_FNP_NOT_SET); 065 } 066 067 if (isParentPrudent()) { 068 addError("Prudent mode is not supported with FixedWindowRollingPolicy."); 069 addError(PRUDENT_MODE_UNSUPPORTED); 070 throw new IllegalStateException("Prudent mode is not supported."); 071 } 072 073 if (getParentsRawFileProperty() == null) { 074 addError("The File name property must be set before using this rolling policy."); 075 addError(SEE_PARENT_FN_NOT_SET); 076 throw new IllegalStateException("The \"File\" option must be set."); 077 } 078 079 if (maxIndex < minIndex) { 080 addWarn("MaxIndex (" + maxIndex + ") cannot be smaller than MinIndex (" + minIndex + ")."); 081 addWarn("Setting maxIndex to equal minIndex."); 082 maxIndex = minIndex; 083 } 084 085 final int maxWindowSize = getMaxWindowSize(); 086 if ((maxIndex - minIndex) > maxWindowSize) { 087 addWarn("Large window sizes are not allowed."); 088 maxIndex = minIndex + maxWindowSize; 089 addWarn("MaxIndex reduced to " + maxIndex); 090 } 091 092 IntegerTokenConverter itc = fileNamePattern.getIntegerTokenConverter(); 093 094 if (itc == null) { 095 throw new IllegalStateException( 096 "FileNamePattern [" + fileNamePattern.getPattern() + "] does not contain a valid IntegerToken"); 097 } 098 099 if (compressionMode == CompressionMode.ZIP) { 100 String zipEntryFileNamePatternStr = transformFileNamePatternFromInt2Date(fileNamePatternStr); 101 zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context); 102 } 103 compressor = new Compressor(compressionMode); 104 compressor.setContext(this.context); 105 super.start(); 106 } 107 108 /** 109 * Subclasses can override this method to increase the max window size, if 110 * required. This is to address LOGBACK-266. 111 * 112 * @return 113 */ 114 protected int getMaxWindowSize() { 115 return MAX_WINDOW_SIZE; 116 } 117 118 private String transformFileNamePatternFromInt2Date(String fileNamePatternStr) { 119 String slashified = FileFilterUtil.slashify(fileNamePatternStr); 120 String stemOfFileNamePattern = FileFilterUtil.afterLastSlash(slashified); 121 return stemOfFileNamePattern.replace("%i", "%d{" + ZIP_ENTRY_DATE_PATTERN + "}"); 122 } 123 124 public void rollover() throws RolloverFailure { 125 126 // Inside this method it is guaranteed that the hereto active log file is 127 // closed. 128 // If maxIndex <= 0, then there is no file renaming to be done. 129 if (maxIndex >= 0) { 130 // Delete the oldest file, to keep Windows happy. 131 File file = new File(fileNamePattern.convertInt(maxIndex)); 132 133 if (file.exists()) { 134 file.delete(); 135 } 136 137 // Map {(maxIndex - 1), ..., minIndex} to {maxIndex, ..., minIndex+1} 138 for (int i = maxIndex - 1; i >= minIndex; i--) { 139 String toRenameStr = fileNamePattern.convertInt(i); 140 File toRename = new File(toRenameStr); 141 // no point in trying to rename a nonexistent file 142 if (toRename.exists()) { 143 util.rename(toRenameStr, fileNamePattern.convertInt(i + 1)); 144 } else { 145 addInfo("Skipping roll-over for inexistent file " + toRenameStr); 146 } 147 } 148 149 // move active file name to min 150 switch (compressionMode) { 151 case NONE: 152 util.rename(getActiveFileName(), fileNamePattern.convertInt(minIndex)); 153 break; 154 case GZ: 155 case XZ: 156 compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), null); 157 break; 158 case ZIP: 159 compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), 160 zipEntryFileNamePattern.convert(new Date())); 161 break; 162 } 163 } 164 } 165 166 /** 167 * Return the value of the parent's RawFile property. 168 */ 169 public String getActiveFileName() { 170 return getParentsRawFileProperty(); 171 } 172 173 public int getMaxIndex() { 174 return maxIndex; 175 } 176 177 public int getMinIndex() { 178 return minIndex; 179 } 180 181 public void setMaxIndex(int maxIndex) { 182 this.maxIndex = maxIndex; 183 } 184 185 public void setMinIndex(int minIndex) { 186 this.minIndex = minIndex; 187 } 188}