+ * Note that once this is invoked the handler will be activated and the messages will no longer be queued. If more + * than one child handler is required the {@link #setHandlers(Handler[])} should be used. + *
+ * + * @see #setHandlers(Handler[]) + */ + @Override + public void addHandler(final Handler handler) throws SecurityException { + super.addHandler(handler); + activate(); + } + + /** + * {@inheritDoc} + *+ * Note that once this is invoked the handler will be activated and the messages will no longer be queued. + *
+ */ + @Override + public Handler[] setHandlers(final Handler[] newHandlers) throws SecurityException { + final Handler[] result = super.setHandlers(newHandlers); + activate(); + return result; + } + + /** + * {@inheritDoc} + *+ * Note that if the last child handler is removed the handler will no longer be activated and the messages will + * again be queued. + *
+ * + * @see #clearHandlers() + */ + @Override + public void removeHandler(final Handler handler) throws SecurityException { + super.removeHandler(handler); + activated = handlers.length != 0; + } + + /** + * {@inheritDoc} + *+ * Note that once this is invoked the handler will no longer be activated and messages will again be queued. + *
+ * + * @see #removeHandler(Handler) + */ + @Override + public Handler[] clearHandlers() throws SecurityException { + activated = false; + return super.clearHandlers(); + } + + /** + * {@inheritDoc} + *+ * This can be overridden to always require the caller calculation by setting the + * {@link #setCallerCalculationRequired(boolean)} value to {@code true}. + *
+ * + * @see #setCallerCalculationRequired(boolean) + */ + @Override + public boolean isCallerCalculationRequired() { + return callerCalculationRequired || super.isCallerCalculationRequired(); + } + + /** + * Sets whether or not {@linkplain ExtLogRecord#copyAll() caller information} will be required when formatting + * records. + *+ * If set to {@code true} the {@linkplain ExtLogRecord#copyAll() caller information} will be calculated for each + * record that is placed in the queue. A value of {@code false} means the + * {@link super#isCallerCalculationRequired()} will be used. + *
+ *+ * Note that the caller information is only attempted to be calculated when the handler has not been activated. Once + * activated it's up to the {@linkplain #getHandlers() children handlers} to determine how the record is processed. + *
+ * + * @param callerCalculationRequired {@code true} if the {@linkplain ExtLogRecord#copyAll() caller information} + * should always be calculated before the record is being placed in the queue + */ + public void setCallerCalculationRequired(final boolean callerCalculationRequired) { + this.callerCalculationRequired = callerCalculationRequired; + } + + /** + * Indicates whether or not this handler has been activated. + * + * @return {@code true} if the handler has been activated, otherwise {@code false} + */ + public final boolean isActivated() { + return activated; + } + + private synchronized void activate() { + // Always attempt to drain the queue + ExtLogRecord record; + while ((record = logRecords.pollFirst()) != null) { + if (isEnabled() && isLoggable(record)) { + publishToChildren(record); + } + } + activated = true; + } + + private void publishToChildren(final ExtLogRecord record) { + for (Handler handler : handlers) { + handler.publish(record); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/jboss/logmanager/handlers/DelayedHandlerTests.java b/src/test/java/org/jboss/logmanager/handlers/DelayedHandlerTests.java new file mode 100644 index 00000000..33b4e251 --- /dev/null +++ b/src/test/java/org/jboss/logmanager/handlers/DelayedHandlerTests.java @@ -0,0 +1,255 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2017 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jboss.logmanager.handlers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import org.jboss.logmanager.ExtHandler; +import org.jboss.logmanager.ExtLogRecord; +import org.jboss.logmanager.LogContext; +import org.jboss.logmanager.config.HandlerConfiguration; +import org.jboss.logmanager.config.LogContextConfiguration; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.wildfly.common.cpu.ProcessorInfo; + +/** + * @author James R. Perkins + */ +public class DelayedHandlerTests { + + private static final int ITERATIONS = Integer.parseInt(System.getProperty("org.jboss.bootstrap.test.iterations", "1000")); + + @After + public void cleanup() { + TestHandler.MESSAGES.clear(); + } + + @Test + public void testQueuedMessages() { + final LogContext logContext = LogContext.create(); + + final LogContextConfiguration logContextConfiguration = LogContextConfiguration.Factory.create(logContext); + final HandlerConfiguration delayedHandler = logContextConfiguration.addHandlerConfiguration( + null, DelayedHandler.class.getName(), "delayed-handler"); + logContextConfiguration.addLoggerConfiguration("").addHandlerName(delayedHandler.getName()); + logContextConfiguration.commit(); + + final Logger rootLogger = logContext.getLogger(""); + rootLogger.info("Test message 1"); + rootLogger.fine("Test message 2"); + + final Logger testLogger = logContext.getLogger(DelayedHandlerTests.class.getName()); + testLogger.warning("Test message 3"); + + final Logger randomLogger = logContext.getLogger("org.jboss.logmanager." + UUID.randomUUID()); + randomLogger.severe("Test message 4"); + + // Activate after some messages have been logged + final HandlerConfiguration handlerConfiguration = logContextConfiguration.addHandlerConfiguration( + null, TestHandler.class.getName(), "test-handler"); + delayedHandler.setHandlerNames(handlerConfiguration.getName()); + logContextConfiguration.commit(); + + + rootLogger.info("Test message 5"); + testLogger.severe("Test message 6"); + randomLogger.finest("Test message 7"); + + // The default root logger message is INFO so FINE and FINEST should not be logged + Assert.assertEquals(5, TestHandler.MESSAGES.size()); + + // Test the messages actually logged + Assert.assertEquals("Test message 1", TestHandler.MESSAGES.get(0).getFormattedMessage()); + Assert.assertEquals("Test message 3", TestHandler.MESSAGES.get(1).getFormattedMessage()); + Assert.assertEquals("Test message 4", TestHandler.MESSAGES.get(2).getFormattedMessage()); + Assert.assertEquals("Test message 5", TestHandler.MESSAGES.get(3).getFormattedMessage()); + Assert.assertEquals("Test message 6", TestHandler.MESSAGES.get(4).getFormattedMessage()); + } + + + @Test + public void testAllLoggedAfterActivation() throws Exception { + final ExecutorService service = createExecutor(); + final LogContext logContext = LogContext.create(); + + final DelayedHandler handler = new DelayedHandler(); + final Logger rootLogger = logContext.getLogger(""); + rootLogger.addHandler(handler); + + try { + for (int i = 0; i < ITERATIONS; i++) { + final int current = i; + service.submit(() -> rootLogger.info(Integer.toString(current))); + } + // Wait until all the messages have been logged before + service.shutdown(); + service.awaitTermination(5, TimeUnit.SECONDS); + handler.addHandler(new TestHandler()); + + // Test that all messages have been flushed to the handler + Assert.assertEquals(ITERATIONS, TestHandler.MESSAGES.size()); + + // Test that all messages have been flushed to the handler + final List