15

Kind of a puzzle here.

I have an application in WAR. There are web.xml and application context.xml in there, and also there is log4j.properties. This WAR runs in tomcat.

There is a possibility to use some variables in log4j.properties, e.g. log4j.appender.file.File=${catalina.base}/logs/app.log

I want to define a variable in web.xml or context.xml and use it in log4j.properties. For example, somehow set version=1.1 and use log4j.appender.file.File=${catalina.base}/logs/app-${version}.log. It should not be an environment variable.

Can I do it without recompiling the app?

ADD shouldn't affect anything, but just in case...

Its web.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     version="2.5"
     xmlns="http://java.sun.com/xml/ns/javaee">

<!-- Spring -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:context.xml</param-value>
</context-param>

<!-- log4j -->
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
</context-param>
<context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>10000</param-value>
</context-param>
<context-param>
    <param-name>log4jExposeWebAppRoot</param-name>
    <param-value>false</param-value>
</context-param>
...
</web-app>
Andrey Regentov
  • 3,687
  • 4
  • 34
  • 40

3 Answers3

9

Make a subclass of org.springframework.web.util.Log4jConfigListenerthat expose version value as System variable (and of course you can pass the value via standard System/Java environment variable).

public class TestListener extends Log4jConfigListener{

    @Override
    public void contextInitialized(ServletContextEvent pEvent) {
        String value = pEvent.getServletContext().getInitParameter("version");
        String version = System.getProperty("version");
        if (version == null || version.trim().length()==0) System.setProperty("version", value);

        super.contextInitialized(pEvent);
    }
}

And fix your web.xml

 <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
              http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee">

    <!-- Spring -->
    ...
    <!-- log4j -->
    <listener>
        <listener-class>com.TestListener</listener-class>
    </listener>
    <context-param>
        <param-name>version</param-name>
        <param-value>1.1</param-value>
    </context-param>
    ...
FoxyBOA
  • 5,788
  • 8
  • 48
  • 82
  • So you say i cannot do it without recompiling the app. Did i get it right? – Andrey Regentov Aug 28 '15 at 03:33
  • Suppose you have several instances of the app in one container. Wouldn't it interfere through the environment/jvm variable (single per container)? – Andrey Regentov Aug 28 '15 at 03:35
  • @AndreyRegentov. You can do it w/o recompiling the app by passing the value via standard System/Java environment variable. – FoxyBOA Aug 28 '15 at 08:12
  • @AndreyRegentov. In case of several instances of the app. Changed above code. – FoxyBOA Aug 28 '15 at 08:14
  • So, whether we have both "it should not be environment variable" and "can i do it without recompile?" in a question - your answer is "NO", do i get it right? :) – Andrey Regentov Aug 28 '15 at 10:14
  • If you have both and common web.xml you can have 1 env. variable that will be set by any servlet. For this scenario you need to recompile. Option 2: you can define the value in environment variables directly (on via Java startup params). It allows you to do what you need w/o changes. – FoxyBOA Aug 28 '15 at 10:19
  • My fault, didn't make the following clear enough in the question: I have an already-compiled app without sources. Several instances of the app are running in a single container. Each of them have individual web.xml, but they all have the same log4j.properties. That's the given. The question is - Can i configure them to have individual logfile for each? And as i see - the answer is cannot. Thank you anyway! – Andrey Regentov Aug 28 '15 at 10:33
0

I think you can do this by declaring a context-param in web.xml and using that param name in the property file you want to put the version or whatever.

in web.xml

<context-param>
    <param-name>version</param-name>
    <param-value>1.1</param-value>
</context-param>

in property file

log4j.appender.file.File=${version}/logs/app.log

This should work with a server restart, no compilation needed.

You can also try setting a property from the xml and accessing ti from the property file.

in web.xml

<Properties>
    <Property name="version">1.1</Property>
</properties>

in property file

log4j.appender.file.File=${version}/logs/app.log
javabot
  • 173
  • 10
  • No it doesn't. At least in tomcat-7.0.52 it gives no error/warning messages, but creates `logs/app-.log` instead of expected `logs/app-1.1.log` when `logs/app-${version}.log` specified. – Andrey Regentov Jul 28 '15 at 04:49
  • did you try setting a property from xml and accessing it from property file? – javabot Jul 28 '15 at 05:31
  • Yes, I did just like you tell. By the way, there is log4j-1.2.17.jar in libs, so i suppose our log4j version may differ as well. – Andrey Regentov Jul 28 '15 at 05:40
  • Added another way, try that too. – javabot Jul 28 '15 at 05:51
  • Tried and didn't work also. Edited question, added web.xml if it can help. Do you really get the folder "1.1" and subfolder "logs" inside it? – Andrey Regentov Jul 28 '15 at 06:32
  • 1
    okay, did some more digging. Turns out the ${version} that was being resolved in property file was actually the one declared in pom file. nothing declared in web.xml seems to work. I'll try more and get back to you if i find an answer. If you are using maven you are obviously defining app version in pom, right? if so seems that can be accessed from log4j properties. So confused now... – javabot Jul 28 '15 at 07:00
0

javabot's answer about declaring a context-param to set the variable is correct, but in order to retrieve it in your properties file you will need something like https://code.google.com/p/eproperties/ ("EProperties extends the standard java.util.Properties object and syntax to include things like: variable substitution, nesting, inclusion and lists.").

You may want to look at this previous question: Reference other variables in Java properties file

Best of luck to you in finding the solution!

Community
  • 1
  • 1