EMMA Coverage Report (generated Wed Feb 26 21:48:09 GMT 2014)
[all classes][uk.org.simonsite.log4j.appender]

COVERAGE SUMMARY FOR SOURCE FILE [ActiveAsynchronousAppender.java]

nameclass, %method, %block, %line, %
ActiveAsynchronousAppender.java100% (1/1)63%  (19/30)77%  (244/317)75%  (73.8/99)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ActiveAsynchronousAppender100% (1/1)63%  (19/30)77%  (244/317)75%  (73.8/99)
getAllAppenders (): Enumeration 0%   (0/1)0%   (0/4)0%   (0/1)
getAppender (String): Appender 0%   (0/1)0%   (0/5)0%   (0/1)
getBufferSize (): int 0%   (0/1)0%   (0/4)0%   (0/1)
getFailToSync (): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
getUseCAS (): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
getUseConcurrentBackport (): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
isAttached (Appender): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
removeAllAppenders (): void 0%   (0/1)0%   (0/4)0%   (0/2)
removeAppender (Appender): void 0%   (0/1)0%   (0/5)0%   (0/2)
removeAppender (String): void 0%   (0/1)0%   (0/5)0%   (0/2)
requiresLayout (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
doAppend (LoggingEvent): void 100% (1/1)44%  (14/32)31%  (4/13)
setBufferSize (int): void 100% (1/1)59%  (10/17)70%  (2.8/4)
activateOptions (): void 100% (1/1)85%  (11/13)83%  (5/6)
ActiveAsynchronousAppender (): void 100% (1/1)100% (22/22)100% (5/5)
activateComponents (): void 100% (1/1)100% (58/58)100% (17/17)
addAppender (Appender): void 100% (1/1)100% (5/5)100% (2/2)
append (LoggingEvent): void 100% (1/1)100% (41/41)100% (12/12)
close (): void 100% (1/1)100% (16/16)100% (4/4)
compareAndSetClosed (boolean, boolean): boolean 100% (1/1)100% (11/11)100% (4/4)
getAppenderAttachable (): ExecutingAppenderAttachable 100% (1/1)100% (5/5)100% (1/1)
getDispatcher (): LoggingEventDispatcher 100% (1/1)100% (5/5)100% (1/1)
getLocationInfo (): boolean 100% (1/1)100% (4/4)100% (1/1)
getProperties (): ActiveAsynchronousAppenderProperties 100% (1/1)100% (3/3)100% (1/1)
isClosed (): boolean 100% (1/1)100% (3/3)100% (1/1)
setFailToSync (boolean): void 100% (1/1)100% (5/5)100% (2/2)
setLocationInfo (boolean): void 100% (1/1)100% (5/5)100% (2/2)
setName (String): void 100% (1/1)100% (8/8)100% (3/3)
setUseCAS (boolean): void 100% (1/1)100% (9/9)100% (3/3)
setUseConcurrentBackport (boolean): void 100% (1/1)100% (9/9)100% (3/3)

1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 * 
6 *      http://www.apache.org/licenses/LICENSE-2.0
7 * 
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14package uk.org.simonsite.log4j.appender;
15 
16import java.util.Enumeration;
17 
18import org.apache.log4j.Appender;
19import org.apache.log4j.AppenderSkeleton;
20import org.apache.log4j.AsyncAppender;
21import org.apache.log4j.helpers.LogLog;
22import org.apache.log4j.spi.AppenderAttachable;
23import org.apache.log4j.spi.Filter;
24import org.apache.log4j.spi.LoggingEvent;
25import org.apache.log4j.spi.OptionHandler;
26 
27/**
28 * This appender appends {@link LoggingEvent}s asynchronously. It acts solely as
29 * an event dispatcher, and must therefore be attached to one or more child
30 * appenders in order to do useful work.
31 * <p>
32 * The appender buffers events into the tail of a queue (on an application
33 * thread). The appender's dispatch thread takes events from the head of the
34 * queue, and dispatches them to all the appenders that are attached to this
35 * appender.
36 * <h2>Lifecycle</h2>
37 * <p>
38 * It is the user's responsibility to close appenders, typically at the end of
39 * the application lifecycle. If this is done, instances of this appender make
40 * best efforts to dispatch all buffered events. Upon a dirty shutdown
41 * (e.g.&nbsp;Ctrl+C, no shutdown hooks) then no guarantees can be made.
42 * <h2>Dependencies</h2>
43 * <p>
44 * This appender has an <em>optional</em> dependency upon the <a
45 * href="http://backport-jsr166.sourceforge.net/">SourceForge
46 * backport-util-concurrent</a> concurrency JAR. To use
47 * backport-util-concurrent, ensure the JAR is in your application's CLASSPATH,
48 * and set <tt>UseConcurrentBackport</tt> to true.
49 * <h2>Tuning</h2>
50 * <p>
51 * The appender's event buffer is configured with the <tt>BufferSize</tt>
52 * parameter (default 32). If the buffer is full, then application threads are
53 * blocked from appending new events until the dispatch thread has had a chance
54 * to dispatch one or more events. When the buffer is no longer full,
55 * application threads are notified, and are able to start appending events once
56 * more.
57 * <p>
58 * Optimally tuning the size of the appender's event buffer for maximum
59 * application throughput depends upon several factors. Any or all of the
60 * following factors are likely to affect throughput:
61 * <ul>
62 * <li>Large numbers of application threads</li>
63 * <li>Large numbers of logging events per application call</li>
64 * <li>Large amounts of data per logging event</li>
65 * <li>High latency of child appenders</li>
66 * </ul>
67 * Increasing the size of the appender's buffer may help improve throughput, at
68 * the expense of heap available to the application when large numbers of
69 * logging events are buffered. An important factor is obviously the number of
70 * logging calls a single application thread makes during the execution of a
71 * task, as is the log level the application is normally expected to run at.
72 * Profiling the application is advisable.
73 * <h2>Non-Blocking {@link LoggingEventQueue}</h2>
74 * <p>
75 * When using Java 5 or above there is the option to use the non-blocking CAS
76 * implementation of the {@link LoggingEventQueue}, configured by setting
77 * <tt>UseCAS</tt> parameter to <tt>true</tt>. This implementation offers the
78 * highest throughput of the various provided implementations of
79 * {@link LoggingEventQueue} though there is a cost in terms of higher CPU
80 * usage; it is recommended to use this where high throughput is a hard
81 * requirement and the application is doing a relatively large amount of
82 * logging. <strong>NB</strong> This cannot be used in conjunction with the
83 * <tt>UseConcurrentBackport</tt> parameter.
84 * <h2>Appender Chaining</h2>
85 * <p>
86 * It is possible to attach separate instances of this appender to one another
87 * to achieve multi-threaded dispatch. For example, assume a requirement for two
88 * child appenders that each perform relatively expensive operations such as
89 * messaging and file IO. For this case, one could set up a graph of appenders
90 * such that the parent asynchronous appender, A, has two child asynchronous
91 * appenders attached, B and C. Let's say B in turn has a child file IO appender
92 * attached, and C has a child messaging appender attached. This will result in
93 * fast dispatch from A to both B and C via A's dispatch thread. B's dispatch
94 * thread will be dedicated to logging to file IO, and C's dispatch thread will
95 * be dedicated to logging via messaging.
96 * <h2>Error Handling</h2>
97 * <p>
98 * {@link RuntimeException}s are caught by the dispatch thread. For this case
99 * the appender can be configured either to continue dispatching events
100 * asynchronously, or to fail back to synchronous logging. By default the
101 * appender fails back to synchronous logging, since this is the default
102 * behaviour of the standard Log4J {@link AsyncAppender}. To configure the
103 * appender to continue asynchronous dispatch, set the <tt>FailToSync</tt> flag
104 * to <tt>false</tt>.
105 * <h2>Gotchas</h2>
106 * <p>
107 * A restriction of this appender is that attached appenders must be named. A
108 * call to {@link #addAppender(Appender)} wherein the parameter is not a named
109 * appender will result in a failure to attach.
110 * <h2>Sample configuration</h2>
111 * &lt;appender name=&quot;active-async-appender&quot;
112 * class=&quot;uk.org.simonsite.log4j.appender.ActiveAsynchronousAppender&quot;&gt;<br/>
113 * &lt;appender-ref ref=&quot;console&quot; /&gt;<br/>
114 * &lt;param name=&quot;BufferSize&quot; value=&quot;16&quot; /&gt; &lt;!--
115 * Default is 32 --&gt;<br/>
116 * &lt;param name=&quot;FailToSync&quot; value=&quot;false&quot; /&gt; &lt;!--
117 * Default is true --&gt;<br/>
118 * &lt;param name=&quot;LocationInfo&quot; value=&quot;false&quot; /&gt; &lt;!--
119 * Default is false --&gt;<br/>
120 * &lt;param name=&quot;UseCAS&quot; value=&quot;true&quot; /&gt; &lt;!--
121 * Default is false --&gt;<br/>
122 * &lt;/appender&gt;
123 * 
124 * @see AsyncAppender
125 * @author <a href="mailto:simon_park_mail AT yahoo DOT co DOT uk">Simon
126 *         Park</a>
127 * @version 2.5
128 */
129public final class ActiveAsynchronousAppender extends AppenderSkeleton
130    implements Appender, AppenderAttachable, OptionHandler {
131 
132  /**
133   * Nested appenders.
134   */
135  private final SynchronizedObject appenderAttachable;
136 
137  /**
138   * {@link LoggingEvent} dispatcher, responsible for forwarding events to
139   * attached appenders.
140   */
141  private final SynchronizedObject dispatcher;
142 
143  /**
144   * Configuration properties.
145   */
146  private final ActiveAsynchronousAppenderProperties properties;
147 
148  public ActiveAsynchronousAppender() {
149    super();
150    this.properties = new ActiveAsynchronousAppenderProperties();
151    this.appenderAttachable = new SynchronizedObject(
152        ActiveAsynchronousAppenderComponentFactory.create(this.getProperties())
153            .createAppenderAttachable());
154    this.dispatcher = new SynchronizedObject();
155  }
156 
157  /**
158   * @see org.apache.log4j.AppenderSkeleton#activateOptions()
159   */
160  public final void activateOptions() {
161    final LoggingEventDispatcher dispatcher = this.getDispatcher();
162    if (dispatcher != null) {
163      dispatcher.end();
164    }
165    this.activateComponents();
166    this.getDispatcher().begin();
167  }
168 
169  /**
170   * @see org.apache.log4j.spi.AppenderAttachable#addAppender(org.apache.log4j.Appender)
171   */
172  public final void addAppender(final Appender newAppender) {
173    this.getAppenderAttachable().addAppender(newAppender);
174  }
175 
176  /**
177   * Closes all attached appenders.
178   * 
179   * @see org.apache.log4j.Appender#close()
180   */
181  public final void close() {
182    if (this.compareAndSetClosed(false, true)) {
183      this.getDispatcher().end();
184      this.getAppenderAttachable().execute(new CloseAppenderCommand(), null);
185    }
186  }
187 
188  /**
189   * Minimally-synchronized override of
190   * {@link AppenderSkeleton#doAppend(LoggingEvent)}.
191   * 
192   * @see org.apache.log4j.AppenderSkeleton#doAppend(org.apache.log4j.spi.LoggingEvent)
193   */
194  public final void doAppend(final LoggingEvent event) {
195    if (!super.isAsSevereAsThreshold(event.getLevel())) {
196      return;
197    }
198    for (Filter f = super.getFirstFilter(); f != null;) {
199      switch (f.decide(event)) {
200      case Filter.DENY:
201        return;
202      case Filter.NEUTRAL:
203        f = f.getNext();
204        break;
205      case Filter.ACCEPT:
206        f = null;
207        break;
208      default:
209        f = null;
210        LogLog.error("Unknown Filter type");
211      }
212 
213    }
214    this.append(event);
215  }
216 
217  /**
218   * @see org.apache.log4j.spi.AppenderAttachable#getAllAppenders()
219   */
220  public final Enumeration getAllAppenders() {
221    return this.getAppenderAttachable().getAllAppenders();
222  }
223 
224  /**
225   * @see org.apache.log4j.spi.AppenderAttachable#getAppender(java.lang.String)
226   */
227  public final Appender getAppender(final String name) {
228    return this.getAppenderAttachable().getAppender(name);
229  }
230 
231  /**
232   * Gets the current buffer size.
233   * 
234   * @return the current value of the <b>BufferSize</b> option.
235   */
236  public final int getBufferSize() {
237    return this.getProperties().getMaxSize();
238  }
239 
240  /**
241   * @return the current value of the <b>FailToSync</b> option.
242   */
243  public final boolean getFailToSync() {
244    return this.getProperties().isFailToSync();
245  }
246 
247  public final boolean getUseConcurrentBackport() {
248    return this.getProperties().isUseBackport();
249  }
250 
251  public final boolean getUseCAS() {
252    return this.getProperties().isUseCAS();
253  }
254 
255  /**
256   * @return the current value of the <b>LocationInfo</b> option.
257   * @see AsyncAppender#getLocationInfo()
258   */
259  public final boolean getLocationInfo() {
260    return this.getProperties().isLocationInfo();
261  }
262 
263  /**
264   * @see org.apache.log4j.spi.AppenderAttachable#isAttached(org.apache.log4j.Appender)
265   */
266  public final boolean isAttached(final Appender appender) {
267    return this.getAppenderAttachable().isAttached(appender);
268  }
269 
270  /**
271   * @see org.apache.log4j.spi.AppenderAttachable#removeAllAppenders()
272   */
273  public final void removeAllAppenders() {
274    this.getAppenderAttachable().removeAllAppenders();
275  }
276 
277  /**
278   * @see org.apache.log4j.spi.AppenderAttachable#removeAppender(org.apache.log4j.Appender)
279   */
280  public final void removeAppender(final Appender appender) {
281    this.getAppenderAttachable().removeAppender(appender);
282  }
283 
284  /**
285   * @see org.apache.log4j.spi.AppenderAttachable#removeAppender(java.lang.String)
286   */
287  public final void removeAppender(final String name) {
288    this.getAppenderAttachable().removeAppender(name);
289  }
290 
291  /**
292   * @see org.apache.log4j.Appender#requiresLayout()
293   */
294  public final boolean requiresLayout() {
295    return false;
296  }
297 
298  /**
299   * @param size
300   *          buffer size, must be positive.
301   * @see AsyncAppender#setBufferSize(int)
302   */
303  public final void setBufferSize(final int size) {
304    // Same behaviour as AsyncAppender for compatibility
305    if (size < 0) {
306      throw new java.lang.NegativeArraySizeException("size");
307    }
308    this.getProperties().setMaxSize((size < 1) ? 1 : size);
309  }
310 
311  /**
312   * @param flag
313   *          true if the asynchronous appender should fail-over to synchronous
314   *          logging in the event that an attempt to append a LoggingEvent
315   *          fails due to a {@link RuntimeException}.
316   */
317  public final void setFailToSync(final boolean flag) {
318    this.getProperties().setFailToSync(flag);
319  }
320 
321  public final void setName(final String name) {
322    super.setName(name);
323    this.getProperties().setName(name);
324  }
325 
326  /**
327   * @param flag
328   *          true if location information should be extracted.
329   * @see AsyncAppender#setLocationInfo(boolean)
330   */
331  public final void setLocationInfo(final boolean flag) {
332    this.getProperties().setLocationInfo(flag);
333  }
334 
335  public final void setUseConcurrentBackport(final boolean flag) {
336    this.getProperties().setUseCAS(false);
337    this.getProperties().setUseBackport(flag);
338  }
339 
340  public final void setUseCAS(final boolean flag) {
341    this.getProperties().setUseBackport(false);
342    this.getProperties().setUseCAS(flag);
343  }
344 
345  /**
346   * @return true if this appender has already been closed.
347   */
348  final synchronized boolean isClosed() {
349    return super.closed;
350  }
351 
352  /**
353   * @param expect
354   *          The expected value.
355   * @param update
356   *          The replacement value.
357   * @return <tt>true</tt> if the value was changed.
358   */
359  final synchronized boolean compareAndSetClosed(final boolean expect,
360      final boolean update) {
361    if (super.closed == expect) {
362      super.closed = update;
363      return true;
364    }
365    return false;
366  }
367 
368  /**
369   * Forwards {@link LoggingEvent}s to the dispatcher, which in turn delegates
370   * append operations to the attached appenders.
371   * 
372   * @see org.apache.log4j.AppenderSkeleton#append(org.apache.log4j.spi.LoggingEvent)
373   */
374  protected final void append(final LoggingEvent event) {
375    // ***
376    // LoggingEvent::getXXX() operations copied-and-pasted from
377    // AsyncAppender for compatibility.
378    // ***
379    // Set the NDC and thread name for the calling thread as these
380    // LoggingEvent fields were not set at event creation time.
381    event.getNDC();
382    event.getThreadName();
383    // Get a copy of this thread's MDC.
384    event.getMDCCopy();
385    if (this.getLocationInfo()) {
386      event.getLocationInformation();
387    }
388    event.getRenderedMessage();
389    event.getThrowableStrRep();
390 
391    if (this.isClosed()) {
392      LogLog.warn("Attempted to append to closed appender named [" + name
393          + "].");
394      return;
395    }
396    this.getDispatcher().dispatch(event);
397  }
398 
399  private void activateComponents() {
400    final ExecutingAppenderAttachable attachable = ActiveAsynchronousAppenderComponentFactory
401        .create(this.getProperties()).createAppenderAttachable();
402    final ExecutingAppenderAttachable currentAttachable = this
403        .getAppenderAttachable();
404    if (currentAttachable != null) {
405      for (Enumeration appenderEnum = currentAttachable.getAllAppenders(); (appenderEnum != null)
406          && appenderEnum.hasMoreElements();) {
407        final Appender appender = (Appender) appenderEnum.nextElement();
408        attachable.addAppender(appender);
409      }
410      for (Enumeration appenderEnum = attachable.getAllAppenders(); (appenderEnum != null)
411          && appenderEnum.hasMoreElements();) {
412        final Appender appender = (Appender) appenderEnum.nextElement();
413        // don't just use removeAllAppenders() as this also closes Appenders
414        // down
415        currentAttachable.removeAppender(appender);
416      }
417      // no appenders should be left so this is belt-and-braces
418      currentAttachable.removeAllAppenders();
419    }
420    this.appenderAttachable.set(attachable);
421    this.dispatcher.set(new LoggingEventDispatcher(attachable, this
422        .getProperties()));
423  }
424 
425  private ActiveAsynchronousAppenderProperties getProperties() {
426    return this.properties;
427  }
428 
429  private ExecutingAppenderAttachable getAppenderAttachable() {
430    return (ExecutingAppenderAttachable) this.appenderAttachable.get();
431  }
432 
433  private LoggingEventDispatcher getDispatcher() {
434    return (LoggingEventDispatcher) this.dispatcher.get();
435  }
436}

[all classes][uk.org.simonsite.log4j.appender]
EMMA 2.0.5312 (C) Vladimir Roubtsov