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  package org.apache.log4j;
    19
    20  import java.io.BufferedWriter;
    21  import java.io.File;
    22  import java.io.FileNotFoundException;
    23  import java.io.FileOutputStream;
    24  import java.io.IOException;
    25  import java.io.InterruptedIOException;
    26  import java.io.Writer;
    27
    28  import org.apache.log4j.helpers.LogLog;
    29  import org.apache.log4j.helpers.QuietWriter;
    30  import org.apache.log4j.spi.ErrorCode;
    31
    32  // Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de>
    33  //              Ben Sandee
    34
    35  /**
    36   *  FileAppender appends log events to a file.
    37   *
    38   *  <p>Support for <code>java.io.Writer</code> and console appending
    39   *  has been deprecated and then removed. See the replacement
    40   *  solutions: {@link WriterAppender} and {@link ConsoleAppender}.
    41   *
    42   * @author Ceki G&uuml;lc&uuml;
    43   * */
    44  public class FileAppender extends WriterAppender {
    45
    46    /** Controls file truncatation. The default value for this variable
    47     * is <code>true</code>, meaning that by default a
    48     * <code>FileAppender</code> will append to an existing file and not
    49     * truncate it.
    50     *
    51     * <p>This option is meaningful only if the FileAppender opens the
    52     * file.
    53     */
    54    protected boolean fileAppend = true;
    55
    56    /**
    57       The name of the log file. */
    58    protected String fileName = null;
    59
    60    /**
    61       Do we do bufferedIO? */
    62    protected boolean bufferedIO = false;
    63
    64    /**
    65     * Determines the size of IO buffer be. Default is 8K.
    66     */
    67    protected int bufferSize = 8*1024;
    68
    69
    70    /**
    71       The default constructor does not do anything.
    72    */
    73    public
    74    FileAppender() {
    75    }
    76
    77    /**
    78      Instantiate a <code>FileAppender</code> and open the file
    79      designated by <code>filename</code>. The opened filename will
    80      become the output destination for this appender.
    81
    82      <p>If the <code>append</code> parameter is true, the file will be
    83      appended to. Otherwise, the file designated by
    84      <code>filename</code> will be truncated before being opened.
    85
    86      <p>If the <code>bufferedIO</code> parameter is <code>true</code>,
    87      then buffered IO will be used to write to the output file.
    88
    89    */
    90    public
    91    FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,
    92  	       int bufferSize) throws IOException {
    93      this.layout = layout;
    94      this.setFile(filename, append, bufferedIO, bufferSize);
    95    }
    96
    97    /**
    98      Instantiate a FileAppender and open the file designated by
    99      <code>filename</code>. The opened filename will become the output
    100     destination for this appender.
    101
    102     <p>If the <code>append</code> parameter is true, the file will be
    103     appended to. Otherwise, the file designated by
    104     <code>filename</code> will be truncated before being opened.
    105   */
    106   public
    107   FileAppender(Layout layout, String filename, boolean append)
    108                                                              throws IOException {
    109     this.layout = layout;
    110     this.setFile(filename, append, false, bufferSize);
    111   }
    112
    113   /**
    114      Instantiate a FileAppender and open the file designated by
    115     <code>filename</code>. The opened filename will become the output
    116     destination for this appender.
    117
    118     <p>The file will be appended to.  */
    119   public
    120   FileAppender(Layout layout, String filename) throws IOException {
    121     this(layout, filename, true);
    122   }
    123
    124   /**
    125      The <b>File</b> property takes a string value which should be the
    126      name of the file to append to.
    127
    128      <p><font color="#DD0044"><b>Note that the special values
    129      "System.out" or "System.err" are no longer honored.</b></font>
    130
    131      <p>Note: Actual opening of the file is made when {@link
    132      #activateOptions} is called, not when the options are set.  */
    133   public void setFile(String file) {
    134     // Trim spaces from both ends. The users probably does not want
    135     // trailing spaces in file names.
    136     String val = file.trim();
    137     fileName = val;
    138   }
    139
    140   /**
    141       Returns the value of the <b>Append</b> option.
    142    */
    143   public
    144   boolean getAppend() {
    145     return fileAppend;
    146   }
    147
    148
    149   /** Returns the value of the <b>File</b> option. */
    150   public
    151   String getFile() {
    152     return fileName;
    153   }
    154
    155   /**
    156      If the value of <b>File</b> is not <code>null</code>, then {@link
    157      #setFile} is called with the values of <b>File</b>  and
    158      <b>Append</b> properties.
    159
    160      @since 0.8.1 */
    161   public
    162   void activateOptions() {
    163     if(fileName != null) {
    164       try {
    165 	setFile(fileName, fileAppend, bufferedIO, bufferSize);
    166       }
    167       catch(java.io.IOException e) {
    168 	errorHandler.error("setFile("+fileName+","+fileAppend+") call failed.",
    169 			   e, ErrorCode.FILE_OPEN_FAILURE);
    170       }
    171     } else {
    172       //LogLog.error("File option not set for appender ["+name+"].");
    173       LogLog.warn("File option not set for appender ["+name+"].");
    174       LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
    175     }
    176   }
    177
    178  /**
    179      Closes the previously opened file.
    180   */
    181   protected
    182   void closeFile() {
    183     if(this.qw != null) {
    184       try {
    185 	this.qw.close();
    186       }
    187       catch(java.io.IOException e) {
    188           if (e instanceof InterruptedIOException) {
    189               Thread.currentThread().interrupt();
    190           }
    191 	// Exceptionally, it does not make sense to delegate to an
    192 	// ErrorHandler. Since a closed appender is basically dead.
    193 	LogLog.error("Could not close " + qw, e);
    194       }
    195     }
    196   }
    197
    198   /**
    199      Get the value of the <b>BufferedIO</b> option.
    200
    201      <p>BufferedIO will significatnly increase performance on heavily
    202      loaded systems.
    203
    204   */
    205   public
    206   boolean getBufferedIO() {
    207     return this.bufferedIO;
    208   }
    209
    210
    211   /**
    212      Get the size of the IO buffer.
    213   */
    214   public
    215   int getBufferSize() {
    216     return this.bufferSize;
    217   }
    218
    219
    220
    221   /**
    222      The <b>Append</b> option takes a boolean value. It is set to
    223      <code>true</code> by default. If true, then <code>File</code>
    224      will be opened in append mode by {@link #setFile setFile} (see
    225      above). Otherwise, {@link #setFile setFile} will open
    226      <code>File</code> in truncate mode.
    227
    228      <p>Note: Actual opening of the file is made when {@link
    229      #activateOptions} is called, not when the options are set.
    230    */
    231   public
    232   void setAppend(boolean flag) {
    233     fileAppend = flag;
    234   }
    235
    236   /**
    237      The <b>BufferedIO</b> option takes a boolean value. It is set to
    238      <code>false</code> by default. If true, then <code>File</code>
    239      will be opened and the resulting {@link java.io.Writer} wrapped
    240      around a {@link BufferedWriter}.
    241
    242      BufferedIO will significatnly increase performance on heavily
    243      loaded systems.
    244
    245   */
    246   public
    247   void setBufferedIO(boolean bufferedIO) {
    248     this.bufferedIO = bufferedIO;
    249     if(bufferedIO) {
    250       immediateFlush = false;
    251     }
    252   }
    253
    254
    255   /**
    256      Set the size of the IO buffer.
    257   */
    258   public
    259   void setBufferSize(int bufferSize) {
    260     this.bufferSize = bufferSize;
    261   }
    262
    263   /**
    264     <p>Sets and <i>opens</i> the file where the log output will
    265     go. The specified file must be writable.
    266
    267     <p>If there was already an opened file, then the previous file
    268     is closed first.
    269
    270     <p><b>Do not use this method directly. To configure a FileAppender
    271     or one of its subclasses, set its properties one by one and then
    272     call activateOptions.</b>
    273
    274     @param fileName The path to the log file.
    275     @param append   If true will append to fileName. Otherwise will
    276         truncate fileName.  */
    277   public
    278   synchronized
    279   void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
    280                                                             throws IOException {
    281     LogLog.debug("setFile called: "+fileName+", "+append);
    282
    283     // It does not make sense to have immediate flush and bufferedIO.
    284     if(bufferedIO) {
    285       setImmediateFlush(false);
    286     }
    287
    288     reset();
    289     FileOutputStream ostream = null;
    290     try {
    291           //
    292           //   attempt to create file
    293           //
    294           ostream = new FileOutputStream(fileName, append);
    295     } catch(FileNotFoundException ex) {
    296           //
    297           //   if parent directory does not exist then
    298           //      attempt to create it and try to create file
    299           //      see bug 9150
    300           //
    301           String parentName = new File(fileName).getParent();
    302           if (parentName != null) {
    303              File parentDir = new File(parentName);
    304              if(!parentDir.exists() && parentDir.mkdirs()) {
    305                 ostream = new FileOutputStream(fileName, append);
    306              } else {
    307                 throw ex;
    308              }
    309           } else {
    310              throw ex;
    311           }
    312     }
    313     Writer fw = createWriter(ostream);
    314     if(bufferedIO) {
    315       fw = new BufferedWriter(fw, bufferSize);
    316     }
    317     this.setQWForFiles(fw);
    318     this.fileName = fileName;
    319     this.fileAppend = append;
    320     this.bufferedIO = bufferedIO;
    321     this.bufferSize = bufferSize;
    322     writeHeader();
    323     LogLog.debug("setFile ended");
    324   }
    325
    326
    327   /**
    328      Sets the quiet writer being used.
    329
    330      This method is overriden by {@link RollingFileAppender}.
    331    */
    332   protected
    333   void setQWForFiles(Writer writer) {
    334      this.qw = new QuietWriter(writer, errorHandler);
    335   }
    336
    337
    338   /**
    339      Close any previously opened file and call the parent's
    340      <code>reset</code>.  */
    341   protected
    342   void reset() {
    343     closeFile();
    344     this.fileName = null;
    345     super.reset();
    346   }
    347 }
    348