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

问题描述 投票:0回答:1

我正在使用嵌入式 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>

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

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

  • https://tomcat.apache.org/tomcat-8.0-doc/logging.html 所以我查看了文档,其中提到

    log4j
    作为日志记录框架。它提到了
    tomcat-juli-adapters.jar
    ,我在嵌入式版本中找不到它(它与“普通”Tomcat 相同吗?)。我将如何以编程方式(即在我的嵌入式 Tomcat 实现中)执行此操作。

  • Tomcat 使用 log4j 进行日志记录? 这并不是我真正遇到的问题,它不是基于嵌入式 Tomcat,版本相当旧,而且我实际上使用的是 log4j 而不是

    System.out

  • 通过 logback / sl4j 进行嵌入式 Tomcat 日志记录 这个问题实际上涉及

    logback
    ,作者提到了
    I found some info about using a standalone tomcat with log4j
    ,但独立版本不同,我看到作者正在使用类似的依赖项,但不确定是否有解决方案。

  • 如何启用嵌入式tomcat日志记录 首先,我认为他可能是解决方案,但它只是处理嵌入式 Tomcat 的附加日志记录。我希望嵌入式 Tomcat 使用应用程序

    log4j
    ,因此有一个
    log4j.properties
    文件,它定义了如何记录所有内容。

  • 登录嵌入式Tomcat 我不确定为什么这个答案被标记为正确,但这只是解释 Tomcat 如何编写

    catalina.out
    文件,而不是嵌入式 Tomcat 的日志记录如何工作。

java tomcat logging log4j embedded-tomcat-8
1个回答
15
投票

我花了一段时间,但在获得

8.5.4
实现的源代码后,我意识到
juli
日志记录实现已添加到
core
jar 中。

版本<= 8.5.2

使用 Tomcat 8.5.2 时,请使用以下 jar:

1. tomcat-embed-logging-log4j-8.5.2.jar

2. tomcat-embed-core-8.5.2.jar

不要包含

tomcat-embed-logging-juli-8.5.2.jar
,尽管一些在线文档可能会建议。

通过此配置,Tomcat 8.5.2 可以开箱即用地使用 log4j,无需额外设置。这种方法为将 log4j 与 Tomcat 8.5.2 集成提供了一个简单的解决方案。

版本 > 8.5.2

使用较新版本的嵌入式 Tomcat(8.5.4 及更高版本)时,LogFactory 类已包含在核心 jar 中。如果您将旧的 tomcat-embed-logging-log4j-8.5.2.jar 添加到您的类路径中,您最终将得到 (2) 两个 LogFactory 实现:

1. Core-LogFactory: Provided by Tomcat core, which loads DirectJDKLog.

2. Log4j-LogFactory: Provided by the additional log4j jar.

这种重复会导致问题,因为:

  1. 类加载器通常会选择 Core-LogFactory,因为它靠近类路径。
  2. 在类路径中拥有同一类的多个版本通常被认为是不好的做法,并且可能导致不可预测的行为。

要解决此问题,建议使用较新 Tomcat 版本的 Core-LogFactory 中实现的 ServiceLoader 方法,而不是使用较旧的 tomcat-embed-logging-log4j-8.5.2.jar。这种方法避免了类重复和潜在的冲突,提供了更可靠和可维护的日志记录设置。

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

为此,我在

org.apache.juli.logging.Log
文件夹(在我的 jar/sources 中)中添加了文件
META-INF/services
,并添加了我的“自己的”
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 中的同一个包中使用相同的类:

© www.soinside.com 2019 - 2024. All rights reserved.