views:

777

answers:

2

Log4J appears to have an annoying restriction – at runtime, variable substitution does not appear to work.

In this example

File: Log4j.properties

file_pattern=%d{ISO8601} %-5p %m%n

log4j.rootLogger=DEBUG, FileAppender

log4j.appender.FileAppender=org.apache.log4j.FileAppender log4j.appender.FileAppender.layout=org.apache.log4j.PatternLayout log4j.appender.FileAppender.layout.ConversionPattern=${file_pattern} log4j.appender.FileAppender.File=log4jtest1.log

log4j.appender.FileAppender.Threshold=ERROR

The FileAppender configured in the log4j.properties file produces the correct output:

File: log4jtest1.log

ERROR Sample error message FATAL Sample fatal message

If I attempt to create a FileAppender at runtime

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

public class Main {
    static final Logger logger = Logger.getLogger(Main.class);

    public static void main(String[] args) throws Exception {

     FileAppender appender = new FileAppender();
     appender.setFile("log4test2.log");

     PatternLayout pl = new PatternLayout("${file_pattern}");

     appender.setLayout(pl);
     appender.setName("log4jtest2");
     appender.setThreshold(Level.ERROR);
     appender.activateOptions(); 
     logger.addAppender(appender);

     logger.trace("Sample trace message");
     logger.debug("Sample debug message");
     logger.info("Sample info message");
     logger.warn("Sample warn message");
     logger.error("Sample error message");
     logger.fatal("Sample fatal message");
    }
}

Te output is

File: log4jtest2.log

${file_pattern}${file_pattern}

Can anyone explain what is the problem and how can it be fixed?

Related question: Can an application access the ResourceBundle in order to read variables intended to be substituted?

+5  A: 

Variable substitution is a feature of PropertyConfigurator not of PatternLayout. If you look at your code, you never define what file_pattern should be. But why would you need variable substitution in code? Just do this:

 PatternLayout pl = new PatternLayout("%d{ISO8601} %-5p %m%n");

If you want to reference that string somewhere else, just make it a constant.

Edit: You will have to read the properties object, but PropertyConfigurator can take a properties object instead a file, so you could load that, do what you need to do and pass it on to the PropertiesConfigurator, so you only have one configuration path.

Yishai
Well, yes it would be an easy workaround but unfortunately it is not acceptable. It appears I will just have to read the log4j.properties file directly (e.g. using the Properties class) and apply "manually" the value of file_pattern variable at runtime
Adrian
Variable file_pattern is defined in Log4j.properties file
Adrian
You can't use ${file_pattern} to reference a value from a properties file outside the properties file. In code you need to use java variables. Check to see if Log4j exposes its properties and if it does you will not need to read the file yourself. If not then you are correct re: the Properties class.
Chris Nava
As far as I can tell from here http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PropertyConfigurator.html the Properties object it not exposed.
Adrian
+1  A: 

You can load it manually:

Properties props = new Properties();
InputStream fis = new FileInputStream(new File("/somewhere/log4j.properties")); 
props.load(fis);
fis.close();
PatternLayout layout = new PatternLayout(props.getProperty("file_pattern"));
alygin