49

I want to load Log4j2 XML configuration file programmatically from my application.

Tried this:

ConfigurationSource source = new ConfigurationSource();
source.setLocation(logConfigurationFile);
Configurator.initialize(null, source);

and this:

ConfigurationSource source = new ConfigurationSource();
source.setLocation(logConfigurationFile);
ConfigurationFactory factory = (ConfigurationFactory) XMLConfigurationFactory.getInstance().getConfiguration(source);
ConfigurationFactory.setConfigurationFactory(factory);

But nothing works yet.

HashimR
  • 3,803
  • 8
  • 32
  • 49
  • 1
    with all the backward incompatible changes introduced to the configuration reloading in log4j2 it would make sense to specify which version of the log4j2 api and core you are using ... – Yordan Georgiev Dec 21 '17 at 09:43

9 Answers9

45

For the newest version of log4j, here is what should work for loading an external log4j2.xml:

String log4jConfigFile = System.getProperty("user.dir") + File.separator + "log4j2.xml";
ConfigurationSource source = new ConfigurationSource(new FileInputStream(log4jConfigFile));
Configurator.initialize(null, source);
Tripp Kinetics
  • 5,178
  • 2
  • 23
  • 37
switch_java3
  • 479
  • 1
  • 4
  • 5
25

If you have a single main entry point, this code snippet might save you some trouble. The set property call must fire before any loggers are created. This approach works with files on the classpath.

public class TestProcess {
    static {
        System.setProperty("log4j.configurationFile", "log4j-alternate.xml");
    }

    private static final Logger log = LoggerFactory.getLogger(TestProcess.class);

}
dcompiled
  • 4,762
  • 6
  • 33
  • 36
  • This worked excellent, I was trying to set the property on Constructor, but it didn't work. Static block solved. – another Sep 22 '17 at 12:30
  • I had setProperty() call but not in "static" block. It worked once I kept the call in the static block. Thanks! – mvsagar Jul 04 '19 at 14:31
  • Won't changing the system property affect other logger's? – mtk Sep 11 '19 at 13:31
18

Found the answer myself. Someone might find it useful.

ConfigurationSource source = new ConfigurationSource();
source.setLocation(logConfigurationFile);
source.setFile(new File(logConfigurationFile));
source.setInputStream(new FileInputStream(logConfigurationFile));
Configurator.initialize(null, source);
HashimR
  • 3,803
  • 8
  • 32
  • 49
  • I'm trying to do the same thing but am failing even with the code above. Did you have the log4j2 web dependency in your project? My log4j2.xml is outside the classpath and I'm trying to load it up in a Spring bean that has a @PostConstruct method. – ColinMc Jun 13 '14 at 11:16
  • My log xml configuration file was outside the classpath. – HashimR Jun 13 '14 at 11:25
  • Was your application a web app? I should mention I am using slf4j as well to abstract log4j2. – ColinMc Jun 13 '14 at 11:33
  • Yes it was a web app. And I also used `slf4j` for routing `jcl` logs to `log4j2`. – HashimR Jun 13 '14 at 11:38
  • Is the `@PostConstruct` method being called when the server starts? Can you use a simple listener? Register it in `web.xml` and write this code in the overridden method `contextInitialized` of a class that implements `ServletContextListener`. – HashimR Jun 13 '14 at 11:41
  • Yes the @PostConstruct method is being called on server startup. I put the code you mentioned in your answer in the method. No exception was thrown and no log files were created in the directory I specified in my configuration. Here is my log4j2 config: http://pastebin.com/wSWWk2kb Maybe something is wrong with my configuration. – ColinMc Jun 13 '14 at 11:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/55567/discussion-between-hashimr-and-colinmc). – HashimR Jun 13 '14 at 12:11
  • I was able to get this working with the config outside the web app by using the log4jConfiguration context param in the web.xml as in this link: http://logging.apache.org/log4j/2.x/manual/webapp.html#Servlet-3.0 – ColinMc Jun 13 '14 at 13:38
  • 1
    okay cool. this answer is based on Servlet 2.5 ... anyhow great that its working! – HashimR Jun 16 '14 at 04:25
  • why pass null to the Configurator.initialize() ? – Gaurav Aug 24 '19 at 05:47
10

Below worked for me, Log4j2 with SLF4J wrapper:

Solution 1:

public class MyClass {

    static {
        try {
            InputStream inputStream = new FileInputStream("C:/path/to/log4j2.xml");
            ConfigurationSource source = new ConfigurationSource(inputStream);
            Configurator.initialize(null, source);
        } catch (Exception ex) {
            // Handle here
        }
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class); // LogManager if not using SLF4J

    public void doSomething() {
        LOGGER.info(...)
    }

}

Solution 2:

static {
    File log4j2File = new File("C:/path/to/log4j2.xml");
    System.setProperty("log4j2.configurationFile", log4j2File.toURI().toString());
}

Need toURI() to follow File URI Scheme format, else it throws MalformedURLException.

Sources:

k_rollo
  • 5,304
  • 16
  • 63
  • 95
4

If you are using a Servlet 3.0 Web Application you can use the Log4jServletContextListener and do the following:

Write a custom LogContextListener which extends from Log4jServletContextListener, set it up in your web.xml and disable auto initialization:

<listener>
    <listener-class>com.example.LogContextListener</listener-class>
</listener>
<context-param>
    <param-name>isLog4jAutoInitializationDisabled</param-name>
    <param-value>true</param-value>
</context-param>

In your custom LogContextListener overwrite contextInitialized and set the config location

public void contextInitialized(ServletContextEvent event) { 
    /* Some logic to calculate where the config file is saved. For 
     * example you can read an environment variable.
     */
    String pathToConfigFile = ... + "/log4j2.xml";
    Configurator.initialize(null, pathToConfigFile);
    super.contextInitialized(event);
}

The advantage over configuring the location directly in the web.xml is that you can compute the path based on some additional information and access the log4j2.xml even if its outside of your classpath.

lanoxx
  • 12,249
  • 13
  • 87
  • 142
  • I used similar solution with log4j 1.2.x but unable to get working 2.14.x. The reason is, that my "LogContextListener" is created after log4j2 is already initialized with `WARN StatusLogger No Log4j 2 configuration file found. Using default configuration...` I guess it used to be the same for log4j v1 but with `PropertyConfigurator.configure(logConfigProperties);` it has reconfigured current log4j but this doesn't happen with `Configurator.initialize(null, logConfigSource);` My listener is the first one in web.xml but there are others in TLD files that are fired before. – Vojtěch Zavřel Dec 10 '21 at 21:05
3

Considering - https://logging.apache.org/log4j/2.x/log4j-core/apidocs/org/apache/logging/log4j/core/config/Configurator.html

Configurator.initialize(null, "classpath:conf/logger.xml");
or
Configurator.initialize(null, "/full_path/conf/logger.xml");

Be aware and does not use both at the same time.

Cristian Florescu
  • 1,660
  • 20
  • 24
3

If you config with .properties file:

String propertiesFile = "./test/Configuration/log4j2.properties";  
ConfigurationSource source = new ConfigurationSource(new FileInputStream(propertiesFile), new File(propertiesFile));
Configurator.initialize(null, source);
tiboo
  • 8,213
  • 6
  • 33
  • 43
1
final URL log4j = Resources.getResource("log4j2-test.xml");
LoggerContext.getContext(false)
  .start(new XmlConfiguration(new ConfigurationSource(
    Resources.asByteSource(log4j).openStream(),
    log4j)));
TJR
  • 3,617
  • 8
  • 38
  • 41
  • Just an FYI for this one - be careful using "log4j2-test.xml" for testing things. If this is in any of the folders log4j2 checks it will use it over "log4j2.xml" and make you think your overriding of this worked, when it actually didn't. I haven't tried your code, but I had this problem with a previous code snippet. I was using "log4j2-test.xml" thinking everything was overriding at runtime, only to find that it was overriding because log4j2 looks for "log4j2-test.xml" first, and then "log4j2.xml" https://logging.apache.org/log4j/2.0/manual/configuration.html#AutomaticConfiguration – adprocas Aug 24 '17 at 13:34
  • @TJR will it also work wit new CompositeConfiguration() instead of new XmlConfiguration()? – Gaurav Aug 24 '19 at 05:32
0

I tried all the other solutions nothing worked for me.

tried Log4jServletContextListenerImpl extending Log4jServletContextListener @lonxx said able to write logs but not able to read environmental bean from spring context.

As log4j context is initializing before spring context initialization.

So Placed the below code in init(ServletConfig config) of InitServlet extends HttpServlet.

LoggerContext context = (LoggerContext) 
LogManager.getContext(false);
File file = new File("relativepath/log4j2/log4j2.xml");
context.setConfigLocation(file.toURI());

Now it's working.

using spring 5.3.22 & log4j2 2.17.2 and initservlet is configured in web.xml version ="2.5"

Eric Aya
  • 69,473
  • 35
  • 181
  • 253