埃里克·迪特里希算法
2018 年 9 月 5 日apache
软件开发中最烦人的方面之一绝对是日志记录。若是一个非平凡的应用程序缺乏日志记录,那么维护它的人都会遇到困难,过后调试将主要是一个猜谜游戏。今天,咱们经过提供有关 log4j2 配置的教程,为解决这种状况作出了另外一贡献。json
有不少方法能够为Java记录日志。您可使用更手动的方法,但推荐的方法是采用专用的日志记录框架。这就是为何咱们要介绍 Apache log4j2,它是 log4j 的改进版本,是行业标准的日志框架之一。api
咱们将首先快速回顾一下咱们以前关于 Java 日志的文章,并介绍一些关于 log4j2 的事实。而后咱们将继续介绍咱们在上一个教程中开始编写的示例应用程序的状态。数组
以后,咱们进入文章的重点。您将学习如何配置 log4j2,从基础开始,并逐步学习更高级的主题,例如日志格式、附加程序、日志级别和日志层次结构。网络
让咱们开始吧。app
本教程中,咱们使用了 log4j 版本 2,这是来自 Apache 项目的日志框架。框架
让咱们进一步了解 Java 应用程序日志并查看 log4j2 配置。今天咱们将介绍 log4j2 配置的基本方面,以帮助您入门。运维
Log4j 的功能使其成为 Java 最流行的日志框架之一。它能够配置为多个日志记录目的地和各类日志文件格式。函数
能够在单个类级别过滤和定向日志消息,从而使开发人员和运维人员可以对应用程序消息进行精细控制。
让咱们经过使用命令行 Java 应用程序配置 log4j 来检查这些机制。
让咱们使用 log4j 进行日志记录的应用程序。
package com.company; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; public class Main { private static final Logger logger = LogManager.getLogger(Main.class); public static void main(String[] args) { String message = "Hello there!"; logger.trace(message); logger.debug(message); logger.info(message); logger.warn(message); logger.error(message); logger.fatal(message); } }
咱们在每一个 log4j 预约义的日志级别记录相同的消息:跟踪、调试、信息、警告、错误和致命。
咱们将使用 log4j 的 YAML 文件格式,所以您须要向pom.xml(或build.gradle)添加一些额外的依赖项。
<dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency> </dependencies>
设置此代码,以便您可使用您喜欢的 Java 工具构建和运行它。
让咱们在没有 log4j 配置文件的状况下运行咱们的应用程序。若是您已经有一个,请将其删除或将其移动到另外一个文件名,以便 log4j 将忽略它。
当咱们运行应用程序时,咱们会在控制台上看到:
09:38:14.114 [main] ERROR com.company.Main - Hello there! 09:38:14.119 [main] FATAL com.company.Main - Hello there!
六个日志消息中的两个,指定为“错误”和“致命”的消息被发送到控制台。
Log4j 有一个默认配置。它将记录到控制台,显示归类为“错误”或更高级别的消息。
了解 log4j 在没有配置文件的状况下如何运行颇有用,但让咱们看看如何根据咱们的须要设置它。
咱们能够经过log4j.configurationFile系统属性在特定位置为 log4j 提供配置文件。这是它将查找配置文件的第一个位置。
若是 log4j 找不到系统属性,它会在类路径中查找文件。因为 log4j 版本 2 支持四种不一样的文件格式和两种不一样的文件命名约定,所以定位文件的规则很复杂。在咱们介绍了不一样的选项后,咱们将讨论它们。
Log4j 将加载 Java 属性和 YAML、JSON 和 XML 配置文件。它经过检查文件扩展名来识别文件格式。
log4j.configurationFile
系统属性指定的文件必须具备这些文件扩展名之一,但能够具备任何基本名称。Log4j 将根据扩展名指示的格式对其进行解析。
当 log4j 扫描文件的类路径时,它会按照上面列出的顺序扫描每种格式,并在找到匹配项时中止。
例如,若是它找到一个 YAML 配置,它将中止搜索并加载它。若是没有 YAML 文件但它找到了 JSON,它将中止搜索并使用它。
当 log4j 扫描类路径时,它会查找两个文件名之一:log4j2-test.[extension] 或 log4j2.[extension]。
它首先加载测试文件,为开发人员提供了一种方便的机制,能够在不改变标准配置的状况下强制应用程序在调试或跟踪级别进行记录。
当咱们将文件格式和名称的规则放在一块儿时,咱们能够看到log4j的自我配置算法。
若是如下任何步骤成功,log4j 将中止并加载生成的配置文件。
log4j 有 12 个潜在的配置文件名。若是应用程序在生产环境中记录了没必要要的消息,则加载错误的信息可能会致使日志信息丢失或性能降低。
在部署代码以前,请确保您的应用程序只有一个配置文件,而且您知道它在哪里。若是您坚持从类路径加载配置,请在发布代码以前扫描虚假文件。
如今咱们知道如何为 log4j 提供配置,让咱们建立一个并使用它来自定义咱们的应用程序。
让咱们从默认配置开始,而后从那里修改咱们应用程序的行为。咱们将从 log4j 的配置规则中获取提示并使用 YAML。
默认配置以下所示:
Configuration: status: warn Appenders: Console: name: Console target: SYSTEM_OUT PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" Loggers: Root: level: error AppenderRef: ref: Console
使用这些内容建立一个文件名log4j2.yaml并将log4j.configurationFile设置 为指向其位置。
接下来,运行应用程序。您将看到与之前相同的输出。
09:38:14.114 [main] ERROR com.company.Main - Hello there! 09:38:14.119 [main] FATAL com.company.Main - Hello there!
咱们已经控制了应用程序的日志记录配置。如今让咱们改进它。
第一步是将咱们的日志从控制台中取出并放入一个文件中。为此,咱们须要了解appender。
Appenders 将日志消息放在它们所属的地方。默认配置提供一个控制台附加程序。顾名思义,它将消息附加到控制台。
Appenders: Console: name: Console target: SYSTEM_OUT PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
咱们想要一个文件附加程序。让咱们替换咱们的控制台 appender。
Appenders: File: name: File_Appender fileName: logfile.log PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
文件附加器有一个name,就像控制台附加器同样。可是他们有一个fileName而不是target。
与控制台 appender 相似,它们也有一个PatternLayout,咱们将在下面介绍。
这个名字不只仅是为了展现。若是咱们想用文件附加器替换控制台附加器,咱们须要让咱们的记录器 知道在哪里放置咱们的日志消息。
所以,将记录器中的ref值更改成文件附加程序的名称。
Loggers: Root: level: error AppenderRef: ref: File_Appender
如今,从新运行应用程序。它没有记录到控制台,而是将消息放在工做目录中名为logfile.log 的文件中。咱们已将日志移至文件中!
在咱们的日志文件中,咱们仍然只看到了六个日志消息中的两个。让咱们谈谈记录器以及它们如何管理日志消息。
咱们的基本配置定义了一个记录器。
Loggers: Root: level: error AppenderRef: ref: File_Appender
它有一个“错误”级别,因此它只打印错误或致命的消息。
当记录器收到日志消息时,它会根据其配置的级别传递或过滤它。此表显示了记录器配置和日志消息级别之间的关系。
所以,若是咱们更改记录器的级别,咱们将看到更多消息。将其设置为“调试”。
Loggers: Root: level: debug AppenderRef: ref: File_Appender
接下来,从新运行程序。应用程序记录全部调试级别或更高级别的消息。
记录器层次结构
Log4j 按层次结构排列记录器。这使得为各个类指定不一样的配置成为可能。
让咱们更改咱们的应用程序,看看它的实际效果。
public class Main { private static final Logger logger = LogManager.getLogger(Main.class); public static void main(String[] args) { String message = "Hello there!"; System.out.println(message); logger.debug(message); logger.info(message); logger.error(message); LoggerChild.log(); } private static class LoggerChild { private static final Logger childLogger = LogManager.getLogger(LoggerChild.class); static void log() { childLogger.debug("Hi Mom!"); } } }
咱们添加了一个内部类来建立一个记录器并用它记录一条消息。
以后主要作了记录,它调用LoggerChild 。
若是咱们使用当前配置运行它,咱们会看到新消息,而且它是从不一样的类记录的。
12:29:23.325 [main] DEBUG com.company.Main - Hello there! 12:29:23.331 [main] INFO com.company.Main - Hello there! 12:29:23.332 [main] ERROR com.company.Main - Hello there! 12:29:23.333 [main] DEBUG com.company.Main.LoggerChild - Hi Mom!
记录器具备相似于 Java 的类层次结构。全部记录器都是迄今为止咱们一直在使用的根记录器的后代。
缺乏任何特定配置的记录器继承根配置。
因此当 Main 和 LoggerChild 使用它们的类名建立记录器时,这些记录器继承了 Root 的配置,即向 File_Appender发送调试级别和更高级别的消息 。
咱们能够覆盖这两个记录器的指定配置。
Loggers: logger: - name: com.company.Main level: error additivity: false AppenderRef: ref: File_Appender - name: com.company.Main.LoggerChild level: debug additivity: false AppenderRef: ref: File_Appender Root: level: debug AppenderRef: ref: File_Appender
记录器在记录器部分命名 。因为咱们列出了两个,所以咱们使用 YAML 数组语法。
咱们设置 com.company.Main的 记录为“错误”,并 com.company.Main.LoggerChild的 为“调试”。
该加设置控制的log4j是否会从记录仪的祖先将消息发送给后代。
若是设置为 true,两个记录器将处理相同的消息。某些系统但愿将相同的消息添加到两个不一样的日志中。咱们不但愿出现这种行为,所以咱们覆盖了默认值并指定了 false。
如今再次运行程序:
12:33:11.062 [main] ERROR com.company.Main - Hello there! 12:33:11.073 [main] DEBUG com.company.Main.LoggerChild - Hi Mom!
咱们只看到来自Main的错误消息, 但仍然看到来自LoggerChild的调试消息!
就像咱们能够拥有多个 logger 同样,咱们也能够拥有多个 appender。
让咱们对咱们的配置进行一些更改。
添加第二个文件附加程序。为此,请使用原始 appender 建立一个列表,并使用不一样的名称和文件建立第二个列表。您的Appenders部分应以下所示:
Appenders: File: - name: File_Appender fileName: logfile.log PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" - name: Child_Appender fileName: childlogfile.log PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
接下来,将 LoggerChild 记录器指向新的 appender。您的记录器部分将以下所示。
Loggers: logger: - name: com.company.Main level: error additivity: false AppenderRef: ref: File_Appender - name: com.company.Main.LoggerChild level: debug additivity: false AppenderRef: ref: Child_Appender Root: level: debug AppenderRef: ref: File_Appender
如今运行该应用程序,您将看到两个不一样的日志文件,每一个文件都包含来自其关联类的消息。
日志消息格式
咱们的每一个 appender 都有一个 PatternLayout。
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
PatternLayout 是 Log4j 布局类的一个实例。Log4j 具备用于以 CSV、JSON、Syslog 和各类不一样格式记录消息的内置布局。
PatternLayout 有一组用于格式化消息的运算符,其操做相似于 C 的sprintf函数。经过指定模式,咱们能够控制由 appender 写入的日志消息的格式。
咱们的布局字符串以下所示:
"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
每一个 % 对应于日志消息中的一个字段。
PatternLayout有许多额外的操做符。
随着 appender 和 loggers 的增长,配置文件可能会变得重复。Log4j 支持变量替换以帮助减小重复并使其更易于维护。让咱们使用Properties 来优化咱们的配置。
Configuration: status: warn Properties: property: - name: "LogDir" value: "logs" - name: "DefaultPattern" value: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" Appenders: File: - name: File_Appender fileName: ${LogDir}/logfile.log PatternLayout: Pattern: ${DefaultPattern} - name: Child_Appender fileName: ${LogDir}/childlogfile.log PatternLayout: Pattern: ${DefaultPattern} Loggers: logger: - name: com.company.Main level: error additivity: false AppenderRef: ref: File_Appender - name: com.company.Main.LoggerChild level: debug additivity: false AppenderRef: ref: Child_Appender Root: level: debug AppenderRef: ref: File_Appender
在文件的顶部,咱们声明了两个属性,一个名为LogDir,另外一个名为DefaultPattern。
声明属性后,可使用大括号和美圆符号在配置中使用它: ${LogDir} 或${DefaultPattern}
LogDir 是咱们添加到两个日志文件名称中的子目录名称。当咱们运行应用程序时,log4j 将建立这个目录并将日志文件放在那里。
咱们指定DefaultPattern做为咱们两个日志文件的模式布局,将定义移到一个地方。若是咱们想修改咱们的日志文件格式,咱们如今只需担忧更改一次。
Log4j 还能够从环境中导入属性。您能够在此处找到详细信息 。
例如,若是咱们想从 Java 系统属性导入日志文件目录,咱们在 log4j 配置中将其指定为${sys : LogDir}并将 LogDir 系统属性设置为所需的目录。
Log4j 能够按期从新加载其配置,使咱们可以在不从新启动应用程序的状况下更改应用程序的日志记录配置。
将monitorInterval设置添加到文件的Configuration部分,log4j 将按指定的时间间隔扫描文件。
Configuration: monitorInterval: 30
间隔以秒为单位指定。
Log4j 是一个强大的日志框架,它容许咱们将应用程序配置为以各类不一样的方式登陆,并对不一样组件如何使用日志文件进行精细控制。
本教程涵盖了配置 log4j 的基本方面,但还有不少东西须要学习。您能够在项目网站上了解 log4j 配置 。
这篇文章是由 Eric Goebelbecker 撰写的。Eric在纽约市的金融市场工做了 25 年,为市场数据和金融信息交换 (FIX) 协议网络开发基础设施。他喜欢谈论什么使团队有效(或不那么有效!)