So, I’ve spent the last day or so trying to track down how to configure log4j at runtime (log4j 1.2.8). Now, there are some things that are easy: setting the level of the root logger is as easy as: LogManager.getRootLogger().setLevel((Level) Level.DEBUG). However, if you want to do more complicated things at runtime based on other inputs than the log4j.{properties,xml} file, things begin to get a bit kludgy. For example, I wanted to set up a set of appenders with sane defaults. Then, if values were present in a configuration file, I wanted to update those appenders with different configuration values and change the root logger’s behavior.

The easiest way I could find was to manipulate the properties file, as shown below:

package test;

import org.apache.log4j.*;
import org.apache.log4j.net.SMTPAppender;
import org.apache.log4j.net.SyslogAppender;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Properties;
import java.io.*;

public class Test {
   private Log log = LogFactory.getLog(Test.class);
   Test() {
      log.debug("test1");
      switchAppenders();
      log.debug("test2");
   }
   public static void main(String args[]) {
      Test t = new Test();
   }
   private void switchAppenders() {
      Properties props = new Properties();
      try {
           InputStream configStream = getClass().getResourceAsStream("/log4j.properties");
           props.load(configStream);
           configStream.close();
      } catch(IOException e) {
          System.out.println("Error: Cannot laod configuration file ");
      }
      props.setProperty("log4j.rootLogger","DEBUG, file");
      props.setProperty("log4j.appender.file.File","out.log");
      LogManager.resetConfiguration();
      PropertyConfigurator.configure(props);
     }
}

This code is executed via this command, making sure that log4j.properties is present in the classpath:
java -classpath .:log4j-1.2.8.jar:commons-logging.jar test.Test

This is quite a kludge, but I couldn’t find anything better out there. It has the obvious setback that the changes you make to the log4j aren’t persisted, nor can they easily happen in more than one place, and any changes to appender names break a log of things, but at least it works.

15 thoughts on “Runtime log4j configuration

  1. Antony says:

    Hi,
    Thanks a lot for your post!! Spent nearly 3 hours on this topic and finally got a solution thru yours.

    Just a minor suggestion. Instead of reading the properties file into an InputStream and changing values and then reloading. Try using variable substitutions in log4j.properties and set the system properties and reload.

    your log4j.properties should look like:
    …..
    log4j.appender.mailappender.subject=${mail.subject}
    …..

    Your Backend code:

    System.setProperty(“mail.subject”, “Test Succeeded”);
    org.apache.log4j.LogManager.resetConfiguration();
    PropertyConfigurator.configure(“/log4j.properties”);

    By doing so, I feel, the file loading time and the properties memory may be saved.

  2. Meena says:

    Hello,

    Thanks a bunch for this solution.
    I need to turn off the appender dynamically to stop logging and enable it again through the code. I tried to set system properties for the appender as well but it doesnt work.

    I tried to use something like this:
    log4j.logger.Administration=file, syslogEvent, vlogserver
    log4j.appender.syslogEvent=${syslogevent.appender}
    but got this error:
    log4j:ERROR Could not find value for key log4j.appender.
    log4j:ERROR Could not instantiate appender named “”.

    Also tried something like:
    log4j.logger.Administration=file, ${syslogEvent}, vlogserver
    log4j.appender.syslogEvent=org.apache.log4j.net.SyslogAppender
    log4j.appender.syslogEvent.syslogHost=${syslogevent.hostip}
    log4j.appender.syslogEvent.threshold=${syslogevent.logLevel}
    and got the same error as before.

    Is there a way to turn off the appenders or stop logging at runtime?

    Thanks for your help.
    Meena.

  3. moore says:

    Hi Meena,

    Not sure what version of log4j you are using, but if you are using a relatively current one, these methods might be helpful:

    http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/AsyncAppender.html#removeAppender(org.apache.log4j.Appender)

    http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Category.html#removeAppender(org.apache.log4j.Appender)

    You could also subclass an appender and add a property that would control logging at runtime. You’d also have to override the append method:
    http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/net/SyslogAppender.html#append(org.apache.log4j.spi.LoggingEvent)

    Hope this helps.

  4. Denise says:

    This is great!!! I’ve been searching for several hours for a way to do this. I actually used what Antony said in the first comment and it works like a charm!

  5. Andrew says:

    Wow, that’s a scary concept. Have the runtime code change the code that comes from a build. That would never pass muster if found in a code review on an enterprise project; at least not in my enterprise.

    I’m ok with modifying something in memory, but to modify the filesystem; that’s not my preference.

  6. moore says:

    Hi Andrew,

    Yeah, it’s not great, is it? Note that we’re not actually modifying anything on the filesystem–we’re pulling a template from the filesystem (/log4j.properties) and then modifying it in memory. We never actually write that property file to disk: “the changes you make to the log4j aren’t persisted”.

    Still, it’s a kludge.

    I imagine that more current versions of log4j offer more flexibility, especially the addAppender and removeAppender methods on the Logger object. I just haven’t needed to revisit this issue lately.

  7. Bittu says:

    Thanks moore for sharing the peice of code. I have a similar requirement.
    I need to pass the name of my Java Class file name as an input parameter to log4j.xml (can’t use log4j.properties). As I am new to log4j.xml, I could not find the right solution. Currently, I am giving the absolute path for each log file in param tag value.
    eg :
    appender name=”FA” class=”org.apache.log4j.DailyRollingFileAppender”> param name=”DatePattern” value=”‘_’yyyyMMdd”/>
    param name=”File” value=”D:/logFiles/GPreprocessor.log”/> layout class=”com.dnb.genericpreprocessor.common.log.AppXMLLayout”/> /appender>

    I do not want to give “GPreprocessor.log” directly.Actually that file name is dynamic based on my project. For ex:- Suppose i run ABC.java program. So file name should be D:/logFiles/ABC.log.In the similar way when i run XYZ.java the log file name should be D:/logFiles/XYZ.log

    Please provide the valuable inputs or code snippets to resolve this issue. I have been searching a lot to find out the solution. Please share the changes that i need to make in my java program and log4j.xml to accomplish my requirement.

  8. moore says:

    Hi Bittu,

    I haven’t tried to do this, but you could try something like

    ((FileAppender)log.getAppender(“FA”)).setFile(this.getClass()+”.log”);

    Again, I don’t know if this works or not. Please let me know if you end up figuring this out.

  9. MAK says:

    Enumeration appenders = rootLogger.getAllAppenders();
    FileAppender fa = null;
    while (appenders.hasMoreElements()) {
    Appender currAppender = (Appender) appenders.nextElement();
    if (currAppender instanceof FileAppender) {
    fa = (FileAppender) currAppender;
    }
    }
    if (fa != null) {
    System.setProperty(“logName”, logFileName+”.log”);
    fa.setFile(logsDir + logFileName + “.log”);
    fa.activateOptions();
    } else {
    log.info(“No File Appender found”);
    }
    }

  10. shef says:

    Hi Moore..
    Thanks for the code… I am planning to use it …
    But I am getting log4j warning while loading the properties.

    og4j:WARN File option not set for appender [AuditFileAppender].
    log4j:WARN Are you using FileAppender instead of ConsoleAppender?
    log4j:WARN File option not set for appender [GeneralFileAppender].
    log4j:WARN Are you using FileAppender instead of ConsoleAppender?
    In fact I am setting file appender property before configuring the same. And it was working fine except the warning message..

    props.setProperty(“log4j.appender.AuditFileAppender.File”, ENV_LOCATION + props.getProperty(“AuditLogFile”));
    props.setProperty(“log4j.appender.GeneralFileAppender.File”,ENV_LOCATION + props.getProperty(“GeneralLogFile”));
    // LogManager.resetConfiguration();
    PropertyConfigurator.configure(props);
    Any thoughts !!!

  11. moore says:

    Sorry, Shef, nothing jumps to mind. Please do post again if you end up solving the issue.

  12. Ibys says:

    Antony’s solution worked for me … looks hackish to me though. bl00dy incredible it isn’t easier to do this.

  13. Mariano Ruiz says:

    I created a new java application module (jar) to do this in a web interface, and also view the log.
    To configure the jar, must be placed in the WEB-INF/lib folder, and add a mapping to the servlet in the WEB-INF/web.xml file.
    Look my site! http://www.log4jwebtracker.com

Comments are closed.


© Moore Consulting, 2003-2017 +