50

I have a log4j2.xml config file in the class path. One of the appenders is a File appender, and I would like to set the target file name at run time in the Java application.

According to the docs I should be able to use a double "$" and a context prefix in the log4j2.xml file:

<appenders>
    <File name="MyFile" fileName="$${sys:logFilename}">
        <PatternLayout pattern="%-4r %d{${datestamp}} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
</appenders>

where the "sys" prefix indicates that the Configurator will lookup the property "logFilename" in the system properties. So in the application, I call (rather early on):

System.setProperty("logFilename", filename);

I have also turned on auto-reconfiguration for log4j2 in the xml file:

<configuration status="debug" monitorInterval="5">>

Unfortunately, this has no effect whatsoever, and the log file is never created. Some of the log4j2 status output is below:

2013-02-13 15:36:37,574 DEBUG Calling createAppender on class org.apache.logging.log4j.core.appender.FileAppender for element File with params(fileName="${sys:logFilename}", append="null", locking="null", name="MyFile", immediateFlush="null", suppressExceptions="null", bufferedIO="null", PatternLayout(%-4r %d{yyyy-MM-dd/HH:mm:ss.SSS/zzz} [%t] %-5level %logger{36} - %msg%n), null)

2013-02-13 15:36:37,576 DEBUG Starting FileManager ${sys:logFilename}

How can I have the value of "fileName" in the File Appender be set at run time? Alternatively, how can I simply add a new File Appender to the root logger at run time? In Log4j 2.0 most of the API to change the configuration is hidden.

Community
  • 1
  • 1
user84756
  • 1,195
  • 2
  • 9
  • 10
  • Do an SO search. It has already been answered: http://stackoverflow.com/questions/10699358/log4j-creating-modifying-appenders-at-runtime-log-file-recreated-and-not-appe – mightyrick Feb 13 '13 at 20:51
  • 14
    This is a Log4j 2.0 question, so the API is very different from Log4j 1.2 and many of the methods in previous answers are not exposed. – user84756 Feb 13 '13 at 20:58

4 Answers4

52

h/t rgoers The FileAppender doesn't support two dollar signs on the file name as the file is opened when the appender is started. What you are indicating with two dollar signs is that you want - potentially - a different file name for each event.

With a single $ (as in ${sys:logFilename}), the system will look for property "logFilename" in the system properties.

Thus, the log4j2.xml should have:

<appenders>
    <File name="MyFile" fileName="${sys:logFilename}">
        <PatternLayout pattern="%-4r %d{${datestamp}} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
</appenders>

The Java application should set the system property:

System.setProperty("logFilename", filename);

and reconfigure the logger:

org.apache.logging.log4j.core.LoggerContext ctx =
    (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
ctx.reconfigure();

This produces the desired behavior.

user84756
  • 1,195
  • 2
  • 9
  • 10
  • 1
    I'm not sure if this is because the API has changed, but this didn't work for me. What I had to do in order to get this to work was to do `${sys:logFilename}` – rm5248 Aug 22 '13 at 20:20
  • You're correct - this was a typo in the original answer. Corrected now. Thanks! – user84756 Aug 23 '13 at 20:25
  • 2
    Using version `2.0-beta9` and a single `$` this will create two files, one with the correct name but empty contents, and one with all the log output named literally `${sys:logFilename}.log`. – amoe Dec 11 '13 at 11:02
  • 9
    `LoggerContext.reconfigure` was removed in log4j2 version **2.5**. So this solution can't be used. – Alex Yursha Dec 21 '15 at 21:13
  • 3
    @AlexYursha I am currently using `org.apache.logging.log4j:log4j-core:2.6.1` and have used that method with no problem (didn't even got a deprecation warning). – Chris Aug 25 '16 at 21:36
  • 1
    You can use [this solution](https://stackoverflow.com/a/50459264/3284624) without relying on the implementation details of log4j2. The only catch is you have to define the system property before you log anything. – D.B. May 25 '18 at 04:11
  • This is EXACTLY what I needed. Thanks! – hfontanez Jun 26 '18 at 13:38
  • As of log4j 2.13.0 (and maybe earlier), you can reconfigure after setting the property with `Configurator.reconfigure()` – Dave Yarwood Feb 07 '20 at 03:39
14

As of log4j2 version 2.5 here is the simplest way to achieve this:

In your log4j2.xml:

<Appenders>
   <File name="MyFile" filename="${sys:logFilename}">
   ...

In you main MyApp.java file:

public class MyApp {

    Logger log;

    static {
          System.setProperty("logFilename", ...);
          log = LogManager.getLogger();
    }

    public static void main(String... args) {...}
}

CATCH: You should set logFilename system property before log4j2 is loaded. In this example before calling LogManager.getLogger.

Alex Yursha
  • 3,208
  • 3
  • 26
  • 25
  • 1
    This doesn't work, it just creates `${sys/logFilename}` file – Akshay Dec 27 '15 at 00:00
  • This worked for me in my command-line standalone app. You may experience problems because some other code you depend on initializes log4j2 earlier than you think. Could you please describe the environment where you try this? Is it a web app or a command-line tool? – Alex Yursha Dec 27 '15 at 02:34
  • 1
    +1 for the warning! This is the catch. I was trying to do this, but always got a second empty file `${sys/logFilename}`. – Chris Aug 25 '16 at 21:32
  • This should be the most voted answer as the top one is outdated – L Kemp Mar 07 '22 at 22:52
14

I know this topic is old, but the answers did not really suit me. Here is a function which allows you to reconfigure an existing Appender at runtime:

static void updateLogger(String file_name, String appender_name, String package_name){
LoggerContext context = (LoggerContext) LogManager.getContext(false);
    Configuration configuration = context.getConfiguration();
    Layout<? extends Serializable> old_layout = configuration.getAppender(appender_name).getLayout();

    //delete old appender/logger
    configuration.getAppender(appender_name).stop();
    configuration.removeLogger(package_name);

    //create new appender/logger
    LoggerConfig loggerConfig = new LoggerConfig(package_name, Level.INFO, false);
    FileAppender appender = FileAppender.createAppender(file_name, "false", "false", appender_name, "true", "true", "true",
            "8192", old_layout, null, "false", "", configuration);
    appender.start();
    loggerConfig.addAppender(appender, Level.INFO, null);
    configuration.addLogger(package_name, loggerConfig);

    context.updateLoggers();
}

You can specify a file name, the name of your appender and the package name which you want to log.

Example Logger:

<File name="fileWriter_api" fileName="${LOG_DIR}/api.log" append="false">
  <PatternLayout pattern="${PATTERN}"/>
</File>

Can be reconfigured calling like this:

updateLogger("log/api_new.log", "fileWriter_api", "my.package");
Christian Ammann
  • 888
  • 8
  • 19
1

What you can do is, when running the application, pass logFilename as an argument to the JVM:

java -DlogFilename=myAppName.log -jar /path/to/myApp.jar
Agi Hammerthief
  • 2,114
  • 1
  • 22
  • 38