经常问的问题

我看到此错误“使用 SimpleLogger 无法找到日志记录实现”。怎么了?

您的 Classpath 中有 log4j-api-2.x jar 文件,但仍需要将 log4j-core-2.x jar 添加到 Classpath。 (此外,您似乎正在使用 Log4j 2 的旧版本.您可能需要升级.)

我需要哪些 JAR 文件?

您至少需要 log4j-api-2.x 和 log4j-core-2.x jar 文件。

如果您的应用程序调用另一个日志框架的 API,并且您想将日志调用路由到 Log4j 2 实现,则其他 jar 是必需的。

该图显示了哪些 JAR 对应于哪些系统

当您的应用程序调用 Log4j 2 API 并希望将日志记录调用路由到 SLF4J 实现时,可以使用 log4j-to-slf4j 适配器 jar。

该图显示了将 Log4j 2 API 与 SLF4J 一起使用的依赖关系流

一些 Log4j 组件具有带有可选依赖项的功能。组件页面将有更多详细信息。例如,log4j 核心组件页面概述了哪些 log4j 核心功能具有外部依赖性。

如何排除冲突的依赖关系?

在几种情况下,您最终可能会遇到冲突的依赖关系,尤其是可传递包含的依赖关系。下表显示了左侧的每个 Log4j 依赖项(org.apache.logging.log4j 的隐式 groupId),可以安全地排除右侧的以下依赖项(格式为 groupId:artifactId)。

Log4j Dependency排除的依存关系
[log4j-1.2-api](log4j-1.2-api)log4j:log4jorg.slf4j:log4j-over-slf4j
[log4j-core](log4j-core)log4j:log4jch.qos.logback:logback-coreorg.apache.logging.log4j:log4j-to-slf4j
[log4j-jcl](log4j-jcl)org.slf4j:jcl-over-slf4j
[log4j-jul](log4j-jul)org.slf4j:jul-to-slf4j
[log4j-slf4j-impl](log4j-slf4j-impl)org.apache.logging.log4j:log4j-to-slf4jch.qos.logback:logback-core

使用 Apache Maven,可以在项目中全局排除依赖项,如下所示:

<dependencies>
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    <scope>provided</scope>
  </dependency>
</dependencies>

对于特定的依赖关系,也可以明确排除依赖关系。例如,要使用具有 Log4j 2 而不是 Log4j 1.x 的项目:

<dependencies>
  <dependency>
    <groupId>com.example</groupId>
    <artifactId>example-project</artifactId>
    <version>1.0</version>
    <exclusions>
      <exclusion>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
      </exclusion>
      <exclusion>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.13.3</version>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.13.3</version>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-1.2-api</artifactId>
    <version>2.13.3</version>
  </dependency>
</dependencies>

依赖关系可以在 Gradle 中全局排除,如下所示:

configurations {
  all*.exclude group: 'log4j', module: 'log4j'
}

上述 Maven 排除项的等效 Gradle 配置如下所示:

dependencies {
  compile('com.example:example-project:1.0') {
    exclude group: 'log4j', module: 'log4j'
    exclude group: 'org.slf4j', module: 'slf4j-log4j12'
  }
  compile('org.apache.logging.log4j:log4j-core:2.13.3')
  compile('org.apache.logging.log4j:log4j-slf4j-impl:2.13.3')
  compile('org.apache.logging.log4j:log4j-1.2-api:2.13.3')
}

如何指定配置文件位置?

默认情况下,Log4j 在 Classpath 中查找名为 log4j2.xml (不是 log4j.xml)的配置文件。

您还可以使用以下系统属性指定配置文件的完整路径:-Dlog4j.configurationFile = path/to/log4j2.xml

该属性也可以包含在名为 log4j2.component.properties 的 Classpath 资源文件中。

Web 应用程序可以使用 servlet 上下文参数指定 Log4j 配置文件的位置。请参见“ Web 应用程序”手册页中的“使用 Log4j 2”的this section

如何在不带配置文件的代码中配置 log4j2?

从版本 2.4 开始,Log4j 2 提供了用于程序配置的 API。新的ConfigurationBuilder API允许您通过构造组件定义来在代码中创建配置,而无需了解诸如 Loggers 和 Appenders 之类的实际配置对象的内部。

如何使用特定的配置文件在代码中重新配置 log4j2?

请参见以下示例。请注意,此 LoggerContext 类不是公共 API 的一部分,因此您的代码可能会随任何次要发行版而中断。

// import org.apache.logging.log4j.core.LoggerContext;

LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
File file = new File("path/to/a/different/log4j2.xml");

// this will force a reconfiguration
context.setConfigLocation(file.toURI());

如何关闭代码中的 log4j2?

通常,不需要手动执行此操作。每个 LoggerContext 注册一个关闭钩子,该钩子将在 JVM 退出时负责释放资源(除非系统属性 log4j.shutdownHookEnabled 设置为 false)。 Web 应用程序应在其 Classpath 中包括 log4j-web 模块,该模块可禁用关闭钩子,但在 Web 应用程序停止时清除 log4j 资源。

但是,如果需要手动关闭 Log4j,则可以按照以下示例进行操作。请注意,有一个可选参数用于指定要关闭的 LoggerContext。

import org.apache.logging.log4j.LogManager;

// ...

LogManager.shutdown();

如何将不同级别的日志消息发送到不同的附加程序?

您无需声明单独的 Logger 即可实现此目的。您可以在 AppenderRef 元素上设置日志记录级别。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <File name="file" fileName="app.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m %ex%n</Pattern>
      </PatternLayout>
    </File>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="file" level="DEBUG"/>
      <AppenderRef ref="STDOUT" level="INFO"/>
    </Root>
  </Loggers>
</Configuration>

如何调试配置?

首先,请确保您的Classpath上有正确的 jar 文件。您至少需要 log4j-api 和 log4j-core。

接下来,检查您的配置文件的名称。默认情况下,log4j2 将在 Classpath 上查找名为 log4j2.xml 的配置文件。注意文件名中的“ 2”! (有关更多详细信息,请参见配置手册页。)

从 log4j-2.9 起

从 log4j-2.9 开始,如果定义了系统属性 log4j2.debug(带或不带值),log4j2 将所有内部日志记录到控制台。

在 log4j-2.9 之前

在 log4j-2.9 之前,可以在两个地方控制内部日志记录:

如果正确找到配置文件,则可以通过在配置文件中设置\ 来控制 log4j2 内部状态日志记录。这将在控制台上显示有关配置过程中发生的情况的详细 log4j2-internal 日志语句。这可能有助于解决配置问题。默认情况下,状态 Logger 级别为 WARN,因此仅在出现问题时才看到通知。

如果找不到正确的配置文件,您仍然可以通过设置系统属性-Dorg.apache.logging.log4j.simplelog.StatusLogger.level = TRACE 来启用 log4j2 内部状态日志记录。

如何动态写入单独的日志文件?

RoutingAppender。您可以在配置中定义多个路由,并将值放在 ThreadContextMap 中,以确定该线程中的后续事件记录到哪个日志文件中。

您可以使用 ThreadContextMap 值来确定日志文件名。

<Routing name="Routing">
  <Routes pattern="$${ctx:ROUTINGKEY}">

    <!-- This route is chosen if ThreadContext has value 'special' for key ROUTINGKEY. -->
    <Route key="special">
      <RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="logs/special-${ctx:ROUTINGKEY}.log"
	filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-special-%d{yyyy-MM-dd}-%i.log.gz">
	<PatternLayout>
	  <pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>
	</PatternLayout>
	<Policies>
	  <TimeBasedTriggeringPolicy interval="6" modulate="true" />
          <SizeBasedTriggeringPolicy size="10 MB" />
	</Policies>
      </RollingFile>
    </Route>

    <!-- This route is chosen if ThreadContext has no value for key ROUTINGKEY. -->
    <Route key="$${ctx:ROUTINGKEY}">
      <RollingFile name="Rolling-default" fileName="logs/default.log"
	filePattern="./logs/${date:yyyy-MM}/default-%d{yyyy-MM-dd}-%i.log.gz">
        <PatternLayout>
	  <pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>
        </PatternLayout>
        <Policies>
          <TimeBasedTriggeringPolicy interval="6" modulate="true" />
          <SizeBasedTriggeringPolicy size="10 MB" />
        </Policies>
      </RollingFile>
    </Route>

    <!-- This route is chosen if ThreadContext has a value for ROUTINGKEY
         (other than the value 'special' which had its own route above).
         The value dynamically determines the name of the log file. -->
    <Route>
      <RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="logs/other-${ctx:ROUTINGKEY}.log"
	filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-other-%d{yyyy-MM-dd}-%i.log.gz">
	<PatternLayout>
	  <pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>
	</PatternLayout>
	<Policies>
	  <TimeBasedTriggeringPolicy interval="6" modulate="true" />
	  <SizeBasedTriggeringPolicy size="10 MB" />
	</Policies>
      </RollingFile>
    </Route>
  </Routes>
</Routing>

如何以编程方式设置 Logger 的级别?

您可以使用 Log4j Core 中的Configurator类来设置 Logger 的级别。请注意,Configurator 类不是公共 API 的一部分。

// org.apache.logging.log4j.core.config.Configurator;

Configurator.setLevel("com.example.Foo", Level.DEBUG);

// You can also set the root logger:
Configurator.setRootLevel(Level.DEBUG);

如何设置日志存档保留策略?如何删除旧的日志 Files?

滚动文件附加程序(和滚动随机访问文件附加程序)的 DefaultRolloverStrategy 支持Delete元素。

从指定的基本目录开始,您可以删除满足某些条件的所有文件,例如,与给定文件名模式匹配且早于某些天数的所有文件。可能存在更复杂的条件,并且如果内置条件不足,则用户可以通过创建plugin conditions或编写script condition来提供自定义条件。

使用 Log4j 2 API 与使用 SLF4J API 有何取舍?

Log4j 2 API 和 SLF4J 有很多共同点。他们俩的共同目标是将日志记录 API 与实现完全分开。我们相信 Log4j 2 API 可以帮助您提高应用程序的性能,同时提供更多的功能和灵 Active。

可能存在一个问题,即使用 Log4j 2 API 会将您的应用程序紧密耦合到 Log4j2.情况并非如此:编码为 Log4j 2 API 的应用程序始终可以选择使用任何兼容 SLF4J 的库作为其日志记录实现。从 log4j 到 slf4j 适配器。有关详情,请参见which jars常见问题解答条目。

使用 Log4j 2 API 有几个优点:

  • SLF4J 强制您的应用程序记录字符串。如果要记录文本,Log4j 2 API 支持记录任何 CharSequence,但也支持按原样记录任何对象。日志记录实现负责处理此对象,并且我们认为将应用程序限制为日志记录字符串是一个设计错误。

  • Log4j 2 API 提供对日志记录Message objects的支持。消息允许对有趣和复杂的结构的支持通过日志系统传递并得到有效的操纵。用户可以自由创建自己的消息类型,并编写自定义布局,过滤器和查找来操纵它们。

  • Log4j 2 API 支持 Java 8 lambda expressions

  • Log4j 2 API 对garbage-free logging具有更好的支持:在记录 CharSequence 对象时,它避免创建 vararg 数组并避免创建 String。

使用 SLF4J API 时 Log4j 2 是否仍然没有垃圾?

是的,log4j-slf4j-impl 绑定(与 log4j-core 一起)将 org.slf4j.Logger 方法实现为不依赖 GC 的方法。但是,请记住,存在一些限制:

SLF4J API 最多只能为参数化消息提供两个参数。除此之外,还使用 varargs 为参数数组创建一个临时对象。 Log4j 2.6 API 具有用于最多十个展开参数的方法。

另一个考虑因素是 SLF4J API 会强制您的应用程序记录字符串。 Log4j 2 API 使您可以记录任何 java.lang.CharSequence 甚至任何对象。 Log4j 可以记录任何实现 java.lang.CharSequence 或 org.apache.logging.log4j.util.StringBuilderFormattable 的对象,而不会创建垃圾。

[org.slf4j.spi.LocationAwareLogger :: log] #log(org.slf4j.Marker,java.lang.String,int,java.lang.String,java.lang.Object(http://www.slf4j.org/api/org/slf4j/spi/LocationAwareLogger.html [],java.lang。在 log4j-slf4j-impl 绑定中,尚未以无垃圾方式实现 Throwable))方法。它为每个呼叫创建一个新的消息对象。

如何在不创建垃圾的情况下记录我的域对象?

一种选择是让域对象实现 java.lang.CharSequence。但是,对于许多域对象而言,不分配临时对象而实现它可能并非易事。

另一种方法是实现 org.apache.logging.log4j.util.StringBuilderFormattable 接口。如果记录了实现此接口的对象,则将调用其 formatTo 方法而不是 toString()。

package org.apache.logging.log4j.util;
public interface StringBuilderFormattable {
    /**
     * Writes a text representation of this object into the specified {@code StringBuilder},
     * ideally without allocating temporary objects.
     *
     * @param buffer the StringBuilder to write into
     */
     void formatTo(StringBuilder buffer);
}

如何创建显示正确的类,方法和行号的自定义 Logger 包装?

Log4j 会记住 Logger 的全限定类名(FQCN),并在配置为打印位置时使用它在每个日志事件中遍历堆栈跟踪。 (请注意,使用位置记录很慢,并且可能会影响应用程序的性能.)

定制 Logger 包装程序的问题在于它们具有与实际 Logger 不同的 FQCN,因此 Log4j 找不到调用定制 Logger 的位置。

解决方案是提供正确的 FQCN。最简单的方法是让 Log4j 为您生成 Logger 包装器。 Log4j 随附 Logger 包装器生成器工具。该工具最初旨在支持自定义日志级别,并记录在here中。

生成的 Logger 代码将处理 FQCN。