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

时间:2017-07-31 18:31:22

标签: java tomcat logging log4j embedded-tomcat-8

我正在使用嵌入式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记录到任何文件):

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

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

我查看/尝试了以下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,作者提到I found some info about using a standalone tomcat with log4j,但独立的是不同的,我看到作者使用了类似的依赖关系,但不确定是否有解决方案。

  • How to enable embedded tomcat logging 首先我认为他可能是解决方案,但它只是处理嵌入式Tomcat的额外日志记录。我希望嵌入式Tomcat使用应用程序log4j,因此有一个log4j.properties文件,它定义了所有内容的记录方式。

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

1 个答案:

答案 0 :(得分:7)

我花了一段时间,但在获得8.5.4实现的来源之后,我意识到juli jar中添加了core日志记录实现。

版本&lt; = 8.5.2

所以我回去开始使用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开箱即用,没有什么可以做的。

版本&gt; 8.5.2

当使用较新版本的嵌入式Tomcat时,即8.5.4.或甚至最新的8.5.19时,LogFactory已包含在jar中。因此,在类路径中添加较旧的tomcat-embed-logging-log4j-8.5.2.jar时,现在有两个LogFactory实现可用。第一个提供core并加载DirectJDKLog(我将此引用为Core-LogFactory),第二个通过log4j提供(称为{ {1}})。因此,当从类路径加载Log4j-LogFactory时,会选择LogFactory(因为它位于同一个jar中,因此“更接近”(无需深入到类路径加载顺序) ))。通常,在类路径上使用相同的类(在同一个包中)是不好的做法。这只会导致混乱,而你主要永远不会知道哪个类实际使用(是的我知道有方法和规则,但长话短说,这不好)。因此,我决定不再使用Core-LogFactory,而是遵循tomcat-embed-logging-log4j-8.5.2.jar方法,该方法实际上是在较新版本ServiceLoaderhttps://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/juli/logging/LogFactory.java)中实现的。

Core-LogFactory

为此,我在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中)中添加了文件org.apache.juli.logging.Log,并添加了我自己的“META-INF/services实现的完全限定名称,即,Log,如下:

net.meisen.tomcat.logging.Log4jLog

Andetvoilà,这是最终结果:

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). 内容的链接,以及为什么我决定在我的项目中不同罐中的同一个包中使用相同的类: