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
<?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>
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
@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
}
Solutions
1: Remove APPENDER ref from root category
<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>
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>
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.