我正在使用嵌入式 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 的日志记录如何工作。我花了一段时间,但在获得
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.
这种重复会导致问题,因为:
要解决此问题,建议使用较新 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 中的同一个包中使用相同的类: