About
This article shows you how to configure Log4j 2.2 programmatically by extending the actual configuration.
Why extend the actual configuration?
Because Log4J gives the possibility to reload a configuration file. If this were the case, the programmatic configuration would be lost.
Steps
Steps 1: Programmatic Configuration
In this step, we create a configuration that extends an Xml Configuration (ie Log4j.xml)
- We add a logger called TEST_LOGGER
- that gets two programmatically created Appenders:
- a file Appender
- a console Appender
public class Log4JConfiguration extends XmlConfiguration {
public static final String TEST_LOGGER = Log4JConfiguration.class.getName();
public Log4JConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
super(loggerContext, source);
}
@Override
protected void doConfigure() {
super.doConfigure();
PatternLayout defaultLayout = PatternLayout.createDefaultLayout();
FileAppender fileAppender = FileAppender.newBuilder()
.setName("TestLogFile")
.setConfiguration(this)
.withFileName("test.log")
.setLayout(defaultLayout)
.setImmediateFlush(true)
.build();
this.addAppender(fileAppender);
ConsoleAppender console = ConsoleAppender.newBuilder()
.setName("TestConsole") // mandatory
.setConfiguration(this)
.setTarget(ConsoleAppender.Target.SYSTEM_OUT)
.build();
testLogger.addAppender(console, null, null);
LoggerConfig testLogger = LoggerConfig.newBuilder()
.withLoggerName(TEST_LOGGER)
.withAdditivity(false)
.withConfig(this)
.withLevel(Level.INFO)
.build();
// Add the appenders to the logger
testLogger.addAppender(fileAppender, null, null);
testLogger.addAppender(myFileAppender,null, null);
// Add the logger to the configuration
addLogger(testLogger.getName(), testLogger);
}
}
Step 2: Advertise it with a ConfigurationFactory
The below code is a factory that:
- is registered via the @Plugin
- return the previous Log4JConfiguration
When a Log4j logger is asked for the first time, it invokes this factory and build the configuration.
@Plugin(name = "Log4jConfigurationFactoryPlugin", category = ConfigurationFactory.CATEGORY)
@Order(10)
public class Log4jConfigurationFactoryPlugin extends ConfigurationFactory {
/**
* Valid file extensions for XML files.
*/
public static final String[] SUFFIXES = new String[]{".xml", "*"};
/**
* Return the Configuration.
*
* @param source The InputSource.
* @return The Configuration.
*/
@Override
public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
return new Log4JConfiguration(loggerContext, source);
}
/**
* Returns the file suffixes for XML files.
*
* @return An array of File extensions.
*/
public String[] getSupportedTypes() {
return SUFFIXES;
}
}
Annexes
Remark on Appenders
Note that Appenders created with the builder factory (and not with the Node class representing the file loaded) does not support out-of-the-box lookups to uses variables from the context.
You may then: * add them in your original configuration and * also create a new log4j configuration with new Appenders (See below)
How to load another log4j configuration
You can do it:
- with the xinclude syntax
- via the composite configuration where the log4j2.configurationFile property contains a list of files separated by comma.
- or programmatically
LoggerContext loggerContext = this.getLoggerContext();
// Get your configuration as string
String result = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration>....</Configuration>";
InputStream stream = new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8));
ConfigurationSource configurationSource = new ConfigurationSource(stream);
Configuration currentReadConfiguration = ConfigurationFactory.getInstance().getConfiguration(loggerContext, configurationSource);
currentReadConfiguration.initialize();
// then you can access the appenders
Appender appender = currentReadConfiguration.getAppender("myAppenderName");