55

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 {
    //...
}
Brian Johnson
  • 1,629
  • 5
  • 21
  • 26
  • 8
    This is barely a duplicate because it is meant to be a log4j2 question. The linked question refers to log4j1 (which does not even expose the same methods as log4j2) and slf4j. – Brian Johnson Mar 19 '13 at 12:00
  • 2
    I'm not sure those who closed this question know what it is actually about. – Colin Hebert Apr 28 '13 at 20:06
  • For now, as a workaround, I ditched log4j2 and migrated to Log4J ("Log4J1") which allows dynamic construction from XML configurations. – Brian Johnson May 30 '13 at 16:08
  • Oh I forgot I commented on this one. So basically the big idea behind log4j2 is that you can't (and shouldn't) set the appenders programmatically. So you can't do it by design. – Colin Hebert May 30 '13 at 16:42
  • 1
    Read on RoutingAppender at http://logging.apache.org/log4j/2.x/manual/appenders.html#RoutingAppender Probably Log4j2 wants to avoid a mixture of declarative and programmatic configurations, not sure why, RoutingAppender solution seems cleaner though. – Samarth Seksaria Jun 14 '13 at 13:32

5 Answers5

41

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.

Remko Popma
  • 35,130
  • 11
  • 92
  • 114
  • 9
    I'll note, I kept finding this answer, and it points to the docs, but the example in the docs now uses deprecated methods, and never actually worked for me. I had to add something like this, to get it to work: context.getRootLogger().addAppender(configuration.getAppender(appender.getName())); You also couldn't pass in the appender directly, as that didn't work either.... – user2163960 Apr 04 '18 at 23:00
  • I would be great if you could raise a log4j2 JIRA with a proposal to fix the documentation (giving example code for what the right way is to do this) so that others can benefit from your experience. – Remko Popma Apr 04 '18 at 23:40
  • 2
    @RemkoPopma It is log4j 2.12 version now. There's definitely a huge need for a cleaner API to stop and clear programmatically created appenders and loggers. – Gaurav Jul 19 '19 at 15:42
  • Please raise this on the mailing list and ideally provide a patch or pull request. – Remko Popma Jul 19 '19 at 16:10
  • 23
    I am considering ditching log4j. They overcomplicated it to the point of absurdity. You need 2 hours to write your own logging framework, but 2 weeks to study this crap. – jurez Feb 24 '20 at 10:07
  • @jurez I agree with you! I spent two days for research how add appender.... Earlier could just do `logger.addAppender(appender);` and all... – Amerousful May 07 '20 at 17:18
  • @Amerousful and how do you add an appender now? Help! – user3804769 Feb 24 '22 at 10:54
31

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 {
  //...
}
Community
  • 1
  • 1
Robert Fleming
  • 1,337
  • 17
  • 13
17

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....

user2163960
  • 1,871
  • 19
  • 22
  • 2
    Thanks a lot for this answer! The official documentation now does not even match the interface of log4j 2.14. This should be in the official documentation! – David Georg Reichelt Nov 19 '20 at 17:14
3

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 !!

2

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

Xiaoyuan
  • 63
  • 1
  • 9