Log4j,将 Web 应用配置为使用相对路径
Posted
技术标签:
【中文标题】Log4j,将 Web 应用配置为使用相对路径【英文标题】:Log4j, configuring a Web App to use a relative path 【发布时间】:2010-09-18 00:33:11 【问题描述】:我有一个必须部署在 Win 或 Linux 机器上的 java webapp。我现在想添加 log4j 进行日志记录,并且我想为日志文件使用相对路径,因为我不想在每次部署时更改文件路径。容器很可能是 Tomcat,但不一定。
最好的方法是什么?
【问题讨论】:
这真的只是问题的一半。拥有日志文件的动态路径很好,但是配置文件本身呢。如果只是动态的日志文件位置,那么您将在所有部署的位置拥有相同的日志级别,我认为这是不可取的。我想知道动态指定配置的最佳方法,以便我的开发环境可以在 DEBUG 上登录并在 INFO/WARN 上进行生产。你怎么看? 【参考方案1】: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
【讨论】:
老实说,我看不出这种方法比使用我在回答中解释的监听器有什么优势。我不在乎它是什么容器,无论我在哪里部署它都可以工作它,而在你的方法中,如果我改变环境,我必须改变一个值。 两种解决方案都使用系统属性,我们只是设置不同。它实际上只取决于灵活性与简单性。我们管理运行我们的应用程序的所有 Tomcat 服务器,所以我喜欢这种灵活性。如果您将战争分发给第 3 方使用,那么简单是有意义的【参考方案2】:我终于做到了。
添加了执行以下操作的 ServletContextListener:
public void contextInitialized(ServletContextEvent event)
ServletContext context = event.getServletContext();
System.setProperty("rootPath", context.getRealPath("/"));
然后在log4j.properties文件中:
log4j.appender.file.File=$rootPathWEB-INF/logs/MyLog.log
通过这种方式,只要您在设置“rootPath”系统属性之前不使用它,Log4j 就会写入正确的文件夹。这意味着您不能从 ServletContextListener 本身使用它,但您应该能够从应用程序的其他任何地方使用它。
它应该适用于每个 Web 容器和操作系统,因为它不依赖于容器特定的系统属性,并且不受操作系统特定路径问题的影响。 在 Tomcat 和 Orion Web 容器以及 Windows 和 Linux 上进行了测试,目前运行良好。
你怎么看?
【讨论】:
这是个好主意,但我认为 catalina.home 使用起来可能更安全,因为它总是在任何 log4j 代码初始化之前设置/可用。 如果我只使用 Tomcat,那将是真的,但我的要求是它必须在任何配置为 0 的容器上工作。我的方法实现了这一点,迄今为止没有人为此提出更好的方法。 此解决方案可能适用于使用 Servlet 的 Web 应用程序,但 Steve K 的解决方案 (***.com/questions/216781/…) 适用于任何使用 Log4j 的应用程序。 Spencer K 的解决方案同样基于相对路径,适用于任何使用 Log4j 的应用程序,前提是您将基本目录设置为可预测的路径。 此解决方案仅在您将单个 WebApp 配置为使用它时才有效,因为系统属性对于 tomcat 是全局的,第二个应用程序启动将覆盖第一个 Web 应用程序设置的值。您可以为每个 webapp 指定一个唯一的属性名称,但如果您要这样做,那么您也可以使用 $catalina.home 并在 log4j.properties 文件中添加路径的唯一部分,因为它不容易出错.【参考方案3】:如果你使用 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) 为您的 webapp 的根指定一个唯一变量名,例如"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
【讨论】:
【参考方案4】:只是对Iker's 解决方案的评论。
ServletContext
是解决您问题的好方法。但我认为这对维护没有好处。大多数时间日志文件都需要保存很长时间。
由于ServletContext
将文件放在已部署文件下,因此在重新部署服务器时将删除该文件。我的建议是使用 rootPath 的父文件夹而不是子文件夹。
【讨论】:
【参考方案5】:如果您没有在 FileAppender 的路径属性中指定根目录,log4j 是否只使用应用程序根目录?所以你应该可以使用:
log4j.appender.file.File=logs/MyLog.log
自从我进行 Java Web 开发以来已经有一段时间了,但这似乎是最直观的,并且也不会与写入 $catalina.home/logs 目录的其他不幸命名的日志发生冲突。
【讨论】:
据我所见,它可以使用用户的主目录,如果您不提供绝对路径,则可以使用容器的主目录。不可靠。 @Iker:为什么不能在容器或应用程序配置中明确设置应用程序根目录?一旦在开发和生产中完成了该操作,就可以可靠地使用相对路径。假设根目录设置正确,相对路径是最便携(可重新定位)的解决方案。 我知道这是一篇旧帖子,但我想为其他人做个笔记。对于tomcat,如果没有明确设置应用程序根目录,我相信它默认为您启动tomcat的任何目录。【参考方案6】:作为对https://***.com/a/218037/2279200 的进一步评论 - 这可能会中断,如果 Web 应用程序隐式启动其他 ServletContextListener,它可能会更早被调用并且已经尝试使用 log4j - 在这种情况下,将读取并解析 log4j 配置已经在确定日志根目录的属性设置之前 => 日志文件将出现在当前目录下方的某个位置(启动 tomcat 时的当前目录)。
我只能想到以下解决此问题的方法: - 将您的 log4j.properties(或 logj4.xml)文件重命名为 log4j 不会自动读取的内容。 - 在您的上下文过滤器中,设置属性后,调用 DOM/PropertyConfigurator 助手类以确保读取您的 log4j-.xml,properties - 重置 log4j 配置(IIRC 有办法做到这一点)
这有点蛮力,但我认为这是使其防水的唯一方法。
【讨论】:
【参考方案7】:如果您使用 Maven,我会为您提供一个很好的解决方案:
编辑您的 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
属性。
使用log4j.properties
文件中已经定义的logDirectory
属性:
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=%dISO8601 [%x] %-5p [%t] [%c1] %m%n
就是这样!
P.S.:我确信这可以使用 Ant 实现,但不幸的是我没有足够的经验。
【讨论】:
【参考方案8】:我的建议是日志文件应该始终记录在 webApp 的根上下文之上,所以如果我们重新部署 webApp,我们不想覆盖现有的日志文件。
【讨论】:
【参考方案9】:我的解决方案类似于 Iker Jimenez 的 solution,但我没有使用 System.setProperty(...)
,而是使用 org.apache.log4j.PropertyConfigurator.configure(Properties)
。为此,我还需要 log4j 无法自行找到它的配置,我手动加载它(Wolfgang Liebich 的answer 中描述了这两点)。
这适用于 Jetty 和 Tomcat,独立或从 IDE 运行,需要零配置,允许将每个应用程序的日志放在自己的文件夹中,无论容器内有多少应用程序(the problem 和 System
基于解决方案)。这样一来,您还可以将 log4j 配置文件放在 Web 应用程序中的任何位置(例如,在一个项目中,我们将所有配置文件放在 WEB-INF/
中)。
详情:
-
我的属性在类路径的
log4j-no-autoload.properties
文件中(例如,在我的 Maven 项目中,它最初位于 src/main/resources
,被打包到 WEB-INF/classes
),
它的文件附加器配置为例如:
log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
log4j.appender.MyAppFileAppender.file = $webAppRoot/WEB-INF/logs/my-app.log
...
我有一个像这样的上下文监听器(使用 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(...)
...
【讨论】:
【参考方案10】:您可以使用工作目录指定日志文件的相对路径:
appender.file.fileName = $sys:user.dir/log/application.log
这独立于servlet容器,不需要将自定义变量传递给系统环境。
【讨论】:
以上是关于Log4j,将 Web 应用配置为使用相对路径的主要内容,如果未能解决你的问题,请参考以下文章