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