log4j2 runtime reconfiguration not working - empty file created

3.1k views Asked by At

I am trying to manually reconfigure log4j2 at runtime but getting partial success.

Here is the relevant code:

package examples.test;

public class ABCImpl implements XYX{

    static Logger logger;

public void initialize(){
    LoggerContext ctx = null;
    Configuration config =  null;
    Map mp = null;

    ctx = (LoggerContext) LogManager.getContext(false);
        config = (Configuration)ctx.getConfiguration();
    mp = config.getAppenders();
    System.out.println("***<Provider o/p follows> Before logger re-configuration:");
        System.out.println("\tAppenders:" + mp.keySet());

    //reconfiguration attempt - starts
    try{
        URI configuration = this.getClass().getResource("/log4j2.xml").toURI();
        ctx  = Configurator.initialize(this.getClass().getName(), null, configuration);
    }catch(Exception e){
        System.out.println("\t-------Exception encountered-------");
        e.printStackTrace();
    }

        config = (Configuration) ctx.getConfiguration();
    mp = config.getAppenders();
    System.out.println("***<Provider o/p follows> After logger re-configuration:");
    System.out.println("\tAppenders:" + mp.keySet());

    //reconfiguration attempt - ends

    logger = LogManager.getLogger(this.getClass().getName());
}

public void myBusinessMethod(){
        logger.info("Entry..............");
        logger.info("Exit..............");
    }
}

This class is actually part of a jar file and am running it inside an application server which guarantees that my initialize method would be called as soon as my class is instantiated.

Here is my log4j2.xml, which I have packed into jar's root:

<?xml version="1.0" encoding="UTF-8"?>

    <!-- Don't forget to set system property
    -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
    to make all loggers asynchronous. -->

<Configuration status="info">
    <Appenders>
    <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
        <RollingRandomAccessFile name="Appender1" fileName="servers/${sys:weblogic.Name}/logs/Auditing_${sys:weblogic.Name}.log" immediateFlush="true" append="false" filePattern="servers/${sys:weblogic.Name}/logs/archive/Auditing_${sys:weblogic.Name}-%d{yyyy-MM-dd-HH}-%i.log.gz">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m %ex%n</Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="250 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingRandomAccessFile>
        <Async name="Async1">
            <AppenderRef ref="Appender1"/>
        </Async>
    </Appenders>
    <Loggers>
        <Logger name="examples.test.ABCImpl" level="info" includeLocation="false" additivity="false">
            <AppenderRef ref="Appender1"/>
        </Logger>
        <Root level="info" includeLocation="false">
            <AppenderRef ref="Appender1"/>
        </Root>
    </Loggers>
</Configuration>

The problem is even though the log file gets created, but nothing gets logged in it. The output in std out that I get is :

ABCImpl.initialize
        activeHandlerEntries.length=1
***<Provider o/p follows> Before logger re-configuration:
        Appenders:[Console]
***<Provider o/p follows> After logger re-configuration:
        Appenders:[Async1, Appender1]

Just to make sure that my log4j2.xml and the piece of code is okay without runtime reconfiguration stuff, if I use -Dlog4j.configurationFile=file:SOME_PATH_OUTSIDE_JAR/log4j2.xml log gets populated as expected.

Please help.

2

There are 2 answers

0
Abid On

Hours of helpless staring at the code and trying my hands here and there, I found that if I get the Logger from newly initialized context (which is obtained from Configurator.initialize) instead of LogManager, log gets populated with data.

Without this trick, not sure if I needed to call any additional API methods (just after Configurator.initialize) to hook the new context into LogManager so that I could have continued traditional approach of getting Logger from LogManager.

...
    config = (Configuration) ctx.getConfiguration();
    mp = config.getAppenders();
    System.out.println("***<Provider o/p follows> After logger re-configuration:");
    System.out.println("\tAppenders:" + mp.keySet());

    //reconfiguration attempt - ends


    //Following line screwed me up for over three weeks
    //logger = LogManager.getLogger(this.getClass().getName());

    //workaround:
    logger = ctx.getLogger(this.getClass().getName());
}

public void myBusinessMethod(){
    logger.info("Entry..............");
    logger.info("Exit..............");
}
....
}
3
Florian Neumann On

I wondered, if deriving the context for initialization by

ctx = (LoggerContext) LogManager.getContext(false);

may be the problem, since you have to access the "current" context with

ctx = (LoggerContext) LogManager.getContext(true);

according to the Log4j2 API. I would really appreciate a clarification, since i'm trying to programmatically configuring Log4J2, too.