/ org.apache.log4j / source-bundle / org / apache / log4j / WriterAppender.java
WriterAppender.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.OutputStream;
 22  import java.io.OutputStreamWriter;
 23  
 24  import org.apache.log4j.spi.ErrorHandler;
 25  import org.apache.log4j.spi.LoggingEvent;
 26  import org.apache.log4j.helpers.QuietWriter;
 27  import org.apache.log4j.helpers.LogLog;
 28  
 29  // Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de>
 30  //              Ben Sandee
 31  
 32  /**
 33     WriterAppender appends log events to a {@link java.io.Writer} or an
 34     {@link java.io.OutputStream} depending on the user's choice.
 35  
 36     @author Ceki G&uuml;lc&uuml;
 37     @since 1.1 */
 38  public class WriterAppender extends AppenderSkeleton {
 39  
 40  
 41    /**
 42       Immediate flush means that the underlying writer or output stream
 43       will be flushed at the end of each append operation. Immediate
 44       flush is slower but ensures that each append request is actually
 45       written. If <code>immediateFlush</code> is set to
 46       <code>false</code>, then there is a good chance that the last few
 47       logs events are not actually written to persistent media if and
 48       when the application crashes.
 49  
 50       <p>The <code>immediateFlush</code> variable is set to
 51       <code>true</code> by default.
 52  
 53    */
 54    protected boolean immediateFlush = true;
 55  
 56    /**
 57       The encoding to use when writing.  <p>The
 58       <code>encoding</code> variable is set to <code>null</null> by
 59       default which results in the utilization of the system's default
 60       encoding.  */
 61    protected String encoding;
 62  
 63    /**
 64       This is the {@link QuietWriter quietWriter} where we will write
 65       to.
 66    */
 67    protected QuietWriter qw;
 68  
 69  
 70    /**
 71       This default constructor does nothing.  */
 72    public
 73    WriterAppender() {
 74    }
 75  
 76    /**
 77       Instantiate a WriterAppender and set the output destination to a
 78       new {@link OutputStreamWriter} initialized with <code>os</code>
 79       as its {@link OutputStream}.  */
 80    public
 81    WriterAppender(Layout layout, OutputStream os) {
 82      this(layout, new OutputStreamWriter(os));
 83    }
 84  
 85    /**
 86       Instantiate a WriterAppender and set the output destination to
 87       <code>writer</code>.
 88  
 89       <p>The <code>writer</code> must have been previously opened by
 90       the user.  */
 91    public
 92    WriterAppender(Layout layout, Writer writer) {
 93      this.layout = layout;
 94      this.setWriter(writer);
 95    }
 96  
 97    /**
 98       If the <b>ImmediateFlush</b> option is set to
 99       <code>true</code>, the appender will flush at the end of each
100       write. This is the default behavior. If the option is set to
101       <code>false</code>, then the underlying stream can defer writing
102       to physical medium to a later time.
103  
104       <p>Avoiding the flush operation at the end of each append results in
105       a performance gain of 10 to 20 percent. However, there is safety
106       tradeoff involved in skipping flushing. Indeed, when flushing is
107       skipped, then it is likely that the last few log events will not
108       be recorded on disk when the application exits. This is a high
109       price to pay even for a 20% performance gain.
110     */
111    public
112    void setImmediateFlush(boolean value) {
113      immediateFlush = value;
114    }
115  
116    /**
117       Returns value of the <b>ImmediateFlush</b> option.
118     */
119    public
120    boolean getImmediateFlush() {
121      return immediateFlush;
122    }
123  
124    /**
125       Does nothing.
126    */
127    public
128    void activateOptions() {
129    }
130  
131  
132    /**
133       This method is called by the {@link AppenderSkeleton#doAppend}
134       method.
135  
136       <p>If the output stream exists and is writable then write a log
137       statement to the output stream. Otherwise, write a single warning
138       message to <code>System.err</code>.
139  
140       <p>The format of the output will depend on this appender's
141       layout.
142  
143    */
144    public
145    void append(LoggingEvent event) {
146  
147      // Reminder: the nesting of calls is:
148      //
149      //    doAppend()
150      //      - check threshold
151      //      - filter
152      //      - append();
153      //        - checkEntryConditions();
154      //        - subAppend();
155  
156      if(!checkEntryConditions()) {
157        return;
158      }
159      subAppend(event);
160     }
161  
162    /**
163       This method determines if there is a sense in attempting to append.
164  
165       <p>It checks whether there is a set output target and also if
166       there is a set layout. If these checks fail, then the boolean
167       value <code>false</code> is returned. */
168    protected
169    boolean checkEntryConditions() {
170      if(this.closed) {
171        LogLog.warn("Not allowed to write to a closed appender.");
172        return false;
173      }
174  
175      if(this.qw == null) {
176        errorHandler.error("No output stream or file set for the appender named ["+
177  			name+"].");
178        return false;
179      }
180  
181      if(this.layout == null) {
182        errorHandler.error("No layout set for the appender named ["+ name+"].");
183        return false;
184      }
185      return true;
186    }
187  
188  
189    /**
190       Close this appender instance. The underlying stream or writer is
191       also closed.
192  
193       <p>Closed appenders cannot be reused.
194  
195       @see #setWriter
196       @since 0.8.4 */
197    public
198    synchronized
199    void close() {
200      if(this.closed)
201        return;
202      this.closed = true;
203      writeFooter();
204      reset();
205    }
206  
207    /**
208     * Close the underlying {@link java.io.Writer}.
209     * */
210    protected void closeWriter() {
211      if(qw != null) {
212        try {
213  	qw.close();
214        } catch(IOException e) {
215  	// There is do need to invoke an error handler at this late
216  	// stage.
217  	LogLog.error("Could not close " + qw, e);
218        }
219      }
220    }
221  
222    /**
223       Returns an OutputStreamWriter when passed an OutputStream.  The
224       encoding used will depend on the value of the
225       <code>encoding</code> property.  If the encoding value is
226       specified incorrectly the writer will be opened using the default
227       system encoding (an error message will be printed to the loglog.  */
228    protected
229    OutputStreamWriter createWriter(OutputStream os) {
230      OutputStreamWriter retval = null;
231  
232      String enc = getEncoding();
233      if(enc != null) {
234        try {
235  	retval = new OutputStreamWriter(os, enc);
236        } catch(IOException e) {
237  	LogLog.warn("Error initializing output writer.");
238  	LogLog.warn("Unsupported encoding?");
239        }
240      }
241      if(retval == null) {
242        retval = new OutputStreamWriter(os);
243      }
244      return retval;
245    }
246  
247    public String getEncoding() {
248      return encoding;
249    }
250  
251    public void setEncoding(String value) {
252      encoding = value;
253    }
254  
255  
256  
257  
258    /**
259       Set the {@link ErrorHandler} for this WriterAppender and also the
260       underlying {@link QuietWriter} if any. */
261    public synchronized void setErrorHandler(ErrorHandler eh) {
262      if(eh == null) {
263        LogLog.warn("You have tried to set a null error-handler.");
264      } else {
265        this.errorHandler = eh;
266        if(this.qw != null) {
267  	this.qw.setErrorHandler(eh);
268        }
269      }
270    }
271  
272    /**
273      <p>Sets the Writer where the log output will go. The
274      specified Writer must be opened by the user and be
275      writable.
276  
277      <p>The <code>java.io.Writer</code> will be closed when the
278      appender instance is closed.
279  
280  
281      <p><b>WARNING:</b> Logging to an unopened Writer will fail.
282      <p>
283      @param writer An already opened Writer.  */
284    public synchronized void setWriter(Writer writer) {
285      reset();
286      this.qw = new QuietWriter(writer, errorHandler);
287      //this.tp = new TracerPrintWriter(qw);
288      writeHeader();
289    }
290  
291  
292    /**
293       Actual writing occurs here.
294  
295       <p>Most subclasses of <code>WriterAppender</code> will need to
296       override this method.
297  
298       @since 0.9.0 */
299    protected
300    void subAppend(LoggingEvent event) {
301      this.qw.write(this.layout.format(event));
302  
303      if(layout.ignoresThrowable()) {
304        String[] s = event.getThrowableStrRep();
305        if (s != null) {
306  	int len = s.length;
307  	for(int i = 0; i < len; i++) {
308  	  this.qw.write(s[i]);
309  	  this.qw.write(Layout.LINE_SEP);
310  	}
311        }
312      }
313  
314      if(this.immediateFlush) {
315        this.qw.flush();
316      }
317    }
318  
319  
320  
321    /**
322       The WriterAppender requires a layout. Hence, this method returns
323       <code>true</code>.
324    */
325    public
326    boolean requiresLayout() {
327      return true;
328    }
329  
330    /**
331       Clear internal references to the writer and other variables.
332  
333       Subclasses can override this method for an alternate closing
334       behavior.  */
335    protected
336    void reset() {
337      closeWriter();
338      this.qw = null;
339      //this.tp = null;
340    }
341  
342  
343    /**
344       Write a footer as produced by the embedded layout's {@link
345       Layout#getFooter} method.  */
346    protected
347    void writeFooter() {
348      if(layout != null) {
349        String f = layout.getFooter();
350        if(f != null && this.qw != null) {
351  	this.qw.write(f);
352  	this.qw.flush();
353        }
354      }
355    }
356  
357    /**
358       Write a header as produced by the embedded layout's {@link
359       Layout#getHeader} method.  */
360    protected
361    void writeHeader() {
362      if(layout != null) {
363        String h = layout.getHeader();
364        if(h != null && this.qw != null)
365  	this.qw.write(h);
366      }
367    }
368  }