KickJava   Java API By Example, From Geeks To Geeks.

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


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 // Contibutors: Aaron Greenhouse <aarong@cs.cmu.edu>
18
// Thomas Tuft Muller <ttm@online.no>
19

20 package org.apache.log4j;
21
22 import org.apache.log4j.spi.LoggingEvent;
23 import org.apache.log4j.helpers.BoundedFIFO;
24 import org.apache.log4j.spi.AppenderAttachable;
25 import org.apache.log4j.helpers.AppenderAttachableImpl;
26 import org.apache.log4j.helpers.LogLog;
27 import java.util.Enumeration JavaDoc;
28
29 /**
30    The AsyncAppender lets users log events asynchronously. It uses a
31    bounded buffer to store logging events.
32
33    <p>The AsyncAppender will collect the events sent to it and then
34    dispatch them to all the appenders that are attached to it. You can
35    attach multiple appenders to an AsyncAppender.
36
37    <p>The AsyncAppender uses a separate thread to serve the events in
38    its bounded buffer.
39
40    <p>Refer to the results in {@link org.apache.log4j.performance.Logging}
41    for the impact of using this appender.
42
43    <p><b>Important note:</b> The <code>AsyncAppender</code> can only
44    be script configured using the {@link
45    org.apache.log4j.xml.DOMConfigurator}.
46
47    @author Ceki G&uuml;lc&uuml;
48    @since 0.9.1 */

49 public class AsyncAppender extends AppenderSkeleton
50                                             implements AppenderAttachable {
51
52   /** The default buffer size is set to 128 events. */
53   public static final int DEFAULT_BUFFER_SIZE = 128;
54
55   //static Category cat = Category.getInstance(AsyncAppender.class.getName());
56

57   BoundedFIFO bf = new BoundedFIFO(DEFAULT_BUFFER_SIZE);
58
59   AppenderAttachableImpl aai;
60   Dispatcher dispatcher;
61   boolean locationInfo = false;
62
63   boolean interruptedWarningMessage = false;
64
65   public AsyncAppender() {
66     // Note: The dispatcher code assumes that the aai is set once and
67
// for all.
68
aai = new AppenderAttachableImpl();
69     dispatcher = new Dispatcher(bf, this);
70     dispatcher.start();
71   }
72
73
74   public void addAppender(Appender newAppender) {
75     synchronized(aai) {
76       aai.addAppender(newAppender);
77     }
78   }
79
80   public void append(LoggingEvent event) {
81     // Set the NDC and thread name for the calling thread as these
82
// LoggingEvent fields were not set at event creation time.
83
event.getNDC();
84     event.getThreadName();
85     // Get a copy of this thread's MDC.
86
event.getMDCCopy();
87     if(locationInfo) {
88       event.getLocationInformation();
89     }
90     synchronized(bf) {
91       while(bf.isFull()) {
92     try {
93       //LogLog.debug("Waiting for free space in buffer, "+bf.length());
94
bf.wait();
95     } catch(InterruptedException JavaDoc e) {
96       if(!interruptedWarningMessage) {
97         interruptedWarningMessage = true;
98         LogLog.warn("AsyncAppender interrupted.", e);
99       } else {
100         LogLog.warn("AsyncAppender interrupted again.");
101       }
102     }
103       }
104
105       //cat.debug("About to put new event in buffer.");
106
bf.put(event);
107       if(bf.wasEmpty()) {
108     //cat.debug("Notifying dispatcher to process events.");
109
bf.notify();
110       }
111     }
112   }
113
114   /**
115      Close this <code>AsyncAppender</code> by interrupting the
116      dispatcher thread which will process all pending events before
117      exiting.
118   */

119   public void close() {
120     synchronized(this) {
121       // avoid multiple close, otherwise one gets NullPointerException
122
if(closed) {
123     return;
124       }
125       closed = true;
126     }
127
128     // The following cannot be synchronized on "this" because the
129
// dispatcher synchronizes with "this" in its while loop. If we
130
// did synchronize we would systematically get deadlocks when
131
// close was called.
132
dispatcher.close();
133     try {
134       dispatcher.join();
135     } catch(InterruptedException JavaDoc e) {
136       LogLog.error("Got an InterruptedException while waiting for the "+
137            "dispatcher to finish.", e);
138     }
139     dispatcher = null;
140     bf = null;
141   }
142
143   public Enumeration JavaDoc getAllAppenders() {
144     synchronized(aai) {
145       return aai.getAllAppenders();
146     }
147   }
148
149   public Appender getAppender(String JavaDoc name) {
150     synchronized(aai) {
151       return aai.getAppender(name);
152     }
153   }
154
155   /**
156      Returns the current value of the <b>LocationInfo</b> option.
157   */

158   public boolean getLocationInfo() {
159     return locationInfo;
160   }
161
162   /**
163      Is the appender passed as parameter attached to this category?
164    */

165   public boolean isAttached(Appender appender) {
166     return aai.isAttached(appender);
167   }
168
169
170   /**
171      The <code>AsyncAppender</code> does not require a layout. Hence,
172      this method always returns <code>false</code>.
173   */

174   public boolean requiresLayout() {
175     return false;
176   }
177
178   public void removeAllAppenders() {
179     synchronized(aai) {
180       aai.removeAllAppenders();
181     }
182   }
183
184
185   public void removeAppender(Appender appender) {
186     synchronized(aai) {
187       aai.removeAppender(appender);
188     }
189   }
190
191   public void removeAppender(String JavaDoc name) {
192     synchronized(aai) {
193       aai.removeAppender(name);
194     }
195   }
196
197   /**
198    * The <b>LocationInfo</b> option takes a boolean value. By default,
199    * it is set to false which means there will be no effort to extract
200    * the location information related to the event. As a result, the
201    * event that will be ultimately logged will likely to contain the
202    * wrong location information (if present in the log format).
203    *
204    * <p>Location information extraction is comparatively very slow and
205    * should be avoided unless performance is not a concern.
206    * */

207   public void setLocationInfo(boolean flag) {
208     locationInfo = flag;
209   }
210
211
212   /**
213    * The <b>BufferSize</b> option takes a non-negative integer value.
214    * This integer value determines the maximum size of the bounded
215    * buffer. Increasing the size of the buffer is always
216    * safe. However, if an existing buffer holds unwritten elements,
217    * then <em>decreasing the buffer size will result in event
218    * loss.</em> Nevertheless, while script configuring the
219    * AsyncAppender, it is safe to set a buffer size smaller than the
220    * {@link #DEFAULT_BUFFER_SIZE default buffer size} because
221    * configurators guarantee that an appender cannot be used before
222    * being completely configured.
223    * */

224   public void setBufferSize(int size) {
225     bf.resize(size);
226   }
227
228   /**
229      Returns the current value of the <b>BufferSize</b> option.
230    */

231   public int getBufferSize() {
232     return bf.getMaxSize();
233   }
234
235 }
236 // ------------------------------------------------------------------------------
237
// ------------------------------------------------------------------------------
238
// ----------------------------------------------------------------------------
239
class Dispatcher extends Thread JavaDoc {
240
241   BoundedFIFO bf;
242   AppenderAttachableImpl aai;
243   boolean interrupted = false;
244   AsyncAppender container;
245
246   Dispatcher(BoundedFIFO bf, AsyncAppender container) {
247     this.bf = bf;
248     this.container = container;
249     this.aai = container.aai;
250     // It is the user's responsibility to close appenders before
251
// exiting.
252
this.setDaemon(true);
253     // set the dispatcher priority to lowest possible value
254
this.setPriority(Thread.MIN_PRIORITY);
255     this.setName("Dispatcher-"+getName());
256
257     // set the dispatcher priority to MIN_PRIORITY plus or minus 2
258
// depending on the direction of MIN to MAX_PRIORITY.
259
//+ (Thread.MAX_PRIORITY > Thread.MIN_PRIORITY ? 1 : -1)*2);
260

261   }
262
263   void close() {
264     synchronized(bf) {
265       interrupted = true;
266       // We have a waiting dispacther if and only if bf.length is
267
// zero. In that case, we need to give it a death kiss.
268
if(bf.length() == 0) {
269     bf.notify();
270       }
271     }
272   }
273
274
275
276   /**
277      The dispatching strategy is to wait until there are events in the
278      buffer to process. After having processed an event, we release
279      the monitor (variable bf) so that new events can be placed in the
280      buffer, instead of keeping the monitor and processing the remaining
281      events in the buffer.
282
283     <p>Other approaches might yield better results.
284
285   */

286   public void run() {
287
288     //Category cat = Category.getInstance(Dispatcher.class.getName());
289

290     LoggingEvent event;
291
292     while(true) {
293       synchronized(bf) {
294     if(bf.length() == 0) {
295       // Exit loop if interrupted but only if the the buffer is empty.
296
if(interrupted) {
297         //cat.info("Exiting.");
298
break;
299       }
300       try {
301         //LogLog.debug("Waiting for new event to dispatch.");
302
bf.wait();
303       } catch(InterruptedException JavaDoc e) {
304         LogLog.error("The dispathcer should not be interrupted.");
305         break;
306       }
307     }
308     event = bf.get();
309     if(bf.wasFull()) {
310       //LogLog.debug("Notifying AsyncAppender about freed space.");
311
bf.notify();
312     }
313       } // synchronized
314

315       // The synchronization on parent is necessary to protect against
316
// operations on the aai object of the parent
317
synchronized(container.aai) {
318     if(aai != null && event != null) {
319       aai.appendLoopOnAppenders(event);
320     }
321       }
322     } // while
323

324     // close and remove all appenders
325
aai.removeAllAppenders();
326   }
327 }
328
Popular Tags