public void addAppender(Appender<E> newAppender) { if (appenderCount == 0) { appenderCount++; addInfo("Attaching appender named ["+newAppender.getName()+"] to AsyncAppender."); aai.addAppender(newAppender); } else { addWarn("One and only one appender may be attached to AsyncAppender."); addWarn("Ignoring additional appender named [" + newAppender.getName() + "]"); } }
@Override protected void append(E eventObject) { if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) { return; } preprocess(eventObject); put(eventObject); }
protected Appender<E> wrapAsync(Appender<E> appender, AsyncAppenderFactory<E> asyncAppenderFactory, Context context) { final AsyncAppenderBase<E> asyncAppender = asyncAppenderFactory.build(); if (asyncAppender instanceof AsyncAppender) { ((AsyncAppender) asyncAppender).setIncludeCallerData(includeCallerData); } asyncAppender.setQueueSize(queueSize); asyncAppender.setDiscardingThreshold(discardingThreshold); asyncAppender.setContext(context); asyncAppender.setName("async-" + appender.getName()); asyncAppender.addAppender(appender); asyncAppender.setNeverBlock(neverBlock); asyncAppender.start(); if (messageRate == null) { return asyncAppender; } else { return new ThrottlingAppenderWrapper<>(asyncAppender, messageRate.getQuantity(), messageRate.getUnit()); } }
private void flushAppender(AsyncAppenderBase<?> appender) throws InterruptedException { int timeWaiting = 0; while (timeWaiting < appender.getMaxFlushTime() && appender.getNumberOfElementsInQueue() > 0) { Thread.sleep(100); timeWaiting += 100; } if (appender.getNumberOfElementsInQueue() > 0) { // It may seem odd to log when we're trying to flush a logger that // isn't flushing, but the same warning is issued inside // appender.stop() if the appender isn't able to flush. appender.addWarn(appender.getNumberOfElementsInQueue() + " events may be discarded"); } }
@Override public void stop() { if (!isStarted()) return; // mark this appender as stopped so that Worker can also processPriorToRemoval if it is invoking aii.appendLoopOnAppenders // and sub-appenders consume the interruption super.stop(); // interrupt the worker thread so that it can terminate. Note that the interruption can be consumed // by sub-appenders worker.interrupt(); try { worker.join(maxFlushTime); //check to see if the thread ended and if not add a warning message if(worker.isAlive()) { addWarn("Max queue flush timeout (" + maxFlushTime + " ms) exceeded. Approximately " + blockingQueue.size() + " queued events were possibly discarded."); }else { addInfo("Queue flush finished successfully within timeout."); } } catch (InterruptedException e) { addError("Failed to join worker thread. " + blockingQueue.size() + " queued events may be discarded.", e); } }
@Test(timeout = 2000) public void noEventLoss() { int bufferSize = 10; int loopLen = bufferSize * 2; asyncAppenderBase.addAppender(delayingListAppender); asyncAppenderBase.setQueueSize(bufferSize); asyncAppenderBase.start(); for (int i = 0; i < loopLen; i++) { asyncAppenderBase.doAppend(i); } asyncAppenderBase.stop(); verify(delayingListAppender, loopLen); }
@Override public void start() { if (isStarted()) return; if (appenderCount == 0) { addError("No attached appenders found."); return; } if (queueSize < 1) { addError("Invalid queue size [" + queueSize + "]"); return; } blockingQueue = new ArrayBlockingQueue<E>(queueSize); if (discardingThreshold == UNDEFINED) discardingThreshold = queueSize / 5; addInfo("Setting discardingThreshold to " + discardingThreshold); worker.setDaemon(true); worker.setName("AsyncAppender-Worker-" + getName()); // make sure this instance is marked as "started" before staring the worker Thread super.start(); worker.start(); }
asyncAppender = new AsyncAppenderBase<>(); asyncAppender.setContext(context); asyncAppender.setQueueSize(queueSize); asyncAppender.addAppender(fileAppender); fileAppender.start(); if (queueSize > 0) { asyncAppender.start(); return asyncAppender;
@Override public void stop() { if (!isStarted()) return; // mark this appender as stopped so that Worker can also processPriorToRemoval if it is invoking aii.appendLoopOnAppenders // and sub-appenders consume the interruption super.stop(); // interrupt the worker thread so that it can terminate. Note that the interruption can be consumed // by sub-appenders worker.interrupt(); try { worker.join(1000); } catch (InterruptedException e) { addError("Failed to join worker thread", e); } }
@Override public void start() { if (appenderCount == 0) { addError("No attached appenders found."); return; } if (queueSize < 1) { addError("Invalid queue size [" + queueSize + "]"); return; } blockingQueue = new ArrayBlockingQueue<E>(queueSize); if (discardingThreshold == UNDEFINED) discardingThreshold = queueSize / 5; addInfo("Setting discardingThreshold to " + discardingThreshold); worker.setDaemon(true); worker.setName("AsyncAppender-Worker-" + worker.getName()); // make sure this instance is marked as "started" before staring the worker Thread super.start(); worker.start(); }
@Test public void invalidQueueCapacityShouldResultInNonStartedAppender() { asyncAppenderBase.addAppender(new NOPAppender<Integer>()); asyncAppenderBase.setQueueSize(0); assertEquals(0, asyncAppenderBase.getQueueSize()); asyncAppenderBase.start(); assertFalse(asyncAppenderBase.isStarted()); statusChecker.assertContainsMatch("Invalid queue size"); }
@SuppressWarnings("deprecation") @Test public void workerThreadFlushesOnStop() { int loopLen = 5; int maxRuntime = (loopLen + 1) * Math.max(1000, delayingListAppender.delay); ListAppender<Integer> la = delayingListAppender; asyncAppenderBase.addAppender(la); asyncAppenderBase.setDiscardingThreshold(0); asyncAppenderBase.setMaxFlushTime(maxRuntime); asyncAppenderBase.start(); asyncAppenderBase.worker.suspend(); for (int i = 0; i < loopLen; i++) { asyncAppenderBase.doAppend(i); } assertEquals(loopLen, asyncAppenderBase.getNumberOfElementsInQueue()); assertEquals(0, la.list.size()); asyncAppenderBase.worker.resume(); asyncAppenderBase.stop(); assertEquals(0, asyncAppenderBase.getNumberOfElementsInQueue()); verify(la, loopLen); }
@Test public void exceptionsShouldNotCauseHalting() throws InterruptedException { NPEAppender<Integer> npeAppender = new NPEAppender<Integer>(); npeAppender.setName("bad"); npeAppender.setContext(context); npeAppender.start(); asyncAppenderBase.addAppender(npeAppender); asyncAppenderBase.start(); assertTrue(asyncAppenderBase.isStarted()); for (int i = 0; i < 10; i++) asyncAppenderBase.append(i); asyncAppenderBase.stop(); assertFalse(asyncAppenderBase.isStarted()); assertEquals(AppenderBase.ALLOWED_REPEATS, statusChecker.matchCount("Appender \\[bad\\] failed to append.")); }
@Override public void start() { if (appenderCount == 0) { addError("No attached appenders found."); return; } if (queueSize < 1) { addError("Invalid queue size [" + queueSize + "]"); return; } blockingQueue = new ArrayBlockingQueue<E>(queueSize); if (discardingThreshold == UNDEFINED) discardingThreshold = queueSize / 5; addInfo("Setting discardingThreshold to " + discardingThreshold); worker.setDaemon(true); worker.setName("AsyncAppender-Worker-" + getName()); // make sure this instance is marked as "started" before staring the worker Thread super.start(); worker.start(); }
@Test(timeout = 2000) public void emptyQueueShouldBeStoppable() { asyncAppenderBase.addAppender(listAppender); asyncAppenderBase.start(); asyncAppenderBase.stop(); verify(listAppender, 0); }
@Test public void verifyInterruptionIsNotSwallowed() { asyncAppenderBase.addAppender(delayingListAppender); asyncAppenderBase.start(); Thread.currentThread().interrupt(); asyncAppenderBase.doAppend(0); assertTrue(Thread.currentThread().isInterrupted()); // clear interrupt flag for subsequent tests Thread.interrupted(); }
@Test public void checkThatStartMethodIsIdempotent() { asyncAppenderBase.addAppender(lossyAsyncAppender); asyncAppenderBase.start(); // we don't need mockito for this test, but if we did here is how it would look //AsyncAppenderBase<Integer> spied = Mockito.spy(asyncAppenderBase); //Mockito.doThrow(new IllegalStateException("non idempotent start")).when((UnsynchronizedAppenderBase<Integer>) spied).start(); // a second invocation of start will cause a IllegalThreadStateException thrown by the asyncAppenderBase.worker thread asyncAppenderBase.start(); } }
@Before public void setUp() { asyncAppenderBase.setContext(context); lossyAsyncAppender.setContext(context); listAppender.setContext(context); listAppender.setName("list"); listAppender.start(); delayingListAppender.setContext(context); delayingListAppender.setName("list"); delayingListAppender.start(); }
@Override public void stop() { if (!isStarted()) return; // mark this appender as stopped so that Worker can also processPriorToRemoval if it is invoking // aii.appendLoopOnAppenders // and sub-appenders consume the interruption super.stop(); // interrupt the worker thread so that it can terminate. Note that the interruption can be consumed // by sub-appenders worker.interrupt(); try { worker.join(maxFlushTime); // check to see if the thread ended and if not add a warning message if (worker.isAlive()) { addWarn("Max queue flush timeout (" + maxFlushTime + " ms) exceeded. Approximately " + blockingQueue.size() + " queued events were possibly discarded."); } else { addInfo("Queue flush finished successfully within timeout."); } } catch (InterruptedException e) { addError("Failed to join worker thread. " + blockingQueue.size() + " queued events may be discarded.", e); } }
@Test(timeout = 2000) public void smoke() { asyncAppenderBase.addAppender(listAppender); asyncAppenderBase.start(); asyncAppenderBase.doAppend(0); asyncAppenderBase.stop(); verify(listAppender, 1); }