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