JAL-3048 test updated for AlignExportSettings changes
[jalview.git] / srcjar / org / apache / log4j / WriterAppender.java
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.InterruptedIOException;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.Writer;
25
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;
30
31 // Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de>
32 //              Ben Sandee
33
34 /**
35    WriterAppender appends log events to a {@link java.io.Writer} or an
36    {@link java.io.OutputStream} depending on the user's choice.
37
38    @author Ceki G&uuml;lc&uuml;
39    @since 1.1 */
40 public class WriterAppender extends AppenderSkeleton {
41
42
43   /**
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.
52
53      <p>The <code>immediateFlush</code> variable is set to
54      <code>true</code> by default.
55
56   */
57   protected boolean immediateFlush = true;
58
59   /**
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
63      encoding.  */
64   protected String encoding;
65
66   /**
67      This is the {@link QuietWriter quietWriter} where we will write
68      to.
69   */
70   protected QuietWriter qw;
71
72
73   /**
74      This default constructor does nothing.  */
75   public
76   WriterAppender() {
77   }
78
79   /**
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}.  */
83   public
84   WriterAppender(Layout layout, OutputStream os) {
85     this(layout, new OutputStreamWriter(os));
86   }
87
88   /**
89      Instantiate a WriterAppender and set the output destination to
90      <code>writer</code>.
91
92      <p>The <code>writer</code> must have been previously opened by
93      the user.  */
94   public
95   WriterAppender(Layout layout, Writer writer) {
96     this.layout = layout;
97     this.setWriter(writer);
98   }
99
100   /**
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.
106
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.
113    */
114   public
115   void setImmediateFlush(boolean value) {
116     immediateFlush = value;
117   }
118
119   /**
120      Returns value of the <b>ImmediateFlush</b> option.
121    */
122   public
123   boolean getImmediateFlush() {
124     return immediateFlush;
125   }
126
127   /**
128      Does nothing.
129   */
130   public
131   void activateOptions() {
132   }
133
134
135   /**
136      This method is called by the {@link AppenderSkeleton#doAppend}
137      method.
138
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>.
142
143      <p>The format of the output will depend on this appender's
144      layout.
145
146   */
147   public
148   void append(LoggingEvent event) {
149
150     // Reminder: the nesting of calls is:
151     //
152     //    doAppend()
153     //      - check threshold
154     //      - filter
155     //      - append();
156     //        - checkEntryConditions();
157     //        - subAppend();
158
159     if(!checkEntryConditions()) {
160       return;
161     }
162     subAppend(event);
163    }
164
165   /**
166      This method determines if there is a sense in attempting to append.
167
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. */
171   protected
172   boolean checkEntryConditions() {
173     if(this.closed) {
174       LogLog.warn("Not allowed to write to a closed appender.");
175       return false;
176     }
177
178     if(this.qw == null) {
179       errorHandler.error("No output stream or file set for the appender named ["+
180                         name+"].");
181       return false;
182     }
183
184     if(this.layout == null) {
185       errorHandler.error("No layout set for the appender named ["+ name+"].");
186       return false;
187     }
188     return true;
189   }
190
191
192   /**
193      Close this appender instance. The underlying stream or writer is
194      also closed.
195
196      <p>Closed appenders cannot be reused.
197
198      @see #setWriter
199      @since 0.8.4 */
200   public
201   synchronized
202   void close() {
203     if(this.closed) {
204         return;
205     }
206     this.closed = true;
207     writeFooter();
208     reset();
209   }
210
211   /**
212    * Close the underlying {@link java.io.Writer}.
213    * */
214   protected void closeWriter() {
215     if(qw != null) {
216       try {
217         qw.close();
218       } catch(IOException e) {
219           if (e instanceof InterruptedIOException) {
220               Thread.currentThread().interrupt();
221           }
222         // There is do need to invoke an error handler at this late
223         // stage.
224         LogLog.error("Could not close " + qw, e);
225       }
226     }
227   }
228
229   /**
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.  */
235   protected
236   OutputStreamWriter createWriter(OutputStream os) {
237     OutputStreamWriter retval = null;
238
239     String enc = getEncoding();
240     if(enc != null) {
241       try {
242         retval = new OutputStreamWriter(os, enc);
243       } catch(IOException e) {
244           if (e instanceof InterruptedIOException) {
245               Thread.currentThread().interrupt();
246           }
247               LogLog.warn("Error initializing output writer.");
248               LogLog.warn("Unsupported encoding?");
249       }
250     }
251     if(retval == null) {
252       retval = new OutputStreamWriter(os);
253     }
254     return retval;
255   }
256
257   public String getEncoding() {
258     return encoding;
259   }
260
261   public void setEncoding(String value) {
262     encoding = value;
263   }
264
265
266
267
268   /**
269      Set the {@link ErrorHandler} for this WriterAppender and also the
270      underlying {@link QuietWriter} if any. */
271   public synchronized void setErrorHandler(ErrorHandler eh) {
272     if(eh == null) {
273       LogLog.warn("You have tried to set a null error-handler.");
274     } else {
275       this.errorHandler = eh;
276       if(this.qw != null) {
277         this.qw.setErrorHandler(eh);
278       }
279     }
280   }
281
282   /**
283     <p>Sets the Writer where the log output will go. The
284     specified Writer must be opened by the user and be
285     writable.
286
287     <p>The <code>java.io.Writer</code> will be closed when the
288     appender instance is closed.
289
290
291     <p><b>WARNING:</b> Logging to an unopened Writer will fail.
292     <p>
293     @param writer An already opened Writer.  */
294   public synchronized void setWriter(Writer writer) {
295     reset();
296     this.qw = new QuietWriter(writer, errorHandler);
297     //this.tp = new TracerPrintWriter(qw);
298     writeHeader();
299   }
300
301
302   /**
303      Actual writing occurs here.
304
305      <p>Most subclasses of <code>WriterAppender</code> will need to
306      override this method.
307
308      @since 0.9.0 */
309   protected
310   void subAppend(LoggingEvent event) {
311     this.qw.write(this.layout.format(event));
312
313     if(layout.ignoresThrowable()) {
314       String[] s = event.getThrowableStrRep();
315       if (s != null) {
316         int len = s.length;
317         for(int i = 0; i < len; i++) {
318           this.qw.write(s[i]);
319           this.qw.write(Layout.LINE_SEP);
320         }
321       }
322     }
323
324     if(shouldFlush(event)) {
325       this.qw.flush();
326     }
327   }
328
329
330
331   /**
332      The WriterAppender requires a layout. Hence, this method returns
333      <code>true</code>.
334   */
335   public
336   boolean requiresLayout() {
337     return true;
338   }
339
340   /**
341      Clear internal references to the writer and other variables.
342
343      Subclasses can override this method for an alternate closing
344      behavior.  */
345   protected
346   void reset() {
347     closeWriter();
348     this.qw = null;
349     //this.tp = null;
350   }
351
352
353   /**
354      Write a footer as produced by the embedded layout's {@link
355      Layout#getFooter} method.  */
356   protected
357   void writeFooter() {
358     if(layout != null) {
359       String f = layout.getFooter();
360       if(f != null && this.qw != null) {
361         this.qw.write(f);
362         this.qw.flush();
363       }
364     }
365   }
366
367   /**
368      Write a header as produced by the embedded layout's {@link
369      Layout#getHeader} method.  */
370   protected
371   void writeHeader() {
372     if(layout != null) {
373       String h = layout.getHeader();
374       if(h != null && this.qw != null) {
375         this.qw.write(h);
376     }
377     }
378   }
379   
380   /**
381    * Determines whether the writer should be flushed after
382    * this event is written.
383    * 
384    * @since 1.2.16
385    */
386   protected boolean shouldFlush(final LoggingEvent event) {
387      return immediateFlush;
388   }
389 }