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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.log4j;
20 import java.io.IOException;
21 import java.io.InterruptedIOException;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.Writer;
26 import org.apache.log4j.helpers.LogLog;
27 import org.apache.log4j.helpers.QuietWriter;
28 import org.apache.log4j.spi.ErrorHandler;
29 import org.apache.log4j.spi.LoggingEvent;
31 // Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de>
35 WriterAppender appends log events to a {@link java.io.Writer} or an
36 {@link java.io.OutputStream} depending on the user's choice.
38 @author Ceki Gülcü
40 public class WriterAppender extends AppenderSkeleton {
44 Immediate flush means that the underlying writer or output stream
45 will be flushed at the end of each append operation unless shouldFlush()
46 is overridden. Immediate
47 flush is slower but ensures that each append request is actually
48 written. If <code>immediateFlush</code> is set to
49 <code>false</code>, then there is a good chance that the last few
50 logs events are not actually written to persistent media if and
51 when the application crashes.
53 <p>The <code>immediateFlush</code> variable is set to
54 <code>true</code> by default.
57 protected boolean immediateFlush = true;
60 The encoding to use when writing. <p>The
61 <code>encoding</code> variable is set to <code>null</null> by
62 default which results in the utilization of the system's default
64 protected String encoding;
67 This is the {@link QuietWriter quietWriter} where we will write
70 protected QuietWriter qw;
74 This default constructor does nothing. */
80 Instantiate a WriterAppender and set the output destination to a
81 new {@link OutputStreamWriter} initialized with <code>os</code>
82 as its {@link OutputStream}. */
84 WriterAppender(Layout layout, OutputStream os) {
85 this(layout, new OutputStreamWriter(os));
89 Instantiate a WriterAppender and set the output destination to
92 <p>The <code>writer</code> must have been previously opened by
95 WriterAppender(Layout layout, Writer writer) {
97 this.setWriter(writer);
101 If the <b>ImmediateFlush</b> option is set to
102 <code>true</code>, the appender will flush at the end of each
103 write. This is the default behavior. If the option is set to
104 <code>false</code>, then the underlying stream can defer writing
105 to physical medium to a later time.
107 <p>Avoiding the flush operation at the end of each append results in
108 a performance gain of 10 to 20 percent. However, there is safety
109 tradeoff involved in skipping flushing. Indeed, when flushing is
110 skipped, then it is likely that the last few log events will not
111 be recorded on disk when the application exits. This is a high
112 price to pay even for a 20% performance gain.
115 void setImmediateFlush(boolean value) {
116 immediateFlush = value;
120 Returns value of the <b>ImmediateFlush</b> option.
123 boolean getImmediateFlush() {
124 return immediateFlush;
131 void activateOptions() {
136 This method is called by the {@link AppenderSkeleton#doAppend}
139 <p>If the output stream exists and is writable then write a log
140 statement to the output stream. Otherwise, write a single warning
141 message to <code>System.err</code>.
143 <p>The format of the output will depend on this appender's
148 void append(LoggingEvent event) {
150 // Reminder: the nesting of calls is:
156 // - checkEntryConditions();
159 if(!checkEntryConditions()) {
166 This method determines if there is a sense in attempting to append.
168 <p>It checks whether there is a set output target and also if
169 there is a set layout. If these checks fail, then the boolean
170 value <code>false</code> is returned. */
172 boolean checkEntryConditions() {
174 LogLog.warn("Not allowed to write to a closed appender.");
178 if(this.qw == null) {
179 errorHandler.error("No output stream or file set for the appender named ["+
184 if(this.layout == null) {
185 errorHandler.error("No layout set for the appender named ["+ name+"].");
193 Close this appender instance. The underlying stream or writer is
196 <p>Closed appenders cannot be reused.
212 * Close the underlying {@link java.io.Writer}.
214 protected void closeWriter() {
218 } catch(IOException e) {
219 if (e instanceof InterruptedIOException) {
220 Thread.currentThread().interrupt();
222 // There is do need to invoke an error handler at this late
224 LogLog.error("Could not close " + qw, e);
230 Returns an OutputStreamWriter when passed an OutputStream. The
231 encoding used will depend on the value of the
232 <code>encoding</code> property. If the encoding value is
233 specified incorrectly the writer will be opened using the default
234 system encoding (an error message will be printed to the loglog. */
236 OutputStreamWriter createWriter(OutputStream os) {
237 OutputStreamWriter retval = null;
239 String enc = getEncoding();
242 retval = new OutputStreamWriter(os, enc);
243 } catch(IOException e) {
244 if (e instanceof InterruptedIOException) {
245 Thread.currentThread().interrupt();
247 LogLog.warn("Error initializing output writer.");
248 LogLog.warn("Unsupported encoding?");
252 retval = new OutputStreamWriter(os);
257 public String getEncoding() {
261 public void setEncoding(String value) {
269 Set the {@link ErrorHandler} for this WriterAppender and also the
270 underlying {@link QuietWriter} if any. */
271 public synchronized void setErrorHandler(ErrorHandler eh) {
273 LogLog.warn("You have tried to set a null error-handler.");
275 this.errorHandler = eh;
276 if(this.qw != null) {
277 this.qw.setErrorHandler(eh);
283 <p>Sets the Writer where the log output will go. The
284 specified Writer must be opened by the user and be
287 <p>The <code>java.io.Writer</code> will be closed when the
288 appender instance is closed.
291 <p><b>WARNING:</b> Logging to an unopened Writer will fail.
293 @param writer An already opened Writer. */
294 public synchronized void setWriter(Writer writer) {
296 this.qw = new QuietWriter(writer, errorHandler);
297 //this.tp = new TracerPrintWriter(qw);
303 Actual writing occurs here.
305 <p>Most subclasses of <code>WriterAppender</code> will need to
306 override this method.
310 void subAppend(LoggingEvent event) {
311 this.qw.write(this.layout.format(event));
313 if(layout.ignoresThrowable()) {
314 String[] s = event.getThrowableStrRep();
317 for(int i = 0; i < len; i++) {
319 this.qw.write(Layout.LINE_SEP);
324 if(shouldFlush(event)) {
332 The WriterAppender requires a layout. Hence, this method returns
336 boolean requiresLayout() {
341 Clear internal references to the writer and other variables.
343 Subclasses can override this method for an alternate closing
354 Write a footer as produced by the embedded layout's {@link
355 Layout#getFooter} method. */
359 String f = layout.getFooter();
360 if(f != null && this.qw != null) {
368 Write a header as produced by the embedded layout's {@link
369 Layout#getHeader} method. */
373 String h = layout.getHeader();
374 if(h != null && this.qw != null) {
381 * Determines whether the writer should be flushed after
382 * this event is written.
386 protected boolean shouldFlush(final LoggingEvent event) {
387 return immediateFlush;