View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  
19  
20  package org.apache.log4j;
21  
22  import java.io.IOException;
23  import java.io.Writer;
24  import java.io.File;
25  import java.io.InterruptedIOException;
26  
27  import org.apache.log4j.helpers.OptionConverter;
28  import org.apache.log4j.helpers.LogLog;
29  import org.apache.log4j.helpers.CountingQuietWriter;
30  import org.apache.log4j.spi.LoggingEvent;
31  
32  /**
33     RollingFileAppender extends FileAppender to backup the log files when
34     they reach a certain size.
35     
36     The log4j extras companion includes alternatives which should be considered
37     for new deployments and which are discussed in the documentation
38     for org.apache.log4j.rolling.RollingFileAppender.
39     
40  
41     @author Heinz Richter
42     @author Ceki Gülcü
43  
44  */
45  public class RollingFileAppender extends FileAppender {
46  
47    /**
48       The default maximum file size is 10MB.
49    */
50    protected long maxFileSize = 10*1024*1024;
51  
52    /**
53       There is one backup file by default.
54     */
55    protected int  maxBackupIndex  = 1;
56  
57    private long nextRollover = 0;
58  
59    /**
60       The default constructor simply calls its {@link
61       FileAppender#FileAppender parents constructor}.  */
62    public
63    RollingFileAppender() {
64      super();
65    }
66  
67    /**
68      Instantiate a RollingFileAppender and open the file designated by
69      <code>filename</code>. The opened filename will become the ouput
70      destination for this appender.
71  
72      <p>If the <code>append</code> parameter is true, the file will be
73      appended to. Otherwise, the file desginated by
74      <code>filename</code> will be truncated before being opened.
75    */
76    public
77    RollingFileAppender(Layout layout, String filename, boolean append)
78                                        throws IOException {
79      super(layout, filename, append);
80    }
81  
82    /**
83       Instantiate a FileAppender and open the file designated by
84      <code>filename</code>. The opened filename will become the output
85      destination for this appender.
86  
87      <p>The file will be appended to.  */
88    public
89    RollingFileAppender(Layout layout, String filename) throws IOException {
90      super(layout, filename);
91    }
92  
93    /**
94       Returns the value of the <b>MaxBackupIndex</b> option.
95     */
96    public
97    int getMaxBackupIndex() {
98      return maxBackupIndex;
99    }
100 
101  /**
102     Get the maximum size that the output file is allowed to reach
103     before being rolled over to backup files.
104 
105     @since 1.1
106  */
107   public
108   long getMaximumFileSize() {
109     return maxFileSize;
110   }
111 
112   /**
113      Implements the usual roll over behaviour.
114 
115      <p>If <code>MaxBackupIndex</code> is positive, then files
116      {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>}
117      are renamed to {<code>File.2</code>, ...,
118      <code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is
119      renamed <code>File.1</code> and closed. A new <code>File</code> is
120      created to receive further log output.
121 
122      <p>If <code>MaxBackupIndex</code> is equal to zero, then the
123      <code>File</code> is truncated with no backup files created.
124 
125    */
126   public // synchronization not necessary since doAppend is alreasy synched
127   void rollOver() {
128     File target;
129     File file;
130 
131     if (qw != null) {
132         long size = ((CountingQuietWriter) qw).getCount();
133         LogLog.debug("rolling over count=" + size);
134         //   if operation fails, do not roll again until
135         //      maxFileSize more bytes are written
136         nextRollover = size + maxFileSize;
137     }
138     LogLog.debug("maxBackupIndex="+maxBackupIndex);
139 
140     boolean renameSucceeded = true;
141     // If maxBackups <= 0, then there is no file renaming to be done.
142     if(maxBackupIndex > 0) {
143       // Delete the oldest file, to keep Windows happy.
144       file = new File(fileName + '.' + maxBackupIndex);
145       if (file.exists())
146        renameSucceeded = file.delete();
147 
148       // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
149       for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
150 	file = new File(fileName + "." + i);
151 	if (file.exists()) {
152 	  target = new File(fileName + '.' + (i + 1));
153 	  LogLog.debug("Renaming file " + file + " to " + target);
154 	  renameSucceeded = file.renameTo(target);
155 	}
156       }
157 
158     if(renameSucceeded) {
159       // Rename fileName to fileName.1
160       target = new File(fileName + "." + 1);
161 
162       this.closeFile(); // keep windows happy.
163 
164       file = new File(fileName);
165       LogLog.debug("Renaming file " + file + " to " + target);
166       renameSucceeded = file.renameTo(target);
167       //
168       //   if file rename failed, reopen file with append = true
169       //
170       if (!renameSucceeded) {
171           try {
172             this.setFile(fileName, true, bufferedIO, bufferSize);
173           }
174           catch(IOException e) {
175               if (e instanceof InterruptedIOException) {
176                   Thread.currentThread().interrupt();
177               }
178               LogLog.error("setFile("+fileName+", true) call failed.", e);
179           }
180       }
181     }
182     }
183 
184     //
185     //   if all renames were successful, then
186     //
187     if (renameSucceeded) {
188     try {
189       // This will also close the file. This is OK since multiple
190       // close operations are safe.
191       this.setFile(fileName, false, bufferedIO, bufferSize);
192       nextRollover = 0;
193     }
194     catch(IOException e) {
195         if (e instanceof InterruptedIOException) {
196             Thread.currentThread().interrupt();
197         }
198         LogLog.error("setFile("+fileName+", false) call failed.", e);
199     }
200     }
201   }
202 
203   public
204   synchronized
205   void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
206                                                                  throws IOException {
207     super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
208     if(append) {
209       File f = new File(fileName);
210       ((CountingQuietWriter) qw).setCount(f.length());
211     }
212   }
213 
214 
215   /**
216      Set the maximum number of backup files to keep around.
217 
218      <p>The <b>MaxBackupIndex</b> option determines how many backup
219      files are kept before the oldest is erased. This option takes
220      a positive integer value. If set to zero, then there will be no
221      backup files and the log file will be truncated when it reaches
222      <code>MaxFileSize</code>.
223    */
224   public
225   void setMaxBackupIndex(int maxBackups) {
226     this.maxBackupIndex = maxBackups;
227   }
228 
229   /**
230      Set the maximum size that the output file is allowed to reach
231      before being rolled over to backup files.
232 
233      <p>This method is equivalent to {@link #setMaxFileSize} except
234      that it is required for differentiating the setter taking a
235      <code>long</code> argument from the setter taking a
236      <code>String</code> argument by the JavaBeans {@link
237      java.beans.Introspector Introspector}.
238 
239      @see #setMaxFileSize(String)
240  */
241   public
242   void setMaximumFileSize(long maxFileSize) {
243     this.maxFileSize = maxFileSize;
244   }
245 
246 
247   /**
248      Set the maximum size that the output file is allowed to reach
249      before being rolled over to backup files.
250 
251      <p>In configuration files, the <b>MaxFileSize</b> option takes an
252      long integer in the range 0 - 2^63. You can specify the value
253      with the suffixes "KB", "MB" or "GB" so that the integer is
254      interpreted being expressed respectively in kilobytes, megabytes
255      or gigabytes. For example, the value "10KB" will be interpreted
256      as 10240.
257    */
258   public
259   void setMaxFileSize(String value) {
260     maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
261   }
262 
263   protected
264   void setQWForFiles(Writer writer) {
265      this.qw = new CountingQuietWriter(writer, errorHandler);
266   }
267 
268   /**
269      This method differentiates RollingFileAppender from its super
270      class.
271 
272      @since 0.9.0
273   */
274   protected
275   void subAppend(LoggingEvent event) {
276     super.subAppend(event);
277     if(fileName != null && qw != null) {
278         long size = ((CountingQuietWriter) qw).getCount();
279         if (size >= maxFileSize && size >= nextRollover) {
280             rollOver();
281         }
282     }
283    }
284 }