Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java =================================================================== --- log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java (revision 1591139) +++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java (working copy) @@ -30,7 +30,6 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.helpers.FileUtils; import org.apache.logging.log4j.status.StatusConsoleListener; -import org.apache.logging.log4j.status.StatusListener; import org.apache.logging.log4j.status.StatusLogger; /** @@ -38,8 +37,6 @@ */ public class StatusConfiguration { - @SuppressWarnings("UseOfSystemOutOrSystemErr") - private static final PrintStream DEFAULT_STREAM = System.out; private static final Level DEFAULT_STATUS = Level.ERROR; private static final Verbosity DEFAULT_VERBOSITY = Verbosity.QUIET; @@ -48,10 +45,12 @@ private volatile boolean initialized = false; - private PrintStream destination = DEFAULT_STREAM; + private PrintStream destination = System.out; + private boolean destinationCloseable = false; private Level status = DEFAULT_STATUS; private Verbosity verbosity = DEFAULT_VERBOSITY; private String[] verboseClasses; + private StatusConsoleListener listener; /** * Specifies how verbose the StatusLogger should be. @@ -95,32 +94,42 @@ */ public StatusConfiguration withDestination(final String destination) { try { - this.destination = parseStreamName(destination); + configDestination(destination); } catch (final URISyntaxException e) { this.error("Could not parse URI [" + destination + "]. Falling back to default of stdout."); - this.destination = DEFAULT_STREAM; + configSysOut(); } catch (final FileNotFoundException e) { this.error("File could not be found at [" + destination + "]. Falling back to default of stdout."); - this.destination = DEFAULT_STREAM; + configSysOut(); } return this; } - private PrintStream parseStreamName(final String name) throws URISyntaxException, FileNotFoundException { + private void configDestination(final String name) throws URISyntaxException, FileNotFoundException { if (name == null || name.equalsIgnoreCase("out")) { - return DEFAULT_STREAM; + configSysOut(); } if (name.equalsIgnoreCase("err")) { - return System.err; + configSysErr(); } final URI destination = FileUtils.getCorrectedFilePathUri(name); final File output = FileUtils.fileFromURI(destination); if (output == null) { - // don't want any NPEs, no sir - return DEFAULT_STREAM; + configSysOut(); } final FileOutputStream fos = new FileOutputStream(output); - return new PrintStream(fos, true); + this.destination = new PrintStream(fos, true); + this.destinationCloseable = true; + } + + private void configSysOut() { + this.destination = System.out; + this.destinationCloseable = false; + } + + private void configSysErr() { + this.destination = System.err; + this.destinationCloseable = false; } /** @@ -181,37 +190,18 @@ if (this.status == Level.OFF) { this.initialized = true; } else { - final boolean configured = configureExistingStatusConsoleListener(); - if (!configured) { - registerNewStatusConsoleListener(); - } + registerNewStatusConsoleListener(); migrateSavedLogMessages(); } } } - private boolean configureExistingStatusConsoleListener() { - boolean configured = false; - for (final StatusListener statusListener : this.logger.getListeners()) { - if (statusListener instanceof StatusConsoleListener) { - final StatusConsoleListener listener = (StatusConsoleListener) statusListener; - listener.setLevel(this.status); - if (this.verbosity == Verbosity.QUIET) { - listener.setFilters(this.verboseClasses); - } - configured = true; - } - } - return configured; - } - - private void registerNewStatusConsoleListener() { - final StatusConsoleListener listener = new StatusConsoleListener(this.status, this.destination); + this.listener = new StatusConsoleListener(this.status, this.destination); if (this.verbosity == Verbosity.QUIET) { - listener.setFilters(this.verboseClasses); + this.listener.setFilters(this.verboseClasses); } - this.logger.registerListener(listener); + this.logger.registerListener(this.listener); } private void migrateSavedLogMessages() { @@ -221,4 +211,12 @@ this.initialized = true; this.errorMessages.clear(); } + + public void stop() { + this.logger.removeListener(this.listener); + if (this.destinationCloseable) { + this.destination.close(); + this.destinationCloseable = false; + } + } } \ No newline at end of file Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XMLConfiguration.java =================================================================== --- log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XMLConfiguration.java (revision 1591139) +++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XMLConfiguration.java (working copy) @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; + import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -47,6 +48,8 @@ import org.apache.logging.log4j.core.config.status.StatusConfiguration; import org.apache.logging.log4j.core.helpers.Loader; import org.apache.logging.log4j.core.helpers.Patterns; +import org.apache.logging.log4j.status.StatusConsoleListener; +import org.apache.logging.log4j.status.StatusLogger; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -70,6 +73,8 @@ private static final String LOG4J_XSD = "Log4j-config.xsd"; private final List status = new ArrayList(); + + private StatusConfiguration statusConfig; private Element rootElement; @@ -142,7 +147,7 @@ final Document document = newDocumentBuilder().parse(source); rootElement = document.getDocumentElement(); final Map attrs = processAttributes(rootNode, rootElement); - final StatusConfiguration statusConfig = new StatusConfiguration() + statusConfig = new StatusConfiguration() .withVerboseClasses(VERBOSE_CLASSES) .withStatus(getDefaultStatus()); for (final Map.Entry entry : attrs.entrySet()) { @@ -236,6 +241,7 @@ @Override public Configuration reconfigure() { + stopStatusConfig(); if (configFile != null) { try { final ConfigurationFactory.ConfigurationSource source = @@ -247,6 +253,18 @@ } return null; } + + @Override + public void stop() { + super.stop(); + stopStatusConfig(); + } + + private void stopStatusConfig() { + if (statusConfig != null) { + statusConfig.stop(); + } + } private void constructHierarchy(final Node node, final Element element) { processAttributes(node, element); Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JSONConfiguration.java =================================================================== --- log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JSONConfiguration.java (revision 1591139) +++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JSONConfiguration.java (working copy) @@ -50,6 +50,8 @@ private final List status = new ArrayList(); + private StatusConfiguration statusConfig; + private JsonNode root; private final File configFile; @@ -72,7 +74,7 @@ } } processAttributes(rootNode, root); - final StatusConfiguration statusConfig = new StatusConfiguration() + statusConfig = new StatusConfiguration() .withVerboseClasses(VERBOSE_CLASSES) .withStatus(getDefaultStatus()); for (final Map.Entry entry : rootNode.getAttributes().entrySet()) { @@ -139,6 +141,7 @@ @Override public Configuration reconfigure() { + stopStatusConfig(); if (configFile != null) { try { final ConfigurationFactory.ConfigurationSource source = @@ -150,6 +153,18 @@ } return null; } + + @Override + public void stop() { + super.stop(); + stopStatusConfig(); + } + + private void stopStatusConfig() { + if (statusConfig != null) { + statusConfig.stop(); + } + } private Node constructNode(final String name, final Node parent, final JsonNode jsonNode) { final PluginType type = pluginManager.getPluginType(name);