You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2019/02/02 23:36:35 UTC
[logging-log4j2] branch release-2.x updated: LOG4j2-2061 - Use the
file pattern as the FileManager name when no filename is present.
This is an automated email from the ASF dual-hosted git repository.
rgoers pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/release-2.x by this push:
new 40bed92 LOG4j2-2061 - Use the file pattern as the FileManager name when no filename is present.
40bed92 is described below
commit 40bed92d80e32e999da27f2f507e81d4d6186d01
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sat Feb 2 16:36:22 2019 -0700
LOG4j2-2061 - Use the file pattern as the FileManager name when no filename is present.
---
.../core/appender/rolling/RollingFileManager.java | 10 +-
.../core/appender/ReconfigureAppenderTest.java | 170 +++++++++++++++++++++
src/changes/changes.xml | 3 +
3 files changed, 179 insertions(+), 4 deletions(-)
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 b8f9ef4..6867855 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
@@ -100,7 +100,8 @@ public class RollingFileManager extends FileManager {
final boolean append, final long size, final long initialTime, final TriggeringPolicy triggeringPolicy,
final RolloverStrategy rolloverStrategy, final String advertiseURI,
final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
- super(fileName, os, append, false, advertiseURI, layout, writeHeader, buffer);
+ super(fileName != null ? fileName : pattern, os, append, false, advertiseURI, layout, writeHeader,
+ buffer);
this.size = size;
this.initialTime = initialTime;
this.triggeringPolicy = triggeringPolicy;
@@ -116,7 +117,8 @@ public class RollingFileManager extends FileManager {
final boolean append, final boolean createOnDemand, final long size, final long initialTime,
final TriggeringPolicy triggeringPolicy, final RolloverStrategy rolloverStrategy,
final String advertiseURI, final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
- super(loggerContext, fileName, os, append, false, createOnDemand, advertiseURI, layout, writeHeader, buffer);
+ super(loggerContext, fileName != null ? fileName : pattern, os, append, false, createOnDemand,
+ advertiseURI, layout, writeHeader, buffer);
this.size = size;
this.initialTime = initialTime;
this.triggeringPolicy = triggeringPolicy;
@@ -136,8 +138,8 @@ public class RollingFileManager extends FileManager {
final String advertiseURI, final Layout<? extends Serializable> layout,
final String filePermissions, final String fileOwner, final String fileGroup,
final boolean writeHeader, final ByteBuffer buffer) {
- super(loggerContext, fileName, os, append, false, createOnDemand, advertiseURI, layout,
- filePermissions, fileOwner, fileGroup, writeHeader, buffer);
+ super(loggerContext, fileName != null ? fileName : pattern, os, append, false, createOnDemand,
+ advertiseURI, layout, filePermissions, fileOwner, fileGroup, writeHeader, buffer);
this.size = size;
this.initialTime = initialTime;
this.triggeringPolicy = triggeringPolicy;
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java
new file mode 100644
index 0000000..1df2519
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2019 Nextiva, Inc. to Present.
+ * All rights reserved.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.rolling.DirectWriteRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
+import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
+import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.util.Builder;
+import org.junit.Test;
+
+/**
+ * Class Description goes here.
+ * Created by rgoers on 2019-02-02
+ */
+public class ReconfigureAppenderTest {
+ private RollingFileAppender appender;
+
+ @Test
+ public void addAndRemoveAppenderTest()
+ {
+ // this will create a rolling file appender and add it to the logger
+ // of this class. The file manager is created for the first time.
+ // see AbstractManager.getManager(...).
+ this.createAndAddAppender();
+
+ // let's write something to the logger to ensure the output stream is opened.
+ // We expect this call to create a a new output stream (which is does).
+ // see OutputStreamManager.writeToDestination(...).
+ Logger logger = (Logger)LogManager.getLogger(this.getClass());
+ logger.info("test message 1");
+
+ // this will close the rolling file appender and remove it from the logger
+ // of this class. We expect the file output stream to be closed (which is it)
+ // however the FileManager instance is kept in AbstractManager.MAP. This means that
+ // when we create a new rolling file appender with the DirectWriteRolloverStrategy
+ // this OLD file manager will be retrieved from the map (since it has the SAME file pattern)
+ // and this is a problem as the output stream on that file manager is CLOSED. The problem
+ // here is that we attempt to remove a file manager call NULL instead of FILE PATTERN
+ this.removeAppender();
+
+ // create a new rolling file appender for this logger. We expect this to create a new file
+ // manager as the old one should have been removed. Since the instance of this file manager
+ // is still in AbstractManager.MAP, it is returned and assigned to our new rolling file
+ // appender instance. The problem here is that the file manager is create with the name
+ // FILE PATTERN and that its output stream is closed.
+ this.createAndAddAppender();
+
+ // try and log something. This will not be logged anywhere. An exception will be thrown:
+ // Caused by: java.io.IOException: Stream Closed
+ logger.info("test message 2");
+
+ // remove the appender as before.
+ this.removeAppender();
+
+ // this method will use reflection to go and remove the instance of FileManager from the AbstractManager.MAP
+ // ourselves. This means that the rolling file appender has been stopped (previous method) AND its
+ // manager has been removed.
+ this.removeManagerUsingReflection();
+
+ // now that the instance of FileManager is not present in MAP, creating the appender will actually
+ // create a new rolling file manager, and put this in the map (keyed on file pattern again).
+ this.createAndAddAppender();
+
+ // because we have a new instance of file manager, this will create a new output stream. We can verify
+ // this by looking inside the filepattern.1.log file inside the working directory, and noticing that
+ // we have 'test message 1' followed by 'test message 3'. 'test message 2' is missing because we attempted
+ // to write while the output stream was closed.
+ logger.info("test message 3");
+
+ // possible fixes:
+ // 1) create the RollingFileManager and set it's name to FILE PATTERN when using DirectWriteRolloverStrategy
+ // 2) when stopping the appender (and thus the manager), remove on FILE PATTERN if DirectWriteRolloverStrategy
+ // 3) on OutputStreamManager.getOutputStream(), determine if the output stream is closed, and if it is create
+ // a new one. Note that this isn't really desirable as the only fix as if the file pattern had to change
+ // an instance of file manager would still exist in MAP, causing a resource leak.
+
+ // now the obvious problem here is that if multiple file appenders use the same rolling file manager. We may run
+ // into a case where the file manager is removed and the output stream is closed, and the remaining appenders
+ // may not work correctly. I'm not sure of the use case in this scenario, and if people actually do this
+ // but based on the code it would be possible. I have also not tested this scenario out as it is not the
+ // scenario we would ever use, but it should be considered while fixing this issue.
+ }
+ private void removeManagerUsingReflection()
+ {
+ try
+ {
+ Field field = AbstractManager.class.getDeclaredField("MAP");
+ field.setAccessible(true);
+
+ // Retrieve the map itself.
+ Map<String, AbstractManager> map =
+ (Map<String, AbstractManager>)field.get(null);
+
+ // Remove the file manager keyed on file pattern.
+ map.remove(appender.getFilePattern());
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void removeAppender()
+ {
+ Logger logger = (Logger)LogManager.getLogger(this.getClass());
+
+ // This call attempts to remove the file manager, but uses the name of the appender
+ // (NULL in this case) instead of PATTERN.
+ // see AbstractManager.stop(...).
+ appender.stop();
+ logger.removeAppender(appender);
+ }
+
+ private void createAndAddAppender()
+ {
+ ConfigurationBuilder<BuiltConfiguration> config_builder =
+ ConfigurationBuilderFactory.newConfigurationBuilder();
+
+ // All loggers must have a root logger. The default root logger logs ERRORs to the console.
+ // Override this with a root logger that does not log anywhere as we leave it up the the
+ // appenders on the logger.
+ config_builder.add(config_builder.newRootLogger(Level.INFO));
+
+ // Initialise the logger context.
+ LoggerContext logger_context =
+ Configurator.initialize(config_builder.build());
+
+ // Retrieve the logger.
+ Logger logger = (Logger) LogManager.getLogger(this.getClass());
+
+ Builder pattern_builder = PatternLayout.newBuilder().withPattern(
+ "[%d{dd-MM-yy HH:mm:ss}] %p %m %throwable %n");
+
+ PatternLayout pattern_layout = (PatternLayout) pattern_builder.build();
+
+ appender = RollingFileAppender
+ .newBuilder()
+ .withLayout(pattern_layout)
+ .withName("rollingfileappender")
+ .withFilePattern("target/filepattern.%i.log")
+ .withPolicy(SizeBasedTriggeringPolicy.createPolicy("5 MB"))
+ .withAppend(true)
+ .withStrategy(
+ DirectWriteRolloverStrategy
+ .newBuilder()
+ .withConfig(logger_context.getConfiguration())
+ .withMaxFiles("5")
+ .build())
+ .setConfiguration(logger_context.getConfiguration())
+ .build();
+
+ appender.start();
+
+ logger.addAppender(appender);
+ }
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index f268aaf..d7ca166 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -31,6 +31,9 @@
- "remove" - Removed
-->
<release version="2.11.2" date="2018-MM-DD" description="GA Release 2.11.2">
+ <action issue="LOG4J2-2061" dev="rgoers" type="fix">
+ Use the file pattern as the FileManager "name" when no filename is present.
+ </action>
<action issue="LOG4J2-2009" dev="rgoers" type="fix">
Expose LoggerContext.setConfiguration as a public method.
</action>