log4j:ERROR Attempted to append to closed appender named [*]

During system running, sometimes we can see the error message from log4j log:

log4j:ERROR Attempted to append to closed appender named [*].

Here I would like to talk about one use case which could cause this problem.

Most of the components in our project could use log4j, and some of them could have duplicated log4j configuration file:

  • log4j.xml and log4j.properties
Here is a log4j.xml demo for my test:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
	debug="false">

	<!-- Main log file for general output from SSP server. -->
	<appender name="SYSTEM"
		class="com.gemalto.util.log4j.DailyMaxRollingFileAppender">
		<param name="File" value="logs/logFromlog4jXMLAppender.log" />
		<param name="Append" value="true" />
		<param name="DatePattern" value="'.'yyyy-MM-dd" />
		<param name="maxNumberOfDays" value="7" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %-5p [GTO] [%c] %X{MSISDN} %X{IMEI} %m%n" />
		</layout>
	</appender>

	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<param name="Threshold" value="INFO" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n" />
		</layout>
	</appender>

	<category name="com.test" additivity="true">
		<priority value="DEBUG" />
		<appender-ref ref="SYSTEM" />
	</category>


	<root>
		<priority value="INFO" />
		<appender-ref ref="SYSTEM" />
		<appender-ref ref="CONSOLE" />
	</root>

</log4j:configuration>
 
Also, there is a log4j.properties in same location as log4j.xml:
log4j.rootCategory=DEBUG, stdout
log4j.logger.com=DEBUG, smsdriver

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n

log4j.appender.smsdriver=com.gemalto.util.log4j.DailyMaxRollingFileAppender
log4j.appender.smsdriver.file=logs/logFromlog4jPropertiesAppender.log
log4j.appender.smsdriver.append=true
log4j.appender.smsdriver.layout=org.apache.log4j.PatternLayout
log4j.appender.smsdriver.layout.ConversionPattern=%d %p [%c] - <%m> %F %n
 
During our test, we will load log4j.xml first to configure Logger Hierarchy, and then load log4j.properties to configure Logger Hierarchy. We will see what happen according to logs.
Here is the test code on log4j configuration:
@Test
	public void testlog4jConfigurator() throws Exception {
		System.setProperty("log4j.debug", "true");
		Logger logger = org.apache.log4j.Logger
				.getLogger("com.DOMConfigurator"); 
		logger.info("Logger before DOMConfigurator."); //#1

		System.setProperty("log4j.defaultInitOverride", "true");

		URL log4jXml = TempTest.class.getResource("/log4j.xml");
		URL log4jProperties = TempTest.class.getResource("/log4j.properties");

		DOMConfigurator.configure(log4jXml);
		Logger loggerDOMConfigurator = org.apache.log4j.Logger
				.getLogger("com.DOMConfigurator");
		loggerDOMConfigurator.info("Logger loggerDOMConfigurator.");//#2
		// assertEquals(logger, loggerDOMConfigurator);
		Logger loggerDOMConfigurator2 = org.apache.log4j.Logger
				.getLogger("com.test.2");
		Logger loggerDOMConfigurator3 = org.apache.log4j.Logger
				.getLogger("com.test.3");

		PropertyConfigurator.configureAndWatch(getFilePath(log4jProperties));
		loggerDOMConfigurator.info("Logger loggerPropertyConfigurator."); //#3
		// assertEquals(loggerPropertyConfigurator, loggerDOMConfigurator);

		loggerDOMConfigurator2.info("Logger loggerDOMConfigurator2."); //#4
		loggerDOMConfigurator3.info("Logger loggerDOMConfigurator3."); //#5
	}
 
By default, org.apache.log4j.LogManager will load "log4j.xml", so we can see #1 and #2 output into file: logs/logFromlog4jXMLAppender.log:
2011-12-22 10:17:52,669 INFO [GTO] [com.DOMConfigurator] Logger before DOMConfigurator.
2011-12-22 10:17:52,669 INFO [GTO] [com.DOMConfigurator] Logger loggerDOMConfigurator.
 
#3 will output into file: logs/logFromlog4jPropertiesAppender.log:
2011-12-22 10:17:52,684 INFO [com.DOMConfigurator] - <Logger loggerPropertyConfigurator.> Log4jTest.java
 
#4 and #5 will be log into file logs/logFromlog4jPropertiesAppender.log because we configure a Category: log4j.logger.com. BUT we cannot see any log in logs/logFromlog4jXMLAppender.log. AND we will see the following errors in console:
log4j:ERROR Attempted to append to closed appender named [SYSTEM].
log4j:ERROR Attempted to append to closed appender named [SYSTEM].
Why this situation comes? See log4j source code analyze. Here I will list some solutions to solve this problem.

Solutions

Here I would like to list some workaround.

1: Remove APPENDER ref from root category

We can remove the appender-ref to "SYSTEM" in log4j.xml/root like following:
<category name="com.test" additivity="true">
		<priority value="DEBUG" />
		<appender-ref ref="SYSTEM" />
	</category>


	<root>
		<priority value="INFO" />
		<!--appender-ref ref="SYSTEM" /-->
		<appender-ref ref="DMCONSOLE" />
	</root>
 There won't be any error message refer to appender closed.  In this case, we can see #4 and #5 logs in both files.
(Please be aware of that #2 won't be logged in this case.)

2: Remvoe APPENDER ref in specific Category

We can also remove the appender-ref to "SYSTEM" in log4j.xml/Category/com.test like following:

<category name="com.test" additivity="true">
		<priority value="DEBUG" />
		<!--appender-ref ref="SYSTEM" /-->
	</category>


	<root>
		<priority value="INFO" />
		<appender-ref ref="SYSTEM" />
		<appender-ref ref="DMCONSOLE" />
	</root>
 In this case, there also won't be any error message refer to "appender closed". But #4 and #5 will only be logged in file: logs/logFromlog4jPropertiesAppender.log.

Conclusion

As descript in log4j, there will be only one Logger instance with specific name. Log4j allows logging requests to print to multiple destinations. In log4j speak, an output destination is called an appender. Currently, appenders exist for the console, files, GUI components, remote socket servers, JMS, NT Event Loggers, and remote UNIX Syslog daemons. It is also possible to log asynchronously.

More than one appender can be attached to a logger. 

The addAppender method adds an appender to a given logger. Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy.

As we can see, Appender related to logger could be modified during second Configurator. So the error comes.

相关推荐