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