嵌入式 Tomcat 使用 log4j 进行日志记录
Embedded Tomcat using log4j for logging
我正在使用嵌入式 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(及其配置)。
我尝试添加以下依赖项(8.5.4
版本在 Maven 存储库或 Maven Central 上不可用),但没有成功。
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-log4j</artifactId>
<version>8.5.2</version>
</dependency>
有谁知道如何让嵌入式 Tomcat 使用 log4j(版本 1,我没有使用 log4j2)进行登录?
我查看了 at/tried 以下 Whosebug 答案:
https://tomcat.apache.org/tomcat-8.0-doc/logging.html
所以我查看了文档,其中提到 log4j
作为日志记录框架。它提到 tomcat-juli-adapters.jar
,我找不到嵌入式版本(它与 "normal" 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
,但是standalone是不同的,我看到作者使用了类似的依赖,但不确定是否有解决方案。
How to enable embedded tomcat logging
首先我认为他可能是解决方案,但它只是处理嵌入式 Tomcat 的额外日志记录。我希望嵌入式 Tomcat 使用应用程序 log4j
,所以一个 log4j.properties
文件定义了所有内容的记录方式。
Logging in Embedded Tomcat
我不确定为什么这个答案甚至被标记为正确,但这只是解释 Tomcat 如何写入 catalina.out
文件,而不是嵌入式 Tomcat 的日志记录如何工作。
我花了一些时间,但在我获得 8.5.4
实现的源代码后,我意识到 juli
日志实现被添加到 core
jar 中。
版本 <= 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 中。因此,当在 class 路径上添加较旧的 tomcat-embed-logging-log4j-8.5.2.jar
时,现在有两个 LogFactory
实现可用。第一个由 core
提供并加载 DirectJDKLog
(我将此称为 Core-LogFactory
),第二个通过 log4j
提供(称为 Log4j-LogFactory
).因此,当 LogFactory
从 class 路径加载时, Core-LogFactory
被选中(因为它在同一个 jar 中,因此 "closer" (不需要太深入class路径加载顺序))。通常,在 class 路径上使用相同的 class(在同一个包中)是不好的做法。这只会导致混乱,你 mostly 永远不知道实际使用了哪个 class (是的,我知道有方法和规则,但长话短说,这不好) .因此,我决定不再使用 tomcat-embed-logging-log4j-8.5.2.jar
,而是遵循 ServiceLoader
方法,该方法实际上在较新版本 (https://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;
}
为此,我在 META-INF/services
文件夹中添加了文件 org.apache.juli.logging.Log
(在我的 jar/sources 中)并添加了我的 "own" 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 中的同一个包中使用相同的 class:
我正在使用嵌入式 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(及其配置)。
我尝试添加以下依赖项(8.5.4
版本在 Maven 存储库或 Maven Central 上不可用),但没有成功。
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-log4j</artifactId>
<version>8.5.2</version>
</dependency>
有谁知道如何让嵌入式 Tomcat 使用 log4j(版本 1,我没有使用 log4j2)进行登录?
我查看了 at/tried 以下 Whosebug 答案:
https://tomcat.apache.org/tomcat-8.0-doc/logging.html 所以我查看了文档,其中提到
log4j
作为日志记录框架。它提到tomcat-juli-adapters.jar
,我找不到嵌入式版本(它与 "normal" 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
,但是standalone是不同的,我看到作者使用了类似的依赖,但不确定是否有解决方案。How to enable embedded tomcat logging 首先我认为他可能是解决方案,但它只是处理嵌入式 Tomcat 的额外日志记录。我希望嵌入式 Tomcat 使用应用程序
log4j
,所以一个log4j.properties
文件定义了所有内容的记录方式。Logging in Embedded Tomcat 我不确定为什么这个答案甚至被标记为正确,但这只是解释 Tomcat 如何写入
catalina.out
文件,而不是嵌入式 Tomcat 的日志记录如何工作。
我花了一些时间,但在我获得 8.5.4
实现的源代码后,我意识到 juli
日志实现被添加到 core
jar 中。
版本 <= 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 中。因此,当在 class 路径上添加较旧的 tomcat-embed-logging-log4j-8.5.2.jar
时,现在有两个 LogFactory
实现可用。第一个由 core
提供并加载 DirectJDKLog
(我将此称为 Core-LogFactory
),第二个通过 log4j
提供(称为 Log4j-LogFactory
).因此,当 LogFactory
从 class 路径加载时, Core-LogFactory
被选中(因为它在同一个 jar 中,因此 "closer" (不需要太深入class路径加载顺序))。通常,在 class 路径上使用相同的 class(在同一个包中)是不好的做法。这只会导致混乱,你 mostly 永远不知道实际使用了哪个 class (是的,我知道有方法和规则,但长话短说,这不好) .因此,我决定不再使用 tomcat-embed-logging-log4j-8.5.2.jar
,而是遵循 ServiceLoader
方法,该方法实际上在较新版本 (https://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;
}
为此,我在 META-INF/services
文件夹中添加了文件 org.apache.juli.logging.Log
(在我的 jar/sources 中)并添加了我的 "own" 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 中的同一个包中使用相同的 class: