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? ;)
I solved this problem by calling
System.setProperty("log4j.configurationFactory", CustomConfigurationFactory.class.getName());
As an alternative use this JVM start parameter:-Dlog4j.configurationFactory=factory.CustomConfigurationFactory
instead of
ConfigurationFactory.setConfigurationFactory(new CustomConfigurationFactory());
before accessing the LogManager
for the first time.
-
EDIT: You can also configure this setting in the file log4j2.component.properties
.
Content:
log4j.configurationFactory=factory.CustomConfigurationFactory
By doing so you can be sure this setting is applied before any logger classes are loaded and avoid the potential problems of class initialization order.
If you look for usage of org.apache.logging.log4j.util.PropertiesUtil
in the Log4j2 sources you can find all settings that can be configured that way.
-
While analyzing / debugging the problem i noticed that my ConfigurationFactory
was created, but not used to get the config.
Finally i found this passage in the docs, which explains it all (we probably did not call setConfigurationFactory
early enough):
During initialization, Log4j 2 will search for available ConfigurationFactories and then select the one to use. The selected ConfigurationFactory creates the Configuration that Log4j will use. Here is how Log4j finds the available ConfigurationFactories:
- A system property named "log4j.configurationFactory" can be set with the name of the ConfigurationFactory to be used.
- ConfigurationFactory.setConfigurationFactory(ConfigurationFactory) can be called with the instance of the ConfigurationFactory to be used. This must be called before any other calls to Log4j.
- A ConfigurationFactory implementation can be added to the classpath and configured as a plugin in the "ConfigurationFactory" category. The Order annotation can be used to specify the relative priority when multiple applicable ConfigurationFactories are found.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With