Java Log4j,配置一个 Web App 使用相对路径

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/216781/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 11:30:47  来源:igfitidea点击:

Log4j, configuring a Web App to use a relative path

javaweb-applicationslog4j

提问by Iker Jimenez

I have a java webapp that has to be deployed on either Win or Linux machines. I now want to add log4j for logging and I'd like to use a relative path for the log file as I don't want to change the file path on every deployment. The container will most likely be Tomcat but not necessarily.

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

What's the best way of doing this?

这样做的最佳方法是什么?

采纳答案by Iker Jimenez

I've finally done it in this way.

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

Added a ServletContextListener that does the following:

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

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

Then in the log4j.properties file:

然后在 log4j.properties 文件中:

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

By doing it in this way Log4j will write into the right folder as long as you don't use it before the "rootPath" system property has been set. This means that you cannot use it from the ServletContextListener itself but you should be able to use it from anywhere else in the app.

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

It should work on every web container and OS as it's not dependent on a container specific system property and it's not affected by OS specific path issues. Tested with Tomcat and Orion web containers and on Windows and Linux and it works fine so far.

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

What do you think?

你怎么认为?

回答by Steve K

Tomcat sets a catalina.home system property. You can use this in your log4j properties file. Something like this:

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

log4j.rootCategory=DEBUG,errorfile

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

On Debian (including Ubuntu), ${catalina.home}will not work because that points at /usr/share/tomcat6 which has no link to /var/log/tomcat6. Here just use ${catalina.base}.

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

If your using another container, try to find a similar system property, or define your own. Setting the system property will vary by platform, and container. But for Tomcat on Linux/Unix I would create a setenv.sh in the CATALINA_HOME/bin directory. It would contain:

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

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

Then your log4j.properties would be:

那么你的 log4j.properties 将是:

log4j.rootCategory=DEBUG,errorfile

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

回答by Spencer Kormos

Doesn't log4j just use the application root directory if you don't specify a root directory in your FileAppender's path property? So you should just be able to use:

如果您没有在 FileAppender 的 path 属性中指定根目录,log4j 是否只使用应用程序根目录?所以你应该能够使用:

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

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

It's been awhile since I've done Java web development, but this seems to be the most intuitive, and also doesn't collide with other unfortunately named logs writing to the ${catalina.home}/logs directory.

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

回答by Dimitri De Franciscis

If you use Spring you can:

如果您使用 Spring,您可以:

1) create a log4j configuration file, e.g. "/WEB-INF/classes/log4j-myapp.properties" DO NOT name it "log4j.properties"

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

Example:

例子:

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

We'll define "myWebapp-instance-root" later on point (3)

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

2) Specify config location in web.xml:

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

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

3) Specify a uniquevariable name for your webapp's root, e.g. "myWebapp-instance-root"

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

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

4) Add a Log4jConfigListener:

4)添加一个Log4jConfigListener:

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

If you choose a different name, remember to change it in log4j-myapp.properties, too.

如果您选择不同的名称,请记住也在 log4j-myapp.properties 中更改它。

See my article (Italian only... but it should be understandable): http://www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring

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

UPDATE (2009/08/01)I've translated my article to English: http://www.megadix.it/node/136

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

回答by lizi

Just a comment on Iker'ssolution.

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

ServletContextis a good solution for your problem. But I don't think it is good for maintains. Most of the time log files are required to be saved for long time.

ServletContext是您问题的一个很好的解决方案。但我不认为这对维护有好处。大多数时候日志文件需要长时间保存。

Since ServletContextmakes the file under the deployed file, it will be removed when server is redeployed. My suggest is to go with rootPath's parent folder instead of child one.

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

回答by user553633

My suggestion is the log file should always be logged above the root context of the webApp, so in case we redeploy the webApp, we don't want to override the existing log files.

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

回答by alex.antaniuk

In case you're using Maven I have a great solution for you:

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

  1. Edit your pom.xml file to include following lines:

    <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>
    

    Here you define logDirectoryproperty specifically to OS family.

  2. Use already defined logDirectoryproperty in log4j.propertiesfile:

    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. That's it!
  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. 就是这样!

P.S.:I'm sure this can be achieved using Ant but unfortunately I don't have enough experience with it.

PS:我确信这可以使用 Ant 来实现,但不幸的是我没有足够的经验。

回答by Wolfgang Liebich

As a further comment on https://stackoverflow.com/a/218037/2279200- this may break, if the web app implicitely starts other ServletContextListener's, which may get called earlier and which already try to use log4j - in this case, the log4j configuration will be read and parsed already before the property determining the log root directory is set => the log files will appear somewhere below the current directory (the current directory when starting tomcat).

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

I could only think of following solution to this problem: - rename your log4j.properties (or logj4.xml) file to something which log4j will not automatically read. - In your context filter, after setting the property, call the DOM/PropertyConfigurator helper class to ensure that your log4j-.{xml,properties} is read - Reset the log4j configuration (IIRC there is a method to do that)

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

This is a bit brute force, but methinks it is the only way to make it watertight.

这有点蛮力,但我认为这是使其防水的唯一方法。

回答by starikoff

My solution is similar to Iker Jimenez's solution, but instead of using System.setProperty(...)I use org.apache.log4j.PropertyConfigurator.configure(Properties). For that I also need log4j to be unable to find its configuration on its own and I load it manually (both points described in Wolfgang Liebich's answer).

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

This works for Jetty and Tomcat, standalone or run from IDE, requires zero configuration, allows to put each app's logs in their own folder, no matter how many apps inside the container (which is the problemwith the System-based solution). This way one can also put the log4j config file anywhere inside the web app (e.g. in one project we had all config files inside WEB-INF/).

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

Details:

细节:

  1. I have my properties in the log4j-no-autoload.propertiesfile in the classpath (e.g. in my Maven project it's originally in src/main/resources, gets packaged into WEB-INF/classes),
  2. It has the file appender configured as e.g.:

    log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
    log4j.appender.MyAppFileAppender.file = ${webAppRoot}/WEB-INF/logs/my-app.log
    ...
    
  3. And I have a context listener like this (gets much shorter with Java 7's "try-with-resource" syntax):

    @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. 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(...)
        }
        ...
    }
    

回答by Dimitar II

You can specify relative path to the log file, using the work directory:

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

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

This is independent from the servlet container and does not require passing custom variable to the system environment.

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