Reputation: 1649
Is it possible to add Log4J2 appenders programmatically using the specifications from the XML configuration?
I plan to define it all in the log4j2.xml and then pick appenders situationally like this (won't compile):
if (arg[0].equals("log") ) {
Logger.getLogger("loggerNameFromXMLConfig").addAppender("appenderNameFromXMLConfig");
} else {
//...
}
Upvotes: 57
Views: 72129
Reputation: 53
I ended up here looking for a solution to programmatically adding an Appender to log4j2. Here's a solution that works with the current log4j2 version, 2.24.1.
static final String APPENDER_NAME = "MyAppender";
Configuration configuration = LoggerContext.getContext().getConfiguration();
// I need a JsonTemplateLayout
Layout<?> layout =
JsonTemplateLayout.newBuilder()
.setConfiguration(configuration)
.setEventTemplateUri("classpath:Layout.json")
.build();
// Call the static factory method to create the Appender. This will be
// custom to the particular Appender we are implementing.
Appender appender =
MyAppender.createAppender(
APPENDER_NAME,
layout,
null
);
configuration.addAppender(appender);
// This and the next line are the crucial steps.
configuration
.getRootLogger()
.addAppender(configuration.getAppender(APPENDER_NAME), Level.DEBUG, null);
Configurator.reconfigure(configuration);
The XML equivalent of the above would be:
<MyAppender name="MyAppender">
<JsonTemplateLayout eventTemplateUri="classpath:Layout.json"/>
</MyAppender>
I could not find these particular steps in any of the log4j2 documents, and I pieced these together after reading Programmatic configuration
Changed the JsonTemplateLayout
to take the existing of configuration
instead of creating a new one as suggested by @PiotrP.Karwasz
Upvotes: 1
Reputation: 87
In Log4j2 structure
---"Config"---
Appenders
Appender(0)
Console
Appender(1)
File
LoggerConfigs
-- LoggerConfig(0)
-- LoggerConfig(1)
-- LoggerConfig(2)
----"LoggerConfig"----
- AppenderRefs
-- AppenderRef(0)
-- Name Console
-- Level : DEBUG
- Appenders
-- Appender(0)
-- Name Console
-- Level : DEBUG
- Level -- ALL
loggerConfig.getAppenders() --> Will return the Appenders in "Config". For me this is a BUG
loggerConfig.getAppenderRefs() --> is working well !!
Upvotes: 4
Reputation: 1911
As I noted above, I couldn't get https://logging.apache.org/log4j/2.x/manual/customconfig.html#AddingToCurrent to work, at least, not the way I expected it to (my appender would never get messages routed to it). I did finally stumble across a pattern that works for me - allowing me to add an appender at runtime, and have that appender actually get log messages routed to it.
Edit I removed a bunch of confusing code from here that wasn't doing anything....
LoggerContext lc = (LoggerContext) LogManager.getContext(false);
FileAppender fa = FileAppender.newBuilder().withName("mylogger").withAppend(false).withFileName(new File(outputDirectory, "ConsoleOutput.txt").toString())
.withLayout(PatternLayout.newBuilder().withPattern("%-5p %d [%t] %C{2} (%F:%L) - %m%n").build())
.setConfiguration(lc.getConfiguration()).build();
fa.start();
lc.getConfiguration().addAppender(fa);
lc.getRootLogger().addAppender(lc.getConfiguration().getAppender(fa.getName()));
lc.updateLoggers();
A key point for me, was that calling addAppender and passing your appender directly doesn't work, but asking for your appender back by name seems to. Which doesn't make sense... but since working, and I'm tired of wasting time on something that should be so simple....
Upvotes: 20
Reputation: 1407
Edit: for the newest versions of log4j2, see https://stackoverflow.com/a/33472893/1899566 instead.
I get the impression they don't want you doing this, but this works for me:
if (arg[0].equals("log") ) {
org.apache.logging.log4j.Logger logger
= org.apache.logging.log4j.LogManager.getLogger("loggerNameFromXMLConfig");
org.apache.logging.log4j.core.Logger coreLogger
= (org.apache.logging.log4j.core.Logger)logger;
org.apache.logging.log4j.core.LoggerContext context
= (org.apache.logging.log4j.core.LoggerContext)coreLogger.getContext();
org.apache.logging.log4j.core.config.BaseConfiguration configuration
= (org.apache.logging.log4j.core.config.BaseConfiguration)context.getConfiguration();
coreLogger.addAppender(configuration.getAppender("appenderNameFromXMLConfig"));
} else {
//...
}
Upvotes: 33
Reputation: 36844
There have been a number of requests to support better programmatic configuration for Log4j 2. Sorry it took so long. 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.
Upvotes: 43
Reputation: 73
I don't know if it's useful: An Appender can be added to a Logger by calling the addLoggerAppender method of the current Configuration. reference:http://logging.apache.org/log4j/2.x/manual/architecture.html
Upvotes: 3