Log4j,将Web应用程序配置为使用相对路径


80

我有一个必须在Win或Linux机器上部署的Java Webapp。现在,我想添加log4j进行日志记录,并且我想为日志文件使用相对路径,因为我不想在每次部署中都更改文件路径。容器很可能是Tomcat,但不一定。

最好的方法是什么?


3
这实际上只是问题的一半。具有日志文件的动态路径很棒,但是配置文件本身呢。如果只是动态的日志文件位置,那么在部署它的所有位置上您都将拥有相同的日志级别,并且我认为这不是理想的。我想知道动态指定配置的最佳方法,以便我的开发环境可以登录DEBUG并在INFO / WARN进行生产。你怎么看?
卢卡斯

Answers:


100

Tomcat设置catalina.home系统属性。您可以在log4j属性文件中使用它。像这样:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${catalina.home}/logs/LogFilename.log

在Debian(包括Ubuntu)上,${catalina.home}将无法使用,因为它指向/ usr / share / tomcat6,该目录没有指向/ var / log / tomcat6的链接。在这里使用${catalina.base}

如果您使用其他容器,请尝试查找类似的系统属性,或定义自己的系统属性。设置系统属性将因平台和容器而异。但是对于Linux / Unix上的Tomcat,我将在CATALINA_HOME / bin目录中创建一个setenv.sh。它包含:

export JAVA_OPTS="-Dcustom.logging.root=/var/log/webapps"

然后,您的log4j.properties将是:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${custom.logging.root}/LogFilename.log

2
老实说,我看不出这种方法比使用我在回答中解释的监听器有什么优势。我不在乎它是什么容器,无论我在哪里部署它都可以工作,而在你的方法中我必须如果我更改环境,请更改值。
伊克·希门尼斯

4
两种解决方案都使用系统属性,我们只是对它们进行了不同的设置。它实际上仅取决于灵活性与简单性。我们管理应用程序在其上运行的所有Tomcat服务器,因此我喜欢灵活性。如果您派发第三者使用的战争,那么简单就很有意义
Steve K

54

我终于以这种方式做到了。

添加了执行以下操作的ServletContextListener:

public void contextInitialized(ServletContextEvent event) {
    ServletContext context = event.getServletContext();
    System.setProperty("rootPath", context.getRealPath("/"));
}

然后在log4j.properties文件中:

log4j.appender.file.File=${rootPath}WEB-INF/logs/MyLog.log

通过这种方式,只要在设置“ rootPath”系统属性之前不使用Log4j,Log4j就会将其写入正确的文件夹。这意味着您不能从ServletContextListener本身使用它,但应该可以从应用程序中的其他任何地方使用它。

它应在每个Web容器和OS上工作,因为它不依赖于特定于容器的系统属性,并且不受特定于操作系统的路径问题的影响。在Tomcat和Orion Web容器以及Windows和Linux上进行了测试,到目前为止,它可以正常工作。

你怎么看?


4
这是个好主意,但我认为catalina.home可能会更安全使用,因为在任何log4j代码初始化之前它将始终被设置/可用。
马特b

6
如果我仅使用Tomcat,那将是正确的,但是我的要求是,它必须在任何配置为0的容器上都可以工作。我的方法满足了这一要求,到目前为止,没有人提出过更好的方法。
Iker Jimenez

2
该解决方案可能适用于使用Servlet的Web应用程序,但是Steve K的解决方案(stackoverflow.com/questions/216781/…)适用于使用Log4j的任何应用程序。
德里克·马哈

2
Spencer K的解决方案也基于相对路径,适用于所有使用Log4j的应用程序,前提是您将基本目录设置为可预测的路径。
德里克·马哈

12
仅当您配置了一个WebApp来使用该解决方案时,此解决方案才有效,因为系统属性是全局的,因此要启动第二个应用程序会覆盖第一个WebApp设置的值。您可以为每个Web应用程序指定一个唯一的属性名称,但是如果要这样做,则最好使用$ {catalina.home}并在log4j.properties文件中添加路径的唯一部分,因为它不容易出错。
3urdoch

14

如果您使用Spring,则可以:

1)创建一个log4j配置文件,例如“ /WEB-INF/classes/log4j-myapp.properties”,请勿将其命名为“ log4j.properties”

例:

log4j.rootLogger=ERROR, stdout, rollingFile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n

log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=${myWebapp-instance-root}/WEB-INF/logs/application.log
log4j.appender.rollingFile.MaxFileSize=512KB
log4j.appender.rollingFile.MaxBackupIndex=10
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.rollingFile.Encoding=UTF-8

我们稍后将在第(3)点定义“ myWebapp-instance-root”

2)在web.xml中指定配置位置:

<context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>/WEB-INF/classes/log4j-myapp.properties</param-value>
</context-param>

3)为您的Web应用程序的根目录指定一个唯一的变量名称,例如“ myWebapp-instance-root”

<context-param>
  <param-name>webAppRootKey</param-name>
  <param-value>myWebapp-instance-root</param-value>
</context-param>

4)添加一个Log4jConfigListener:

<listener>
  <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

如果选择其他名称,请记住也要在log4j-myapp.properties中进行更改。

请参阅我的文章(仅限意大利语...,但应该可以理解):http : //www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring

更新(2009/08/01) 我已经将文章翻译成英文:http : //www.megadix.it/node/136


6

只是对Iker解决方案的评论。

ServletContext是解决您问题的好方法。但我认为这对维护不利。大多数时间的日志文件都需要长时间保存。

由于ServletContext该文件位于已部署的文件下,因此在重新部署服务器时将其删除。我的建议是使用rootPath的父文件夹而不是子文件夹。


5

如果未在FileAppender的path属性中指定根目录,log4j会不会仅使用应用程序根目录?因此,您应该只能使用:

log4j.appender.file.File = logs / MyLog.log

自从我完成Java Web开发以来已经有一段时间了,但这似乎是最直观的,并且也不会与写入$ {catalina.home} / logs目录的其他不幸的日志冲突。


4
从我所看到的来看,它可以使用用户的主目录,或者如果您不提供绝对路径,则可以使用容器的主目录。
Iker Jimenez

1
@Iker:为什么不能仅在容器或应用程序配置中显式设置应用程序根目录?在开发和生产中完成此操作后,就可以可靠地使用相对路径。假设根目录设置正确,相对路径是最可移植(可重定位)的解决方案。
德里克·马哈

1
我知道这是一篇旧文章,但我想为其他人做个注释。对于tomcat,如果未明确设置应用程序根目录,我相信它默认为您从中启动tomcat的目录。
Geren White

2

作为对https://stackoverflow.com/a/218037/2279200的进一步评论-如果Web应用隐式启动其他ServletContextListener(可能会更早调用并且已经尝试使用log4j),则这可能会中断,在这种情况下,在确定设置了日志根目录的属性之前,将已读取并解析log4j配置=>日志文件将出现在当前目录(启动tomcat时为当前目录)下方的某个位置。

我只能想到以下解决方案:-将您的log4j.properties(或logj4.xml)文件重命名为log4j不会自动读取的文件。-在您的上下文过滤器中,设置属性后,请调用DOM / PropertyConfigurator帮助器类,以确保已读取log4j-。{xml,properties}。-重置log4j配置(IIRC有一种方法可以做到这一点)

这有点蛮力,但据信这是使其防水的唯一方法。


1

如果您使用的是Maven,那么我为您提供了一个很好的解决方案:

  1. 编辑您的pom.xml文件以包括以下几行:

    <profiles>
        <profile>
            <id>linux</id>
            <activation>
                <os>
                    <family>unix</family>
                </os>
            </activation>
            <properties>
                <logDirectory>/var/log/tomcat6</logDirectory>
            </properties>
        </profile>
        <profile>
            <id>windows</id>
            <activation>
                <os>
                    <family>windows</family>
                </os>
            </activation>
            <properties>
                <logDirectory>${catalina.home}/logs</logDirectory>
            </properties>
        </profile>
    </profiles>
    

    在这里,您可以logDirectory为OS系列专门定义属性。

  2. 使用文件中已定义的logDirectory属性log4j.properties

    log4j.appender.FILE=org.apache.log4j.RollingFileAppender
    log4j.appender.FILE.File=${logDirectory}/mylog.log
    log4j.appender.FILE.MaxFileSize=30MB
    log4j.appender.FILE.MaxBackupIndex=10
    log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.FILE.layout.ConversionPattern=%d{ISO8601} [%x] %-5p [%t] [%c{1}] %m%n
    
  3. 而已!

PS:我敢肯定,使用Ant可以实现这一点,但不幸的是我没有足够的经验。


1

我的建议是,日志文件应始终记录在WebApp的根上下文之上,因此,如果我们重新部署WebApp,我们不想覆盖现有的日志文件。


1

我的解决方案类似于Iker Jimenez的解决方案,但是System.setProperty(...)我没有使用org.apache.log4j.PropertyConfigurator.configure(Properties)。为此,我还需要log4j自身无法找到其配置,并手动加载它(这两点都在Wolfgang Liebich的答案中进行了描述)。

这适用于码头和Tomcat,独立的或从IDE中运行,需要零配置,允许将每个应用程序的日志在自己的文件夹,无论在容器内许多应用程序(这是怎样的问题System基础的解决方案)。这样一来,您也可以将log4j配置文件放在Web应用程序内的任何位置(例如,在一个项目中,我们所有的配置文件都在内部WEB-INF/)。

细节:

  1. log4j-no-autoload.properties在类路径的文件中拥有属性(例如,在我的Maven项目中,该属性最初位于中src/main/resources,并打包到中WEB-INF/classes),
  2. 它的文件附加器配置为例如:

    log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
    log4j.appender.MyAppFileAppender.file = ${webAppRoot}/WEB-INF/logs/my-app.log
    ...
    
  3. 我有一个这样的上下文侦听器(使用Java 7的“ try-with-resource”语法会变得更短):

    @WebListener
    public class ContextListener implements ServletContextListener {
        @Override
        public void contextInitialized(final ServletContextEvent event) {
            Properties props = new Properties();
            InputStream strm =
                ContextListener.class.getClassLoader()
                    .getResourceAsStream("log4j-no-autoload.properties");
            try {
                props.load(strm);
            } catch (IOException propsLoadIOE) {
                throw new Error("can't load logging config file", propsLoadIOE);
            } finally {
                try {
                    strm.close();
                } catch (IOException configCloseIOE) {
                    throw new Error("error closing logging config file", configCloseIOE);
                }
            }
            props.put("webAppRoot", event.getServletContext().getRealPath("/"));
            PropertyConfigurator.configure(props);
            // from now on, I can use LoggerFactory.getLogger(...)
        }
        ...
    }
    

1

您可以使用工作目录指定日志文件的相对路径:

appender.file.fileName = ${sys:user.dir}/log/application.log

这独立于Servlet容器,不需要将自定义变量传递到系统环境。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.