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

Phi*_*ipp 6 java logging tomcat 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>
Run Code Online (Sandbox Code Playgroud)

实现工作完美(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).
...
Run Code Online (Sandbox Code Playgroud)

在这个片段中...,我的应用程序使用log4j和log4j的配置(写入文件和)来记录第一行和最后一行(之前和之后System.out).然而,中间部分(Tomcat的日志记录)由嵌入式Tomcat处理,我不知道如何让Tomcat使用可用的log4j(以及它的配置).

我尝试添加以下依赖项(8.5.4Maven Repository或Maven Central上没有版本),但没有任何成功.

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-logging-log4j</artifactId>
    <version>8.5.2</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

有谁知道如何使用log4j(版本1,我不使用log4j2)使嵌入式Tomcat登录?

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

  • https://tomcat.apache.org/tomcat-8.0-doc/logging.html 所以我查看了文档,其中提到log4j了日志框架.它提到了tomcat-juli-adapters.jar,我找不到嵌入式版本(它与"普通"Tomcat相同吗?).我将如何以编程方式执行此操作,即在我的嵌入式Tomcat实现中.

  • Tomcat使用log4j进行日志记录? 这不是我真正遇到的问题,它不是基于嵌入式Tomcat,版本很旧,我实际上并没有使用log4j System.out.

  • 嵌入式Tomcat登录logback/sl4j 这个问题实际上是处理的,logback作者提到I found some info about using a standalone tomcat with log4j,但是独立不同,我看到作者使用了类似的依赖,但不确定是否有解决方案.

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

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

Phi*_*ipp 8

我花了一段时间,但在得到实现的来源之后8.5.4,我意识到juli日志实现已添加到corejar中.

版本<= 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开箱即用,没有什么可以做的.

版本> 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(称为Log4j-LogFactory)提供.因此,当LogFactory从类路径加载时, Core-LogFactory拾取(因为它在同一个jar中因此"更接近"(不需要太深入类路径加载顺序)).通常,在类路径上使用相同的类(在同一个包中)是不好的做法.这只会导致混乱,你几乎不知道哪个类实际使用(是的,我知道有方法和规则,但长话短说,这不好).因此,我决定不使用其他方法,而是采用了最新版本实现tomcat-embed-logging-log4j-8.5.2.jarServiceLoader方法Core-LogFactory(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;
}
Run Code Online (Sandbox Code Playgroud)

要做到这一点,我添加了文件org.apache.juli.logging.LogMETA-INF/services的文件夹(我的JAR /源内),并加入我的"自己"完全合格的名称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);
    }
}
Run Code Online (Sandbox Code Playgroud)

而且等,这是最终的结果:

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).
Run Code Online (Sandbox Code Playgroud)

附录:

这里有一些链接帮助我弄清楚这些ServiceLoader东西以及为什么我很快就决定不在我项目的不同jar中的同一个包中使用相同的类: