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&uuml;lc&uuml;
    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 }