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