LOG4J2 - How to create a File appender programmatically?

Cisco picture Cisco · Dec 13, 2013 · Viewed 9k times · Source

I need to set a File appender programmatically.

I want to set my appenders on the log4j2.xml BUT I want to write (programmatically) only to one of these appenders at runtime.

<Configuration status="WARN">
<Appenders>
    <File name="File_1" fileName="c:/FILE_1.log" >
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
    <File name="File_2" fileName="c:/FILE_2.log" >
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
    <File name="File_3" fileName="c:/FILE_3.log" >
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
</Appenders>
<Loggers>
    <Root level="debug">
        <AppenderRef ref="File_1"/>
        <AppenderRef ref="File_2"/>
        <AppenderRef ref="File_3"/>
    </Root>
</Loggers>

EXAMPLE:

     if(condition.equals("A")){
             //write log on File_1
         }else if(condition.equals("B")){
              //write log on File_2
         }else {
              //write log on File_3
         }

Looking on the various tutorials in Internet they explain how to add appenders programmatically at runtime; is there a way to select at runtime only one of the appenders defined in xml configuration file?

A FIRST SOLUTION:

I've tried to solve the problem in the following way, but I am not satisfied by my solution because the files defined in the log4j2.xml are not removed programmatically AND the first Appender is never removed! I think there's a bug in log4j2: https://issues.apache.org/jira/browse/LOG4J2-135 (the Status is Solved but I don't think so...I'm using log4j-2.0-beta9)

static org.apache.logging.log4j.core.Logger coreLogger =    
(org.apache.logging.log4j.core.Logger)LogManager.getLogger(MyClass.class.getName());
static LoggerContext context = (LoggerContext)coreLogger.getContext();
static BaseConfiguration configuration = (BaseConfiguration)context.getConfiguration();

static String FILE_1 = "File_1";
static String FILE_2 = "File_2";
static String FILE_3 = "File_3";

private static Map<String, Boolean> appendersMap;
static
{
    appendersMap = new HashMap<String, Boolean>();
    appendersMap.put(FILE_1, true);
    appendersMap.put(FILE_2, true);
    appendersMap.put(FILE_3, true);
}

/**
 * Configure appenders.
 *
 * @param appender the appender
 */
public static void configureAppenders(String appender){

    if(appender.equals(FILE_1)){
        addAppenders(FILE_1);
        removeAppenders(FILE_2,FILE_3);
    }

    else if(appender.equals(FILE_3)){
        addAppenders(FILE_3);
        removeAppenders(FILE_1,FILE_2);
    }

    else if(appender.equals(FILE_2)){
        addAppenders(FILE_2);
        removeAppenders(FILE_1,FILE_3);
    }

}

private static void addAppenders(String appender){
    if (!appendersMap.get(appender)){
        appendersMap.put(appender, true);
        coreLogger.addAppender(configuration.getAppender(appender));
    }
}

private static void removeAppenders(String... appenders){
    for(String appender : appenders){
        appendersMap.put(appender, false);
        coreLogger.removeAppender(configuration.getAppender(appender));
    }
}

Answer

Remko Popma picture Remko Popma · Nov 2, 2015

It has been a while since this question was asked, and there have been a number of requests to support better programmatic configuration for Log4j 2. As of Log4j 2.4, API was added to log4j-core to facilitate programmatic configuration.

The new ConfigurationBuilder API allows users to construct component definitions. With this API, there is no need to work directly with actual configuration objects (like LoggerConfig and FileAppender) which require a lot of knowledge on how Log4j works under the hood. Component definitions are added to the ConfigurationBuilder, and once all the definitions have been collected all the actual configuration objects (like Loggers and Appenders) are constructed. It feels a bit like the XML configuration syntax, except that you are writing Java code.

Note that the new ConfigurationBuilder API allows user code to create a new configuration or completely replace the existing configuration. If your use case is different, and you want to programmatically modify (rather than replace) an existing configuration after Log4j was started, then you will need to work with actual configuration objects. In that case, please see the Programmatically Modifying the Current Configuration after Initialization section of the manual.

If you do need to work directly with the internal configuration objects, please note that you need to call either LoggerContext.updateLoggers() or Configurator.initialize(Configuration) at the end to make your changes take effect. For example:

final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
... // make changes
ctx.updateLoggers(); // NECESSARY!