diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLifeCycle.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLifeCycle.java index a5a0c77..069362d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLifeCycle.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLifeCycle.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.core; +import java.util.concurrent.TimeUnit; + import org.apache.logging.log4j.status.StatusLogger; /** @@ -121,4 +123,10 @@ this.state = LifeCycle.State.STOPPED; } + @Override + public boolean stop(long timeout, TimeUnit timeUnit) { + this.state = LifeCycle.State.STOPPED; + return true; + } + } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LifeCycle.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LifeCycle.java index 33a61a2..9311487 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LifeCycle.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LifeCycle.java @@ -17,6 +17,8 @@ package org.apache.logging.log4j.core; +import java.util.concurrent.TimeUnit; + /** * All proper Java frameworks implement some sort of object life cycle. In Log4j, the main interface for handling * the life cycle context of an object is this one. An object first starts in the {@link State#INITIALIZED} state @@ -60,7 +62,18 @@ void stop(); + /** + * Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current + * thread is interrupted, whichever happens first. + * + * @param timeout the maximum time to wait + * @param timeUnit the time unit of the timeout argument + * @return + */ + boolean stop(long timeout, TimeUnit timeUnit); + boolean isStarted(); boolean isStopped(); + } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java index cfb4945..d1bc137 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java @@ -26,6 +26,11 @@ import java.util.Objects; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -40,6 +45,7 @@ import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.jmx.Server; import org.apache.logging.log4j.core.util.Cancellable; +import org.apache.logging.log4j.core.util.Log4jThreadFactory; import org.apache.logging.log4j.core.util.NetUtils; import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; import org.apache.logging.log4j.message.MessageFactory; @@ -72,6 +78,8 @@ * reference is updated. */ private volatile Configuration configuration = new DefaultConfiguration(); + private ExecutorService executorService; + private ExecutorService executorServiceDeamons; private Object externalContext; private String contextName; private volatile URI configLocation; @@ -294,11 +302,18 @@ @Override public void stop() { + stop(0, null); + } + + @Override + public boolean stop(long timeout, TimeUnit timeUnit) { LOGGER.debug("Stopping LoggerContext[name={}, {}]...", getName(), this); configLock.lock(); + final boolean shutdownEs; + final boolean shutdownEsd; try { if (this.isStopped()) { - return; + return true; } this.setStopping(); @@ -317,11 +332,54 @@ prev.stop(); externalContext = null; LogManager.getFactory().removeContext(this); + shutdownEs = shutdown(executorService, timeout, timeUnit); + // Do not wait for daemon threads + shutdownEsd = shutdown(executorServiceDeamons, 0, null); this.setStopped(); } finally { configLock.unlock(); } LOGGER.debug("Stopped LoggerContext[name={}, {}]...", getName(), this); + return shutdownEs && shutdownEsd; + } + + /** + * Shuts down the given pool. + * + * @param pool + * the pool to shutdown. + * @param timeout + * the maximum time to wait + * @param unit + * the time unit of the timeout argument + * @return {@code true} if the given executor terminated and {@code false} if the timeout elapsed before termination. + */ + private boolean shutdown(ExecutorService pool, long timeout, TimeUnit timeUnit) { + pool.shutdown(); // Disable new tasks from being submitted + if (timeout > 0 && timeUnit == null) { + throw new IllegalArgumentException( + String.format("Logger context '%s' can't shutdown %s when timeout = %,d and timeUnit = %s.", + getName(), pool, timeout, timeUnit)); + } + if (timeout > 0) { + try { + // Wait a while for existing tasks to terminate + if (!pool.awaitTermination(timeout, timeUnit)) { + pool.shutdownNow(); // Cancel currently executing tasks + // Wait a while for tasks to respond to being cancelled + if (!pool.awaitTermination(timeout, timeUnit)) { + LOGGER.error("LoggerContext '{}' pool {} did not terminate after {} {}", getName(), pool, timeout, timeUnit); + } + return false; + } + } catch (InterruptedException ie) { + // (Re-)Cancel if current thread also interrupted + pool.shutdownNow(); + // Preserve interrupt status + Thread.currentThread().interrupt(); + } + } + return true; } /** @@ -490,6 +548,9 @@ try { final Configuration prev = this.configuration; config.addListener(this); + executorService = Executors.newCachedThreadPool(Log4jThreadFactory.createThreadFactory(contextName)); + executorServiceDeamons = Executors.newCachedThreadPool(Log4jThreadFactory.createDaemonThreadFactory(contextName)); + final ConcurrentMap map = config.getComponent(Configuration.CONTEXT_PROPERTIES); try { // LOG4J2-719 network access may throw android.os.NetworkOnMainThreadException @@ -630,4 +691,52 @@ return new Logger(ctx, name, messageFactory); } + /** + * Gets the executor service to submit normal tasks. + * + * @return the ExecutorService to submit normal tasks. + */ + public ExecutorService getExecutorService() { + return executorService; + } + + /** + * Gets the executor service to submit daemon tasks. + * + * @return the ExecutorService to submit normal daemon tasks. + */ + public ExecutorService getExecutorServiceDeamons() { + return executorServiceDeamons; + } + + /** + * Submits a Runnable task for normal execution and returns a Future representing that task. The Future's + * {@code get} method will return {@code null} upon successful completion. + * + * @param task the task to submit + * @return a Future representing pending completion of the task + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null + */ + public Future submit(Runnable task) { + return executorService.submit(task); + } + + /** + * Submits a Runnable task for daemon execution and returns a Future representing that task. The Future's + * {@code get} method will return {@code null} upon successful completion. + * + * @param task + * the task to submit + * @return a Future representing pending completion of the task + * @throws RejectedExecutionException + * if the task cannot be scheduled for execution + * @throws NullPointerException + * if the task is null + */ + public Future submitDaemon(Runnable task) { + return executorServiceDeamons.submit(task); + } + } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java index 4c78e7f..dbe1bc4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.status.StatusLogger; @@ -116,6 +117,11 @@ return count; } + public LoggerContext getLoggerContext() { + // TODO Can and should a manager tracks it's logger context? + return LoggerContext.getContext(false); + } + /** * Called to signify that this Manager is no longer required by an Appender. */ diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java index d535e02..21224e3 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java @@ -26,7 +26,6 @@ import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.logging.log4j.core.appender.AbstractManager; import org.apache.logging.log4j.core.config.Property; -import org.apache.logging.log4j.core.util.Log4jThread; public class KafkaManager extends AbstractManager { @@ -38,7 +37,7 @@ static KafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory(); private final Properties config = new Properties(); - private Producer producer = null; + private Producer producer; private final int timeoutMillis; private final String topic; @@ -59,17 +58,17 @@ public void releaseSub() { if (producer != null) { // This thread is a workaround for this Kafka issue: https://issues.apache.org/jira/browse/KAFKA-1660 - final Thread closeThread = new Log4jThread(new Runnable() { + final Runnable task = new Runnable() { @Override public void run() { - producer.close(); + if (producer != null) { + producer.close(); + } } - }, "KafkaManager-CloseThread"); - closeThread.setDaemon(true); // avoid blocking JVM shutdown - closeThread.start(); + }; try { - closeThread.join(timeoutMillis); - } catch (final InterruptedException ignore) { + getLoggerContext().submitDaemon(task).get(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { // ignore } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java index 3c6739f..3fcf864 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java @@ -17,24 +17,23 @@ package org.apache.logging.log4j.core.appender.rolling; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.nio.ByteBuffer; +import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.FileManager; import org.apache.logging.log4j.core.appender.ManagerFactory; import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction; import org.apache.logging.log4j.core.appender.rolling.action.Action; -import org.apache.logging.log4j.core.util.Clock; import org.apache.logging.log4j.core.util.Constants; -import org.apache.logging.log4j.core.util.Log4jThread; /** * The Rolling File Manager. @@ -236,7 +235,7 @@ } boolean success = false; - Thread thread = null; + Future future = null; try { final RolloverDescription descriptor = strategy.rollover(this); @@ -254,14 +253,13 @@ if (success && descriptor.getAsynchronous() != null) { LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous()); - thread = new Log4jThread(new AsyncAction(descriptor.getAsynchronous(), this)); - thread.start(); + future = LoggerContext.getContext(false).submit(new AsyncAction(descriptor.getAsynchronous(), this)); } return true; } return false; } finally { - if (thread == null || !thread.isAlive()) { + if (future == null || future.isDone() || future.isCancelled()) { semaphore.release(); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java index 744ed24..4987082 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java @@ -39,17 +39,16 @@ private int scheduledItems = 0; - @Override public void start() { super.start(); if (scheduledItems > 0) { - LOGGER.debug("Starting {} Log4j2Scheduled threads", scheduledItems); + LOGGER.debug("Starting {} Log4j2 Scheduled threads", scheduledItems); if (scheduledItems > 5) { scheduledItems = 5; } executorService = new ScheduledThreadPoolExecutor(scheduledItems, - Log4jThreadFactory.createDaemonThreadFactory("Log4j2Scheduled")); + Log4jThreadFactory.createDaemonThreadFactory("Scheduled")); } else { LOGGER.debug("No scheduled items"); } @@ -58,7 +57,7 @@ @Override public void stop() { if (executorService != null) { - LOGGER.debug("Stopping Log4j2Scheduled threads."); + LOGGER.debug("Stopping Log4j2 Scheduled threads."); executorService.shutdown(); } super.stop(); @@ -86,6 +85,7 @@ /** * Creates and executes a ScheduledFuture that becomes enabled after the given delay. + * @param The result type returned by this Future * @param callable the function to execute. * @param delay the time from now to delay execution. * @param unit the time unit of the delay parameter. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java index ceb6b57..cadb5e1 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java @@ -16,11 +16,11 @@ */ package org.apache.logging.log4j.core.config; -import org.apache.logging.log4j.core.util.FileWatcher; -import org.apache.logging.log4j.core.util.Log4jThread; - import java.io.File; import java.util.List; + +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.util.FileWatcher; /** * Watcher for configuration files. Causes a reconfiguration when a file changes. @@ -43,9 +43,7 @@ @Override public void fileModified(final File file) { for (final ConfigurationListener listener : listeners) { - final Thread thread = new Log4jThread(new ReconfigurationWorker(listener, reconfigurable)); - thread.setDaemon(true); - thread.start(); + LoggerContext.getContext(false).submitDaemon(new ReconfigurationWorker(listener, reconfigurable)); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java index e8286c1..3da469d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -328,8 +329,15 @@ } /** - * Shuts down the given logging context. - * @param ctx the logging context to shut down, may be null. + * Shuts down the given logger context. This request does not wait for Log4j tasks to complete. + *

+ * Log4j starts threads to perform certain actions like file rollovers; calling this method will not wait until the + * rollover thread is done. When this method returns, these tasks' status are undefined, the tasks may be done or + * not. + *

+ * + * @param ctx + * the logger context to shut down, may be null. */ public static void shutdown(final LoggerContext ctx) { if (ctx != null) { @@ -337,6 +345,30 @@ } } + /** + * Blocks until all Log4j tasks have completed execution after a shutdown request, or the timeout occurs, or the + * current thread is interrupted, whichever happens first. + *

+ * Log4j can start threads to perform certain actions like file rollovers, calling this method with a timeout will + * block until the rollover thread is done. + *

+ * + * @param ctx + * the logger context to shut down, may be null. + * @param timeout + * the maximum time to wait + * @param timeUnit + * the time unit of the timeout argument + * @return {@code true} if the logger context terminated and {@code false} if the timeout elapsed before + * termination. + */ + public static boolean shutdown(final LoggerContext ctx, final long timeout, final TimeUnit timeUnit) { + if (ctx != null) { + return ctx.stop(timeout, timeUnit); + } + return true; + } + private Configurator() { // empty } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java index 10f343a..2c51758 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java @@ -59,7 +59,7 @@ public static final String DOMAIN = "org.apache.logging.log4j2"; private static final String PROPERTY_DISABLE_JMX = "log4j2.disable.jmx"; private static final String PROPERTY_ASYNC_NOTIF = "log4j2.jmx.notify.async"; - private static final String THREAD_NAME_PREFIX = "log4j2.jmx.notif"; + private static final String THREAD_NAME_PREFIX = "jmx.notif"; private static final StatusLogger LOGGER = StatusLogger.getLogger(); static final Executor executor = isJmxDisabled() ? null : createExecutor(); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java index 752fc13..74a3834 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/JmsServer.java @@ -107,6 +107,13 @@ jmsManager.release(); } + @Override + public boolean stop(long timeout, TimeUnit timeUnit) { + stop(); + return true; + } + + @Override public boolean isStarted() { return state.get() == State.STARTED; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java index 45ae6f5..1aab6c7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java @@ -24,6 +24,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.apache.logging.log4j.Logger; @@ -163,6 +164,12 @@ } } + @Override + public boolean stop(long timeout, TimeUnit timeUnit) { + stop(); + return true; + } + private void removeShutdownHook() { final Thread shutdownThread = shutdownHookRef.get(); if (shutdownThread != null) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadFactory.java index 333ca0a..2f974e4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadFactory.java @@ -40,6 +40,17 @@ return new Log4jThreadFactory(threadNamePrefix, true, Thread.NORM_PRIORITY); } + /** + * Creates a new thread factory. + * + * @param threadNamePrefix + * The thread name prefix. + * @return a new daemon thread factory. + */ + public static Log4jThreadFactory createThreadFactory(final String threadNamePrefix) { + return new Log4jThreadFactory(threadNamePrefix, false, Thread.NORM_PRIORITY); + } + private static final AtomicInteger FACTORY_NUMBER = new AtomicInteger(1); private static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1); private final boolean daemon; diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RandomRollingAppenderOnStartupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RandomRollingAppenderOnStartupTest.java index 2c6fdd3..c24fa50 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RandomRollingAppenderOnStartupTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RandomRollingAppenderOnStartupTest.java @@ -57,7 +57,7 @@ public LoggerContextRule loggerContextRule; public RandomRollingAppenderOnStartupTest(final String configFile) { - this.loggerContextRule = new LoggerContextRule(configFile); + this.loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(configFile); } @Before diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java index 6de7d7d..d04de19 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java @@ -49,7 +49,7 @@ private static final String DIR = "target/rolling-cron"; private static final String FILE = "target/rolling-cron/rollingtest.log"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCustomDeleteActionTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCustomDeleteActionTest.java index 2e48ef8..3b30bd8 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCustomDeleteActionTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCustomDeleteActionTest.java @@ -37,7 +37,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete.xml"; private static final String DIR = "target/rolling-with-delete/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java index e2e59e6..23dcffa 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java @@ -47,7 +47,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete-accum-count1.xml"; private static final String DIR = "target/rolling-with-delete-accum-count1/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java index e03c53a..f636275 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java @@ -47,7 +47,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete-accum-count2.xml"; private static final String DIR = "target/rolling-with-delete-accum-count2/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedSizeTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedSizeTest.java index f6bfee3..f580b79 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedSizeTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedSizeTest.java @@ -37,7 +37,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete-accum-size.xml"; private static final String DIR = "target/rolling-with-delete-accum-size/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteMaxDepthTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteMaxDepthTest.java index bef09c4..740f9ec 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteMaxDepthTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteMaxDepthTest.java @@ -44,7 +44,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete-maxdepth.xml"; private static final String DIR = "target/rolling-with-delete-depth/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java index 081a52e..7d335f0 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java @@ -46,7 +46,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete-nested.xml"; private static final String DIR = "target/rolling-with-delete-nested/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptFri13thTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptFri13thTest.java index a7dcb48..38dca95 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptFri13thTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptFri13thTest.java @@ -36,7 +36,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete-script-fri13th.xml"; private static final String DIR = "target/rolling-with-delete-script-fri13th/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptTest.java index f1c3888..91da1a7 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteScriptTest.java @@ -34,7 +34,7 @@ private static final String CONFIG = "log4j-rolling-with-custom-delete-script.xml"; private static final String DIR = "target/rolling-with-delete-script/test"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderNoUnconditionalDeleteTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderNoUnconditionalDeleteTest.java index d7834f5..0d34be6 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderNoUnconditionalDeleteTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderNoUnconditionalDeleteTest.java @@ -58,7 +58,7 @@ public RollingAppenderNoUnconditionalDeleteTest(final String configFile, final String dir) { this.directory = new File(dir); - this.loggerContextRule = new LoggerContextRule(configFile); + this.loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(configFile); deleteDir(); deleteDirParent(); } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderOnStartupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderOnStartupTest.java index bc74585..aed2d19 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderOnStartupTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderOnStartupTest.java @@ -57,7 +57,7 @@ public LoggerContextRule loggerContextRule; public RollingAppenderOnStartupTest(final String configFile) { - this.loggerContextRule = new LoggerContextRule(configFile); + this.loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(configFile); } @Before diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderSizeTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderSizeTest.java index d354b41..6e73ab3 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderSizeTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderSizeTest.java @@ -94,7 +94,7 @@ public RollingAppenderSizeTest(final String configFile, final String fileExtension, final boolean createOnDemand) { this.fileExtension = fileExtension; this.createOnDemand = createOnDemand; - this.loggerContextRule = new LoggerContextRule(configFile); + this.loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(configFile); this.chain = loggerContextRule.withCleanFoldersRule(DIR); } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeAndSizeTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeAndSizeTest.java index bec238e..ad0ea64 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeAndSizeTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeAndSizeTest.java @@ -38,9 +38,11 @@ */ public class RollingAppenderTimeAndSizeTest { + private static final String CONFIG = "log4j-rolling3.xml"; + private static final String DIR = "target/rolling3/test"; - public static LoggerContextRule loggerContextRule = new LoggerContextRule("log4j-rolling3.xml"); + public static LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeTest.java index aa99595..089908d 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderTimeTest.java @@ -40,7 +40,7 @@ private static final String CONFIG = "log4j-rolling2.xml"; private static final String DIR = "target/rolling2"; - private final LoggerContextRule loggerContextRule = new LoggerContextRule(CONFIG); + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerHeaderFooterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerHeaderFooterTest.java index af668d8..6734d6b 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerHeaderFooterTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerHeaderFooterTest.java @@ -39,10 +39,11 @@ */ public class RollingRandomAccessFileManagerHeaderFooterTest { + private static final String CONFIG = "RollingRandomAccessFileAppenderHeaderFooterTest.xml"; private static final String DIR = "target/RollingRandomAccessFileAppenderHeaderFooterTest/"; private static final String LOGFILE = "target/RollingRandomAccessFileAppenderHeaderFooterTest.log"; - public LoggerContextRule loggerContextRule = new LoggerContextRule("RollingRandomAccessFileAppenderHeaderFooterTest.xml"); + public LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); @Rule public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java index 697e119..c3aaef9 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/junit/LoggerContextRule.java @@ -18,6 +18,8 @@ import static org.junit.Assert.assertNotNull; +import java.util.concurrent.TimeUnit; + import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Logger; @@ -41,12 +43,18 @@ */ public class LoggerContextRule implements TestRule { + public static LoggerContextRule createShutdownTimeoutLoggerContextRule(String config) { + return new LoggerContextRule(config, 10, TimeUnit.SECONDS); + } + private static final String SYS_PROP_KEY_CLASS_NAME = "org.apache.logging.log4j.junit.LoggerContextRule#ClassName"; private static final String SYS_PROP_KEY_DISPLAY_NAME = "org.apache.logging.log4j.junit.LoggerContextRule#DisplayName"; private final String configLocation; private LoggerContext context; private Class contextSelectorClass; private String testClassName; + private long shutdownTimeout; + private TimeUnit shutdownTimeUnit; /** * Constructs a new LoggerContextRule for a given configuration file. @@ -65,11 +73,21 @@ * path to configuration file * @param contextSelectorClass * custom ContextSelector class to use instead of default - * @since 2.5 */ public LoggerContextRule(final String configLocation, final Class contextSelectorClass) { + this(configLocation, contextSelectorClass, 0, null); + } + + public LoggerContextRule(final String configLocation, final Class contextSelectorClass, + final long shutdownTimeout, final TimeUnit shutdownTimeUnit) { this.configLocation = configLocation; this.contextSelectorClass = contextSelectorClass; + this.shutdownTimeout = shutdownTimeout; + this.shutdownTimeUnit = shutdownTimeUnit; + } + + public LoggerContextRule(String config, int shutdownTimeout, TimeUnit shutdownTimeUnit) { + this(config, null, shutdownTimeout, shutdownTimeUnit); } @Override @@ -94,7 +112,10 @@ try { base.evaluate(); } finally { - Configurator.shutdown(context); + if (!Configurator.shutdown(context, shutdownTimeout, shutdownTimeUnit)) { + StatusLogger.getLogger().error("Logger context {} did not shutdown completely after {} {}.", + context.getName(), shutdownTimeout, shutdownTimeUnit); + } context = null; contextSelectorClass = null; StatusLogger.getLogger().reset();