View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2009, 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 java.io.File;
17  import java.util.Date;
18  import java.util.concurrent.Future;
19  
20  import ch.qos.logback.core.CoreConstants;
21  import ch.qos.logback.core.rolling.helper.ArchiveRemover;
22  import ch.qos.logback.core.rolling.helper.AsynchronousCompressor;
23  import ch.qos.logback.core.rolling.helper.CompressionMode;
24  import ch.qos.logback.core.rolling.helper.Compressor;
25  import ch.qos.logback.core.rolling.helper.FileNamePattern;
26  import ch.qos.logback.core.rolling.helper.RenameUtil;
27  
28  /**
29   * <code>TimeBasedRollingPolicy</code> is both easy to configure and quite
30   * powerful. It allows the roll over to be made based on time. It is possible to
31   * specify that the roll over occur once per day, per week or per month.
32   * 
33   * <p>For more information, please refer to the online manual at
34   * http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy
35   * 
36   * @author Ceki G&uuml;lc&uuml;
37   */
38  public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements
39      TriggeringPolicy<E> {
40    static final String FNP_NOT_SET = "The FileNamePattern option must be set before using TimeBasedRollingPolicy. ";
41    static final int NO_DELETE_HISTORY = 0;
42  
43    // WCS: without compression suffix
44    FileNamePattern fileNamePatternWCS;
45  
46    private Compressor compressor;
47    private RenameUtil renameUtil = new RenameUtil();
48    Future<?> future;
49  
50    private int maxHistory = NO_DELETE_HISTORY;
51    private ArchiveRemover archiveRemover;
52  
53    TimeBasedFileNamingAndTriggeringPolicy<E> timeBasedTriggering;
54  
55    public void start() {
56      // set the LR for our utility object
57      renameUtil.setContext(this.context);
58  
59      // find out period from the filename pattern
60      if (fileNamePatternStr != null) {
61        fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);
62        determineCompressionMode();
63      } else {
64        addWarn(FNP_NOT_SET);
65        addWarn(CoreConstants.SEE_FNP_NOT_SET);
66        throw new IllegalStateException(FNP_NOT_SET
67            + CoreConstants.SEE_FNP_NOT_SET);
68      }
69  
70      compressor = new Compressor(compressionMode);
71      compressor.setContext(context);
72  
73      fileNamePatternWCS = new FileNamePattern(computeFileNameStr_WCS(
74          fileNamePatternStr, compressionMode), this.context);
75  
76      addInfo("Will use the pattern " + fileNamePatternWCS
77          + " for the active file");
78  
79      if (timeBasedTriggering == null) {
80        timeBasedTriggering = new DefaultTimeBasedFileNamingAndTriggeringPolicy<E>();
81      }
82      timeBasedTriggering.setContext(context);
83      timeBasedTriggering.setTimeBasedRollingPolicy(this);
84      timeBasedTriggering.start();
85  
86      // the maxHistory property is given to TimeBasedRollingPolicy instead of to
87      // the TimeBasedFileNamingAndTriggeringPolicy. This makes it more convenient
88      // for the user at the cost of inconsistency at the level of this code.
89      if (maxHistory != NO_DELETE_HISTORY) {
90        archiveRemover = timeBasedTriggering.getArchiveRemover();
91        archiveRemover.setMaxHistory(maxHistory);
92      }
93    }
94  
95    public void setTimeBasedFileNamingAndTriggeringPolicy(
96        TimeBasedFileNamingAndTriggeringPolicy<E> timeBasedTriggering) {
97      this.timeBasedTriggering = timeBasedTriggering;
98    }
99  
100   public TimeBasedFileNamingAndTriggeringPolicy<E> getTimeBasedFileNamingAndTriggeringPolicy() {
101     return timeBasedTriggering;
102   }
103 
104   static public String computeFileNameStr_WCS(String fileNamePatternStr,
105       CompressionMode compressionMode) {
106     int len = fileNamePatternStr.length();
107     switch (compressionMode) {
108     case GZ:
109       return fileNamePatternStr.substring(0, len - 3);
110     case ZIP:
111       return fileNamePatternStr.substring(0, len - 4);
112     case NONE:
113       return fileNamePatternStr;
114     }
115     throw new IllegalStateException("Execution should not reach this point");
116   }
117 
118   public void rollover() throws RolloverFailure {
119 
120     // when rollover is called the elapsed period's file has
121     // been already closed. This is a working assumption of this method.
122 
123     String elapsedPeriodsFileName = timeBasedTriggering
124         .getElapsedPeriodsFileName();
125 
126     if (compressionMode == CompressionMode.NONE) {
127       if (getParentsRawFileProperty() != null) {
128         renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName);
129       }
130     } else {
131       if (getParentsRawFileProperty() == null) {
132         future = asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName);
133       } else {
134         future = renamedRawAndAsyncCompress(elapsedPeriodsFileName);
135       }
136     }
137 
138     if (archiveRemover != null) {
139       archiveRemover.clean(new Date(timeBasedTriggering.getCurrentTime()));
140     }
141   }
142 
143   Future asyncCompress(String nameOfFile2Compress, String nameOfCompressedFile)
144       throws RolloverFailure {
145     AsynchronousCompressor ac = new AsynchronousCompressor(compressor);
146     return ac.compressAsynchronously(nameOfFile2Compress, nameOfCompressedFile);
147   }
148 
149   Future renamedRawAndAsyncCompress(String nameOfCompressedFile)
150       throws RolloverFailure {
151     String parentsRawFile = getParentsRawFileProperty();
152     String tmpTarget = parentsRawFile + System.nanoTime() + ".tmp";
153     renameUtil.rename(parentsRawFile, tmpTarget);
154     return asyncCompress(tmpTarget, nameOfCompressedFile);
155   }
156 
157   /**
158    * 
159    * The active log file is determined by the value of the parent's filename
160    * option. However, in case the file name is left blank, then, the active log
161    * file equals the file name for the current period as computed by the
162    * <b>FileNamePattern</b> option.
163    * 
164    * <p>The RollingPolicy must know whether it is responsible for changing the
165    * name of the active file or not. If the active file name is set by the user
166    * via the configuration file, then the RollingPolicy must let it like it is.
167    * If the user does not specify an active file name, then the RollingPolicy
168    * generates one.
169    * 
170    * <p> To be sure that the file name used by the parent class has been
171    * generated by the RollingPolicy and not specified by the user, we keep track
172    * of the last generated name object and compare its reference to the parent
173    * file name. If they match, then the RollingPolicy knows it's responsible for
174    * the change of the file name.
175    * 
176    */
177   public String getActiveFileName() {
178     String parentsRawFileProperty = getParentsRawFileProperty();
179     if (parentsRawFileProperty != null) {
180       return parentsRawFileProperty;
181     } else {
182       return timeBasedTriggering
183           .getCurrentPeriodsFileNameWithoutCompressionSuffix();
184     }
185   }
186 
187   public boolean isTriggeringEvent(File activeFile, final E event) {
188     return timeBasedTriggering.isTriggeringEvent(activeFile, event);
189   }
190 
191   /**
192    * Get the number of archive files to keep.
193    * 
194    * @return number of archive files to keep
195    */
196   public int getMaxHistory() {
197     return maxHistory;
198   }
199 
200   /**
201    * Set the maximum number of archive files to keep.
202    * 
203    * @param maxHistory
204    *                number of archive files to keep
205    */
206   public void setMaxHistory(int maxHistory) {
207     this.maxHistory = maxHistory;
208   }
209 
210   @Override
211   public String toString() {
212     return "c.q.l.core.rolling.TimeBasedRollingPolicy";
213   }
214 }