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