Custom ConfigurationFactory combined with configuration file in log4j2

Viewed 6782

I'm trying to Initialize Log4j by Combining Configuration File with Programmatic Configuration.

I followed the manual (though it's syntax isn't quite right and it's outdated), which resulted in the following classes:

CustomConfigurationFactory.java:

package factory;

import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.Order;
import org.apache.logging.log4j.core.config.plugins.Plugin;

import java.net.URI;

@Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY)
@Order(1)
public class CustomConfigurationFactory extends ConfigurationFactory {

    /**
     * Valid file extensions for XML files.
     */
    private static final String[] SUFFIXES = new String[]{".xml", "*"};

    /**
     * Return the Configuration.
     *
     * @param source The InputSource.
     * @return The Configuration.
     */
    public Configuration getConfiguration(LoggerContext context, ConfigurationSource source) {

        return new CustomConfiguration(context, source);

    }

    /**
     * Returns the file suffixes for XML files.
     * @return An array of File extensions.
     */
    public String[] getSupportedTypes() {

        return SUFFIXES;

    }

}

CustomConfiguration.java:

package factory;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.xml.XmlConfiguration;

import java.util.Map;

public class CustomConfiguration extends XmlConfiguration {

    CustomConfiguration(LoggerContext context, ConfigurationSource configSource) {

        super(context, configSource);

    }

    @Override
    protected void doConfigure() {

        super.doConfigure();

        final LoggerConfig rootLogger = getRootLogger();

        final Map<String, Appender> appenderMap = rootLogger.getAppenders();

        if (MainClass.DEBUG) {

            rootLogger.addAppender(appenderMap.get("Console"), Level.ALL, null);

        } else {

            rootLogger.addAppender(appenderMap.get("Mail"), Level.ERROR, null);

        }

    }
}

Now, when running this and calling ConfigurationFactory.setConfigurationFactory(new CustomConfigurationFactory()) before any calls to the Logging API, I'm getting output to the console in the form of

ERROR StatusLogger Reconfiguration failed: No configuration found for 'someNumbersAndChars' at 'null' in 'null'

While debugging this, I found out that this is printed the first time I'm acquiring a Logger. The reason for that is that, if a custom ConfigurationFactory is supplied, the implementation of ConfigurationFactory.getConfiguration(LoggerContext, String, URI) by ConfigurationFactory's private subclass Factory (which is the default factory) will be overridden by ConfigurationFactory's implementation.

And ConfigurationFactory's implementation simply returns null if the URI is so, while ConfigurationFactory.Factory's implementation nevertheless returns a valid configuration.

(link to source)

My first idea now would be to override these overloads of ConfigurationFactory.getConfiguration() in my custom factory, but there has to be another way, right? ;)

2 Answers

I think this may be due to the @Order() you have. I couldn't find any documentation that explained how the specified orders are compared, but my belief is that higher numbers are used by preferrence. The default XmlConfigurationFactory has an order of 5 (log4j version 2.13.1) so if you change your order to be 6 you should get your factory as you require.

Related