使用log4j进行日志记录的嵌入式Tomcat

12

我正在使用嵌入式Tomcat 8.5.4,即

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>8.5.4</version>
</dependency>

实现工作完美(Tomcat表现得很好),唯一让我烦恼的是,嵌入式Tomcat记录在System.out上。在我的应用程序内部,我使用log4j进行记录,这导致以下记录混合(以及Tomcat未记录到任何文件):

实现工作良好(Tomcat运行良好),唯一让我困扰的是嵌入式Tomcat记录在System.out上。在我的应用程序内部,我使用log4j进行记录,所以会出现以下记录混杂(以及Tomcat未记录到任何文件):

...
2017-07-30 17:57:54 DEBUG EmbeddedTomcat:136 - Binding servlet 'sample' to path '/sample/*'.
Jul 30, 2017 5:57:54 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-15000"]
Jul 30, 2017 5:57:54 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
Jul 30, 2017 5:57:54 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Jul 30, 2017 5:57:54 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/8.5.4
Jul 30, 2017 5:57:54 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler [http-nio-15000]
2017-07-30 17:57:54 INFO  EmbeddedTomcat:80 - Successfully started Tomcat on port 15000 (base: null, url: http://localhost:15000).
...

在这段代码中,我的应用程序使用log4j记录了第一行和最后一行(在...之前和之后)。log4j的配置写入文件和System.out。然而,Tomcat日志(中间部分)由嵌入式Tomcat处理,我不知道如何让Tomcat使用可用的log4j及其配置。
我尝试加入以下依赖项(Maven Repository或Maven Central上没有8.5.4版本),但没有成功。
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-logging-log4j</artifactId>
    <version>8.5.2</version>
</dependency>

有人知道如何让嵌入式Tomcat使用log4j (版本1,我没有使用log4j2)记录日志吗?

我查看/尝试了以下StackOverflow答案:

  • https://tomcat.apache.org/tomcat-8.0-doc/logging.html所以我查看了文档,提到log4j作为日志框架。它提到tomcat-juli-adapters.jar,但我找不到嵌入式版本的(它与“正常”的Tomcat一样吗?)。我该怎么做程序化地,在我的嵌入式Tomcat实现中。

  • Tomcat Logging using log4j?这不是我遇到的问题,它不是基于嵌入式Tomcat,版本相当旧,并且我实际上正在使用log4j而不是System.out

  • Embedded Tomcat logging over logback / sl4j这个问题实际上涉及logback,作者提到我发现一些关于使用一个独立的带有log4j的tomcat的信息,但是独立版本不同,我看到作者正在使用类似的依赖项,但不确定是否曾经有解决方案。

  • How to enable embedded tomcat logging一开始我认为这可能是解决方案,但它只是处理嵌入式Tomcat的额外日志记录。我想要嵌入式Tomcat使用应用程序的log4j,因此只需一个log4j.properties文件即可定义如何记录所有内容。

  • Logging in Embedded Tomcat我不确定为什么这个答案甚至被标记为正确,但这只是解释了Tomcat如何编写文件,而不是嵌入式Tomcat工作的日志记录方式。


你有查看这里的Tomcat文档吗:https://tomcat.apache.org/tomcat-8.0-doc/logging.html? - JJF
@JJF 谢谢,这是我检查的第一个链接之一。实际上,它不涉及嵌入式Tomcat,只处理“普通”的Tomcat日志记录。我试图按照那里的说明操作,但是当涉及嵌入式Tomcat版本时,大多数提到的东西都不可用,或者没有办法在嵌入式版本中加载日志记录。 - Philipp
1个回答

14
我花了一些时间,但在获得8.5.4实现的源代码后,我意识到juli日志实现是添加在core jar中的。因此,我回到了8.5.2版本并使用了tomcat-embed-logging-log4j-8.5.2.jar和tomcat-embed-core-8.5.2.jar。需要注意的第一件重要的事情是,与大多数在线文档相反,重要的是不要添加tomcat-embed-logging-juli-8.5.2.jar。话虽如此,8.5.2版本可以直接使用log4j,无需进行其他操作。对于版本大于8.5.2的情况,请见原文。
当使用新版本嵌入式Tomcat时,例如 8.5.4. 或者最新的 8.5.19LogFactory 已经包含在jar包中。因此,在类路径上添加旧版的 tomcat-embed-logging-log4j-8.5.2.jar 后,现在有两个 LogFactory 实现可用。第一个是与 core 提供的并加载 DirectJDKLog(我称其为 Core-LogFactory),而第二个是通过 log4j 提供的(称为 Log4j-LogFactory)。因此,当从类路径加载 LogFactory 时,将选择 Core-LogFactory(因为它在相同的 jar 中,因此“更近”(无需深入类路径加载顺序))。通常,在类路径上具有相同类(在同一包中)是不好的做法。这只会导致混淆,您几乎永远不知道实际使用哪个类(是的,我知道有方法和规则,但长话短说,这不是好事)。因此,我决定不再使用 tomcat-embed-logging-log4j-8.5.2.jar,而是采用实际上在新版本的 Core-LogFactory 中实现的 ServiceLoader 方法(https://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/juli/logging/LogFactory.java)。
private LogFactory() {
    // Look via a ServiceLoader for a Log implementation that has a
    // constructor taking the String name.
    ServiceLoader<Log> logLoader = ServiceLoader.load(Log.class);
    Constructor<? extends Log> m=null;
    for (Log log: logLoader) {
        Class<? extends Log> c=log.getClass();
        try {
            m=c.getConstructor(String.class);
            break;
        }
        catch (NoSuchMethodException | SecurityException e) {
            throw new Error(e);
        }
    }
    discoveredLogConstructor=m;
}

为了实现这个目标,我在我的jar/sources文件夹下的META-INF/services文件夹中添加了org.apache.juli.logging.Log文件,并添加了我自己的Log实现的完全限定名称net.meisen.tomcat.logging.Log4jLog,如下所示:
package net.meisen.tomcat.logging;

import org.apache.juli.logging.Log;
import org.apache.log4j.Logger;

public class Log4jLog implements Log {
    private final Logger logger;

    // this constructor is important, otherwise the ServiceLoader cannot start
    public Log4jLog() {
        logger = Logger.getLogger(Log4jLog.class);
    }

    // this constructor is needed by the LogFactory implementation
    public Log4jLog(final String name) {
        logger = Logger.getLogger(name);
    }

    // now we have to implement the `Log` interface
    @Override
    public boolean isFatalEnabled() {
        return true;
    }

    // ... more isLevelEnabled()

    @Override
    public boolean isTraceEnabled() {
        return logger.isTraceEnabled();
    }

    // ... and also all the fatal(...) - trace(...) methods

    @Override
    public void fatal(final Object msg) {
        logger.fatal(msg);
    }

    @Override
    public void fatal(final Object msg, final Throwable throwable) {
        logger.fatal(msg, throwable);
    }
}

接下来,这就是最终结果:

2017-07-31 19:27:04 TRACE EmbeddedTomcat:48 - Initializing Tomcat on port 15000 (base: null)...
2017-07-31 19:27:33 INFO  Http11NioProtocol:69 - Initializing ProtocolHandler ["http-nio-15000"]
2017-07-31 19:27:33 INFO  NioSelectorPool:69 - Using a shared selector for servlet write/read
2017-07-31 19:27:33 INFO  StandardService:69 - Starting service [Tomcat]
2017-07-31 19:27:33 INFO  StandardEngine:69 - Starting Servlet Engine: Apache Tomcat/8.5.19
2017-07-31 19:27:34 WARN  SessionIdGeneratorBase:79 - Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [170] milliseconds.
2017-07-31 19:27:34 INFO  Http11NioProtocol:69 - Starting ProtocolHandler ["http-nio-15000"]
2017-07-31 19:27:34 INFO  EmbeddedTomcat:80 - Successfully started Tomcat on port 15000 (base: null, url: http://localhost:15000).

附录:

以下是帮助我理解ServiceLoader的一些链接,以及为什么我很快决定不在项目中的不同jar包中使用相同的类:


请考虑将org.apache.logging.log4j:log4j-appserver添加到您的依赖项中。这将引入一个基于log4j构建的TomcatLogger。 - Reuse3733

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接