深入理解 Log4j2:Java 应用的日志记录利器
在软件开发过程中,日志记录扮演着至关重要的角色。一个优秀的 Java 日志框架不仅需要易于使用,还应具备高性能、良好的扩展性以及高度的自定义能力。Log4j2 正是这样一个免费的 Java 日志库,它能满足所有这些需求。
通过将 Log4j2 集成到您的应用程序中,您将能够解锁一系列高级功能,包括高级过滤、Java 8 lambda 支持、属性查找以及自定义日志级别等。接下来,让我们一起探索如何将 Log4j2 添加到您的项目中,并深入了解其强大的功能,助您在日志记录方面更胜一筹。
什么是 Log4j2?
日志记录是一种捕获有用信息(即日志)的方式,这些信息可供后续参考和分析。 通过日志,您可以快速定位并修复应用程序代码中的问题。应用程序日志有助于理解代码执行流程,解决生产环境中的问题和错误。
除了用于诊断外,日志还可用于审计目的。例如,它可以跟踪通知消息是否成功发送给用户。
Log4j2 是目前最流行的 Java 日志库之一。它是备受推崇的 Log4j 库的继任者。由 Apache 软件基金会开发,Log4j2 是 Apache 日志服务的一部分,并以 Apache 许可证 2.0 版的免费开源软件 (FOSS) 形式发布。
Log4j2 构建于原始 Log4j 的坚实基础之上。与使用简单的 `System.out.println()` 打印语句相比,使用 Logger 具备诸多优势。它可以控制显示哪些消息,并避免显示其他不相关的日志消息。在无法使用调试器的生产环境中,正确的日志记录至关重要。
如何将 Log4j2 添加到您的项目中?
有多种方法可以将 Log4j2 添加到 Java 项目中。为了充分利用 Log4j2 的所有功能,建议使用 Java 8 或更高版本。
接下来,我们将讨论根据您的需求添加 Log4j2 的各种方法。
使用 Apache Maven 将 Log4j2 添加到项目中
如果您的项目使用 Apache Maven 作为构建系统,您需要将 Log4j2 的依赖项添加到 `pom.xml` 文件中。
<dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.20.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.20.0</version> </dependency> </dependencies>
为了更容易地在不同组件之间维护相同的版本,Log4j2 提供了一个物料清单 (BOM) `pom.xml` 文件。如果将其添加到依赖项管理中,则无需单独指定版本。
<!-- 将 BOM 添加到 dependencyManagement 中 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-bom</artifactId> <version>2.20.0</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <!-- 添加 BOM 后,不再需要指定版本 --> <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </dependency> </dependencies>
使用 Apache Gradle 将 Log4j2 添加到项目中
如果您使用 Apache Gradle 作为构建工具,您可以将 Log4j2 的依赖项添加到 `build.gradle` 文件中。
dependencies { implementation 'org.apache.logging.log4j:log4j-api:2.20.0' implementation 'org.apache.logging.log4j:log4j-core:2.20.0' }
如果您使用的是 Gradle 5.0 或更高版本,您可以选择导入 Log4j2 Maven 物料清单 (BOM),以保持依赖项版本的一致性。您可以通过将以下内容添加到 `build.gradle` 文件中来实现:
dependencies { implementation platform('org.apache.logging.log4j:log4j-bom:2.20.0') implementation 'org.apache.logging.log4j:log4j-api' runtimeOnly 'org.apache.logging.log4j:log4j-core' }
对于 Gradle 版本 2.8-4.10,没有直接导入 Maven BOM 的选项。您需要为依赖项管理功能添加额外的插件。
plugins { id 'io.spring.dependency-management' version '1.0.15.RELEASE' } dependencyManagement { imports { mavenBom 'org.apache.logging.log4j:log4j-bom:2.20.0' } } dependencies { implementation 'org.apache.logging.log4j:log4j-api' runtimeOnly 'org.apache.logging.log4j:log4j-core' }
无需构建工具即可将 Log4j2 添加到独立应用程序
如果您的项目没有构建工具,您可以从官方 Log4j2 下载页面下载所需的 Log4j2 组件版本。
下载后,您需要确保应用程序的类路径包含以下 JAR 文件:
- log4j-api-2.20.0.jar
- log4j-core-2.20.0.jar
Log4j2 的组成部分有哪些?
为了理解 Log4j2 的特性并充分利用其功能,深入了解 Log4j2 的工作原理至关重要。在 Log4j2 的底层,它由多个构建模块组成。接下来,我们将逐一探讨它们。
#1. LoggerContext(日志记录器上下文)
LoggerContext 是日志系统的核心单元。它持有应用程序中请求的所有 Logger 实例,并且包含对配置的引用。
#2. 配置
配置包含了日志系统所需的所有信息,例如 Logger、Appender、Filter 等。在 Log4j2 中,您可以使用各种文件格式(如 XML、JSON 和 YAML)定义配置,也可以通过 Log4j2 API 以编程方式定义配置。
配置中的任何属性发生更改时,都会自动重新加载,因此无需重新启动应用程序。
#3. Logger(日志记录器)
Log4j2 系统中的主要组件是 Logger。通过 `LogManager.getLogger()` 语句在应用程序代码中获取 Logger 实例,并使用该实例生成日志。可以生成各种严重级别的日志消息,例如 DEBUG、INFO、WARN、ERROR 和 FATAL。
#4. LoggerConfig(日志记录器配置)
LoggerConfig 负责特定 Logger 的行为。它定义了该 Logger 生成的日志事件的行为和设置。它允许配置不同的日志级别、设置 Appender 以及应用 Filter。
#5. Filter(过滤器)
您可以使用 Filter 来有选择地处理 Log4j2 中的日志事件。根据特定标准应用 Filter,您可以将其应用于 Logger 或 Appender。Filter 控制哪些日志事件可以进入日志处理管道进行进一步处理。通过 Filter,您可以微调日志记录行为,确保只处理相关的日志。
#6. Appender(附加器)
Appender 决定了日志消息的目标位置。一个 Logger 可以有多个 Appender。日志事件将被发送到给定 Logger 的所有 Appender。Log4j2 提供了许多预配置的 Appender,例如,`ConsoleAppender` 用于将消息记录到控制台,`FileAppender` 用于将消息输出到文件。每个 Appender 都需要自己的 Layout 来确定最终日志消息的外观。
#7. Layout(布局)
在 Log4j2 中,Layout 用于定义最终日志消息的外观。Layout 与 Appender 相关联。 Appender 确定输出目的地,而 Layout 则描述消息的输出方式。
Log4j2 的五大核心功能
Log4j2 功能丰富,这使得它在其他可用的 Java 日志框架中脱颖而出。从异步 Logger 到支持 Java 8 Lambda 表达式,Log4j2 相比其他日志框架具有显著优势。接下来,让我们探讨一下这个框架的一些突出特性。
#1. 使用插件扩展功能
在 Log4j 1.x 中,要创建扩展,需要进行大量的代码修改。Log4j2 通过引入插件系统解决了可扩展性的问题。
您可以通过在类上使用 `@Plugin` 注释来声明新的插件。借助插件的强大功能,您可以创建自己的组件,例如 Filter 和 Appender。第三方组件也可以轻松添加到库中。
#2. Java 8 Lambda 支持
随着 Log4j2 2.4 版本的发布,引入了对 Java 8 Lambda 表达式的支持。使用 Lambda 表达式,您可以内联定义日志记录逻辑。这减少了对多行检查或匿名内部类的需要。这也确保了不会不必要地执行昂贵的方法。因此,不仅代码更清晰、更易于阅读,而且系统开销也降低了。
我们来考虑一个例子,您想记录昂贵操作的结果,但仅限于启用 DEBUG 级别的情况。在支持 Lambda 之前,需要使用以下代码执行此操作:
if (logger.isDebugEnabled()) { logger.debug("The output of the given operation is: {}", expensiveOperation()); }
如果有很多这样的用例,将会不必要地引入条件检查。但是,使用 Log4j2,可以执行相同的操作,如下所示:
logger.debug("The output of the given operation is: {}", () -> expensiveOperation()
只有在启用 DEBUG 级别时,才会评估 `expensiveOperation()` 方法。不需要任何显式检查。
#3. 异步 Logger
每个日志事件都是一个 I/O 操作,这会增加系统开销。为了缓解这种情况,Log4j2 引入了异步 Logger,它在与应用程序线程不同的线程中运行。使用异步 Logger 时,调用线程在调用 `logger.log()` 方法后会立即收回控制权。
这允许它继续执行应用程序逻辑,而不必等待日志事件完成。利用这种异步行为可以实现更大的日志吞吐量。您可以选择默认使所有 Logger 异步,也可以混合使用同步和异步行为。
#4. 无垃圾记录
在 Java 中,垃圾回收是自动清除应用程序中未使用对象的过程。尽管您不必手动处理此操作,但垃圾回收确实会产生一定的开销。
如果您的应用程序在短时间内创建了大量对象,垃圾回收过程可能会占用过多的系统资源。一些日志库(包括先前版本的 Log4j)会在日志记录过程中创建大量临时对象。随后,垃圾回收器压力的增加会影响系统性能。
从 2.6 版本开始,Log4j2 以“无垃圾”模式运行。这是默认行为。因此,对象被重用,并且临时对象的创建大大减少。
下图展示了与 Log4j2 2.5 版本相比,Log4j2 2.6 版本如何缓解不必要对象的问题。
在 Log4j2 2.5 版本中,在日志记录过程中会创建大量临时对象;来源:apache.org
在 Log4j2.6 中,日志记录过程中不会创建临时对象;来源:apache.org
#5. 查找
在 Log4j2 中,您可以使用查找将上下文信息添加到日志中。 通过使用它们,您可以添加来自各种来源的数据,例如系统属性、环境变量或自定义值。 因此,您可以包含动态获取的相关信息,使日志更易于理解。
我们来考虑一个例子,您希望使用所有日志行记录用户的会话 ID。这将允许您搜索与会话 ID 对应的所有日志。
执行此操作的直接方法是单独显式地添加会话 ID,但这样做变得难以维护。 很快,您可能会忘记添加它,从而丢失有价值的信息。
logger.info("The user data has been fetched for session id {}", sessionId); ... logger.info("The transaction has been processed for session id {}", sessionId); ... logger.info("Request has been successfully processed for session id {}", sessionId);
更好的方法是使用上下文映射查找。 会话 ID 可以添加到应用程序代码中的线程上下文中。 然后可以在 Log4j2 配置内部使用该值。 因此,无需在日志消息中明确提及它。
ThreadContext.put("sessionId", sessionId);
添加值后,可以使用关键字 `ctx` 在查找中使用相同的值。
<File name="Application" fileName="application.log"> <PatternLayout> <pattern>%d %p %c{1.} [%t] $${ctx:sessionId} %m%n</pattern> </PatternLayout> </File>
如何在 Log4j2 中自定义日志级别?
Log4j2 中的日志级别用于根据日志事件的严重性或重要性对其进行分类。 在应用程序代码中记录消息时,您可以控制日志级别。
例如,`logger.debug()` 添加 DEBUG 级别。 相应地,`logger.error()` 添加了 ERROR 级别。 这决定了哪些消息最终显示在输出中。 您可以在配置文件中配置日志级别。
Log4j2 中预配置的日志级别及其对应的值如下:
级别 | 值 |
OFF | 0 |
FATAL | 100 |
ERROR | 200 |
WARN | 300 |
INFO | 400 |
DEBUG | 500 |
TRACE | 600 |
ALL | MAX |
如果日志级别设置为特定级别,则会输出该级别及其以上(具有较小值)的所有日志行。其他日志行将被忽略。
例如,如果将日志级别设置为 WARN,则将显示 WARN、ERROR 和 FATAL 消息。任何具有不同级别的日志行都将被忽略。当您在不同环境中运行相同的代码时,这尤其有用。
在开发环境中运行代码时,您可能希望将日志级别设置为 INFO 或 DEBUG,这会使您能够在开发过程中看到更多日志和帮助。 但是,在生产环境中运行时,您可能希望将其设置为 ERROR。 因此,如果发生任何异常,您可以专注于查找问题,而不必查看不必要的日志行。
除了预配置的日志级别之外,您可能还想添加自己的自定义日志级别。Log4j2 可以让您轻松地做到这一点。接下来,我们将了解如何添加自己的日志级别并在应用程序中使用它们。
#1. 使用配置文件添加自定义日志级别
您可以通过在配置文件中声明来添加自定义日志级别。
在下面的示例中,定义了一个名为 NOTICE 的自定义日志级别,其值为 450。这将其置于 INFO(值为 400)和 DEBUG(值为 500)之间。这意味着,如果日志级别设置为 NOTICE,则会记录 INFO 消息,但会跳过 DEBUG 消息。
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <CustomLevels> <CustomLevel name="NOTICE" intLevel="450" /> </CustomLevels> <Appenders> <File name="MyFile" fileName="logs/app.log"> <PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <Root level="trace"> <AppenderRef ref="MyFile" level="NOTICE" /> </Root> </Loggers> </Configuration>
#2. 在代码中添加自定义日志级别
除了在配置文件中声明之外,您还可以在代码中定义自己的自定义日志级别。
final Level VERBOSE = Level.forName("VERBOSE", 550);
这将创建一个名为 VERBOSE 的新日志级别。此日志级别介于 DEBUG(值为 500)和 TRACE(值为 600)之间。如果将记录器设置为 VERBOSE 级别,则会记录 VERBOSE 及以上级别的所有日志消息,包括 DEBUG。但是,TRACE 消息将被跳过。
#3. 在代码中使用自定义日志级别
自定义日志级别在使用之前需要先声明。您可以在配置文件或代码中声明它们。声明后,您就可以自由地使用它们。
此代码示例展示了如何声明一个名为 NOTICE 的自定义级别,然后如何使用它:
final Level NOTICE = Level.forName("NOTICE", 550); final Logger logger = LogManager.getLogger(); logger.log(NOTICE, "a notice level message");
尽管这样做会使用新创建的级别生成所需的消息,但是每次都显式传递级别可能会变得很麻烦。值得庆幸的是,您可以生成源代码以获得用于记录自定义级别的帮助程序方法。使用相同的方法,您将能够使用自己的 `logger.notice()` 方法,类似于使用 `logger.debug()` 或 `logger.error()` 方法。
Log4j2 附带了一个实用程序,可以帮助您创建自己的扩展 Logger。以下命令将创建一个名为 `CustomLogger.java` 的 Java 文件。该文件包含了现有的日志方法,以及新生成的 NOTICE 级别的方法。
java -cp log4j-core-2.20.0.jar org.apache.logging.log4j.core.tools.ExtendedLoggerGenerator com.example.CustomLogger NOTICE=450 > com/example/CustomLogger.java
生成文件后,您可以在代码中使用该类来创建新的 Logger。这些 Logger 将包含用于您的自定义日志级别的其他方法。因此,您可以扩展 Logger 的功能。
final Logger logger = CustomLogger.create(ValueFirstSmsSender.class); //这个新方法类似于使用 logger.debug() logger.notice("a notice level message");
总结
Log4j2 是一个非常强大的 Java 日志框架,它提供了广泛的功能、配置选项、性能改进等。由于日志是软件开发过程中非常重要的一部分,因此拥有像 Log4j2 这样强大的框架可以增强应用程序的能力。
Log4j2 的灵活性和可扩展性允许正确捕获应用程序中发生的事件。随后,它使您能够将日志视为调试和审核的强大工具。凭借其所有功能和改进,Log4j2 脱颖而出,成为各种软件项目的首选。