KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > log4j > WriterAppender


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 JavaDoc;
20 import java.io.Writer JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.io.OutputStreamWriter JavaDoc;
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 opening an InputStream. <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 JavaDoc 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 JavaDoc os) {
82     this(layout, new OutputStreamWriter JavaDoc(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 JavaDoc 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 JavaDoc 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 JavaDoc createWriter(OutputStream JavaDoc os) {
230     OutputStreamWriter JavaDoc retval = null;
231
232     String JavaDoc enc = getEncoding();
233     if(enc != null) {
234       try {
235     retval = new OutputStreamWriter JavaDoc(os, enc);
236       } catch(IOException JavaDoc e) {
237     LogLog.warn("Error initializing output writer.");
238     LogLog.warn("Unsupported encoding?");
239       }
240     }
241     if(retval == null) {
242       retval = new OutputStreamWriter JavaDoc(os);
243     }
244     return retval;
245   }
246
247   public String JavaDoc getEncoding() {
248     return encoding;
249   }
250
251   public void setEncoding(String JavaDoc 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 JavaDoc 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 JavaDoc[] 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 JavaDoc 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 JavaDoc h = layout.getHeader();
364       if(h != null && this.qw != null)
365     this.qw.write(h);
366     }
367   }
368 }
369
Popular Tags