View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.rolling;
15  
16  import static ch.qos.logback.core.CoreConstants.CODES_URL;
17  
18  import java.io.File;
19  import java.util.Date;
20  
21  import ch.qos.logback.core.CoreConstants;
22  import ch.qos.logback.core.rolling.helper.*;
23  
24  /**
25   * When rolling over, <code>FixedWindowRollingPolicy</code> renames files
26   * according to a fixed window algorithm.
27   * 
28   * For more information about this policy, please refer to the online manual at
29   * http://logback.qos.ch/manual/appenders.html#FixedWindowRollingPolicy
30   * 
31   * @author Ceki G&uuml;lc&uuml;
32   */
33  public class FixedWindowRollingPolicy extends RollingPolicyBase {
34      static final String FNP_NOT_SET = "The \"FileNamePattern\" property must be set before using FixedWindowRollingPolicy. ";
35      static final String PRUDENT_MODE_UNSUPPORTED = "See also "+CODES_URL+"#tbr_fnp_prudent_unsupported";
36      static final String SEE_PARENT_FN_NOT_SET = "Please refer to "+CODES_URL+"#fwrp_parentFileName_not_set";
37      int maxIndex;
38      int minIndex;
39      RenameUtil util = new RenameUtil();
40      Compressor compressor;
41  
42      public static final String ZIP_ENTRY_DATE_PATTERN = "yyyy-MM-dd_HHmm";
43  
44      /**
45       * It's almost always a bad idea to have a large window size, say over 20.
46       */
47      private static int MAX_WINDOW_SIZE = 20;
48  
49      public FixedWindowRollingPolicy() {
50          minIndex = 1;
51          maxIndex = 7;
52      }
53  
54      public void start() {
55          util.setContext(this.context);
56  
57          if (fileNamePatternStr != null) {
58              fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);
59              determineCompressionMode();
60          } else {
61              addError(FNP_NOT_SET);
62              addError(CoreConstants.SEE_FNP_NOT_SET);
63              throw new IllegalStateException(FNP_NOT_SET + CoreConstants.SEE_FNP_NOT_SET);
64          }
65  
66          if (isParentPrudent()) {
67              addError("Prudent mode is not supported with FixedWindowRollingPolicy.");
68              addError(PRUDENT_MODE_UNSUPPORTED);
69              throw new IllegalStateException("Prudent mode is not supported.");
70          }
71  
72          if (getParentsRawFileProperty() == null) {
73              addError("The File name property must be set before using this rolling policy.");
74              addError(SEE_PARENT_FN_NOT_SET);
75              throw new IllegalStateException("The \"File\" option must be set.");
76          }
77  
78          if (maxIndex < minIndex) {
79              addWarn("MaxIndex (" + maxIndex + ") cannot be smaller than MinIndex (" + minIndex + ").");
80              addWarn("Setting maxIndex to equal minIndex.");
81              maxIndex = minIndex;
82          }
83  
84          final int maxWindowSize = getMaxWindowSize();
85          if ((maxIndex - minIndex) > maxWindowSize) {
86              addWarn("Large window sizes are not allowed.");
87              maxIndex = minIndex + maxWindowSize;
88              addWarn("MaxIndex reduced to " + maxIndex);
89          }
90  
91          IntegerTokenConverter itc = fileNamePattern.getIntegerTokenConverter();
92  
93          if (itc == null) {
94              throw new IllegalStateException("FileNamePattern [" + fileNamePattern.getPattern() + "] does not contain a valid IntegerToken");
95          }
96  
97          if (compressionMode == CompressionMode.ZIP) {
98              String zipEntryFileNamePatternStr = transformFileNamePatternFromInt2Date(fileNamePatternStr);
99              zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context);
100         }
101         compressor = new Compressor(compressionMode);
102         compressor.setContext(this.context);
103         super.start();
104     }
105 
106     /**
107      * Subclasses can override this method to increase the max window size, if required.  This is to
108      * address LOGBACK-266.
109      * @return
110      */
111     protected int getMaxWindowSize() {
112         return MAX_WINDOW_SIZE;
113     }
114 
115     private String transformFileNamePatternFromInt2Date(String fileNamePatternStr) {
116         String slashified = FileFilterUtil.slashify(fileNamePatternStr);
117         String stemOfFileNamePattern = FileFilterUtil.afterLastSlash(slashified);
118         return stemOfFileNamePattern.replace("%i", "%d{" + ZIP_ENTRY_DATE_PATTERN + "}");
119     }
120 
121     public void rollover() throws RolloverFailure {
122 
123         // Inside this method it is guaranteed that the hereto active log file is
124         // closed.
125         // If maxIndex <= 0, then there is no file renaming to be done.
126         if (maxIndex >= 0) {
127             // Delete the oldest file, to keep Windows happy.
128             File file = new File(fileNamePattern.convertInt(maxIndex));
129 
130             if (file.exists()) {
131                 file.delete();
132             }
133 
134             // Map {(maxIndex - 1), ..., minIndex} to {maxIndex, ..., minIndex+1}
135             for (int i = maxIndex - 1; i >= minIndex; i--) {
136                 String toRenameStr = fileNamePattern.convertInt(i);
137                 File toRename = new File(toRenameStr);
138                 // no point in trying to rename an inexistent file
139                 if (toRename.exists()) {
140                     util.rename(toRenameStr, fileNamePattern.convertInt(i + 1));
141                 } else {
142                     addInfo("Skipping roll-over for inexistent file " + toRenameStr);
143                 }
144             }
145 
146             // move active file name to min
147             switch (compressionMode) {
148             case NONE:
149                 util.rename(getActiveFileName(), fileNamePattern.convertInt(minIndex));
150                 break;
151             case GZ:
152                 compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), null);
153                 break;
154             case ZIP:
155                 compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), zipEntryFileNamePattern.convert(new Date()));
156                 break;
157             }
158         }
159     }
160 
161     /**
162      * Return the value of the parent's RawFile property.
163      */
164     public String getActiveFileName() {
165         return getParentsRawFileProperty();
166     }
167 
168     public int getMaxIndex() {
169         return maxIndex;
170     }
171 
172     public int getMinIndex() {
173         return minIndex;
174     }
175 
176     public void setMaxIndex(int maxIndex) {
177         this.maxIndex = maxIndex;
178     }
179 
180     public void setMinIndex(int minIndex) {
181         this.minIndex = minIndex;
182     }
183 }