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