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(
95                      "FileNamePattern [" + fileNamePattern.getPattern() + "] does not contain a valid IntegerToken");
96          }
97  
98          if (compressionMode == CompressionMode.ZIP) {
99              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 an inexistent 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 }