介绍
几乎每个大型应用程序都包含自己的日志记录或跟踪 API。
根据这一规则,E.U. SEMPER 项目决定编写自己的跟踪 API。
那是在 1996 年初。经过无数次的改进、多次改进和大量工作,API 已经发展成为 log4j,一个流行的 Java 日志记录包。
该软件包根据 Apache 软件许可证分发,这是一个由开源计划认证的成熟的开源许可证。
最新的 log4j 版本,包括完整的源代码、类文件和文档,可以在 https://logging.apache.org/log4j/2.x/index.html 找到。
将日志语句插入代码是一种低技术含量的调试方法。 这也可能是唯一的方法,因为调试器并不总是可用或适用。
这通常是多线程应用程序和大型分布式应用程序的情况。
经验表明,日志记录是开发周期的重要组成部分。 它具有几个优点。 它提供有关应用程序运行的精确上下文。
一旦插入到代码中,日志输出的生成就不需要人为干预。
此外,日志输出可以保存在持久性介质中以供日后研究。 除了在开发周期中使用之外,足够丰富的日志记录包也可以被视为审计工具。
正如 Brian W. Kernighan 和 Rob Pike 在他们真正优秀的著作《编程实践》中所说的那样:
作为个人选择,除了获取堆栈跟踪或一两个变量的值之外,我们倾向于不使用调试器。 一个原因是很容易迷失在复杂的数据结构和控制流的细节中; 我们发现单步执行一个程序比更努力地思考并在关键位置添加输出语句和自检代码效率要低。 单击语句比扫描精心放置的显示器的输出要花费更长的时间。 与单步执行到代码的关键部分相比,决定在哪里放置 print 语句花费的时间更少,即使假设我们知道关键部分在哪里。 更重要的是,调试语句留在程序中; 调试会话是短暂的。
日志记录确实有其缺点。 它可以减慢应用程序。
如果过于冗长,可能会导致滚动失明。
为了减轻这些担忧,log4j 被设计为可靠、快速和可扩展的。 由于日志记录很少是应用程序的主要焦点,因此 log4j API 力求易于理解和使用。
Log4j 2
Log4j 1.x 已在许多应用程序中得到广泛采用和使用。
然而,多年来它的发展已经放缓。
由于需要与非常旧的 Java 版本兼容,它变得更加难以维护,并于 2015 年 8 月终止使用。
它的替代品 SLF4J/Logback 对框架进行了许多必要的改进。
那么为什么要使用 Log4j 2 呢? 以下是一些原因。
-
Log4j 2 旨在用作审计日志记录框架。 Log4j 1.x 和 Logback 在重新配置时都会丢失事件。 Log4j 2 不会。 在 Logback 中,应用程序永远不会看到 Appender 中的异常。 在 Log4j 2 Appenders 中,可以配置为允许异常渗透到应用程序。
-
Log4j 2 包含基于 LMAX Disruptor 库的下一代异步记录器。 在多线程场景中,Asynchronous Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延迟低几个数量级。
-
Log4j 2 对于独立应用程序来说是无垃圾的,在稳态日志记录期间对于 Web 应用程序来说是低垃圾。 这减少了垃圾收集器的压力并且可以提供更好的响应时间性能。
-
Log4j 2 使用插件系统,通过添加新的 Appender、过滤器、布局、查找和模式转换器,无需对 Log4j 进行任何更改,就可以非常轻松地扩展框架。
-
由于插件系统配置更简单。 配置中的条目不需要指定类名。
-
支持自定义日志级别。 可以在代码或配置中定义自定义日志级别。
-
支持 lambda 表达式。 仅当启用请求的日志级别时,在 Java 8 上运行的客户端代码才可以使用 lambda 表达式延迟构造日志消息。 不需要显式级别检查,从而产生更清晰的代码。
-
支持消息对象。 消息允许支持有趣和复杂的构造通过日志系统传递并被有效地操作。 用户可以自由创建自己的消息类型并编写自定义布局、过滤器和查找来操作它们。
-
Log4j 1.x 支持 Appender 上的过滤器。 Logback 添加了 TurboFilters 以允许在事件被 Logger 处理之前对其进行过滤。 Log4j 2 支持可以配置为在事件由 Logger 处理之前处理事件的过滤器,因 为它们是由 Logger 或 Appender 处理的。
-
许多 Logback Appender 不接受布局,只会以固定格式发送数据。 大多数 Log4j 2 Appenders 接受布局,允许以任何需要的格式传输数据。
-
Log4j 1.x 和 Logback 中的布局返回一个字符串。 这导致了在 Logback 编码器中讨论的问题。 Log4j 2 采用布局始终返回字节数组的更简单方法。 这样做的好处是,这意味着它们几乎可以在任何 - Appender 中使用,而不仅仅是写入 OutputStream 的 Appender。
-
Syslog Appender 支持 TCP 和 UDP,并支持 BSD syslog 和 RFC 5424 格式。
-
Log4j 2 利用 Java 5 并发支持并在可能的最低级别执行锁定。 Log4j 1.x 有已知的死锁问题。 其中许多已在 Logback 中修复,但许多 Logback 类仍然需要相当高级别的同步。
-
它是一个 Apache 软件基金会项目,遵循所有 ASF 项目使用的社区和支持模型。 如果您想贡献或获得提交更改的权利,只需遵循贡献中概述的路径即可。
如何系统学习 log4j2
要系统学习log4j2,您可以按照以下步骤进行:
-
了解日志概念和重要性: 学习关于日志的基本概念,为什么在应用程序中使用日志记录是重要的,以及日志记录的常见用途和好处。
-
熟悉log4j2文档: 访问log4j2的官方网站(https://logging.apache.org/log4j/2.x/)并仔细阅读官方文档。文档提供了关于log4j2的详细信息,包括配置、API和用法示例。
-
安装和配置log4j2: 按照log4j2文档中的指南,将log4j2集成到您的应用程序中。了解如何设置和配置log4j2,包括日志级别、输出格式、目标(如控制台、文件、数据库)等。
-
学习日志级别: 了解log4j2中的不同日志级别,如DEBUG、INFO、WARN、ERROR等,以及它们的用途和适当的使用场景。
-
使用日志记录器: 学习如何在应用程序中使用log4j2记录日志。了解如何获取日志记录器、使用不同的日志级别记录日志消息、参数化日志消息等。
-
理解Appenders和Layouts: 了解log4j2中的Appenders和Layouts的概念。Appenders定义日志消息的输出目标(如控制台、文件),而Layouts定义日志消息的格式。
-
配置文件: 学习如何使用log4j2的配置文件来定义日志记录器、Appenders、Layouts和其他配置选项。掌握配置文件的语法和常用配置示例。
-
异常日志处理: 了解如何在log4j2中记录和处理异常日志。学习如何捕获异常并将其记录为日志消息,以及如何配置异常日志的格式。
-
性能和优化: 研究log4j2的性能特性和最佳实践,以优化日志记录的性能。了解异步日志记录、线程上下文管理、日志事件过滤等高级功能。
-
集成其他工具和框架: 了解log4j2如何与其他工具和框架集成,例如使用SLF4J进行日志门面,使用log4j2与Spring框架集成等。
-
实践和调试: 在实际项目中使用log4j2,编写日志记录代码并进行调试。熟悉常见的问题和解决方法,以及如何使用调试工具进行故障排除。
-
持续学习和社区参与: 订阅log4j2的邮件列表、加入社区论坛或聊天室,以便与其他log4j2用户和开发者交流经验和知识。参与社区活动,了解最新的更新、修复和功能增强,并分享您的学习和发现。
-
阅读书籍和教程: 寻找相关的书籍、教程或在线资源,深入了解log4j2的高级概念和用法。这些资源可以帮助您扩展您的log4j2知识,并提供更多的示例和实践经验。
-
实际项目中应用log4j2: 在实际的项目中广泛应用log4j2,从小规模的应用程序到大型复杂系统。通过实践来加深对log4j2的理解,并在实际场景中应用最佳实践和技巧。
-
调优和故障排除: 针对实际项目中的性能问题或故障,深入研究log4j2的性能调优和故障排除技术。了解如何识别和解决常见的问题,以及如何配置和优化log4j2以满足特定需求。
-
持续关注更新和漏洞修复: 定期检查log4j2的更新和漏洞修复,并及时升级到最新版本,以确保您的应用程序不受已知的安全漏洞影响。
通过以上步骤,您可以系统地学习log4j2,并逐渐成为一个熟练的log4j2用户。
记住,实践是最好的学习方式,因此在学习的同时尽量在实际项目中应用log4j2,以加深对它的理解和掌握。祝您学习顺利!
架构
Log4j 使用下图中显示的类。
主要组件
使用 Log4j 2 API 的应用程序将从 LogManager 请求具有特定名称的 Logger。
LogManager 将找到合适的 LoggerContext,然后从中获取 Logger。 如果必须创建 Logger,它将与 LoggerConfig 相关联,该 LoggerConfig 包含 a) 与 Logger 相同的名称,b) 父包的名称,或 c) 根 LoggerConfig。 LoggerConfig 对象是根据配置中的 Logger 声明创建的。 LoggerConfig 与实际传递 LogEvents 的 Appenders 相关联。
记录器层次结构 Logger Hierarchy
与普通 System.out.println 相比,任何日志记录 API 的首要优势在于它能够禁用某些日志语句,同时允许其他日志语句不受阻碍地打印。
此功能假定日志记录空间,即所有可能的日志记录语句的空间,是根据某些开发人员选择的标准进行分类的。
在 Log4j 1.x 中,Logger 层次结构是通过 Logger 之间的关系来维护的。
在 Log4j 2 中,这种关系不再存在。 相反,层次结构是在 LoggerConfig 对象之间的关系中维护的。
Loggers 和 LoggerConfigs 是命名实体。
记录器名称区分大小写,并遵循分层命名规则:
- 命名层次结构
如果一个 LoggerConfig 的名称后跟一个点是后代记录器名称的前缀,则称该 LoggerConfig 是另一个 LoggerConfig 的祖先。
如果 LoggerConfig 与后代 LoggerConfig 之间没有祖先,则称其为子 LoggerConfig 的父代。
例如,名为“com.foo”的 LoggerConfig 是名为“com.foo.Bar”的 LoggerConfig 的父级。
同样,“java”是“java.util”的父级和“java.util.Vector”的祖先。 大多数开发人员应该熟悉这种命名方案。
根 LoggerConfig 位于 LoggerConfig 层次结构的顶部。 它的特殊之处在于它始终存在并且是每个层次结构的一部分。 直接链接到根LoggerConfig的Logger可以通过如下方式获取:
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
或者更加简单的:
Logger logger = LogManager.getRootLogger();
可以使用 LogManager.getLogger 静态方法通过传递所需记录器的名称来检索所有其他记录器。 可以在 Log4j 2 API 中找到有关日志记录 API 的更多信息。
日志上下文 LoggerContext
LoggerContext 充当日志系统的定位点。 但是,根据情况,应用程序中可能有多个活动的 LoggerContext。 有关 LoggerContext 的更多详细信息,请参阅日志分离部分。
配置 Configuration
每个 LoggerContext 都有一个活动的配置。 Configuration 包含所有 Appenders、context-wide Filters、LoggerConfigs 并包含对 StrSubstitutor 的引用。 在重新配置期间,将存在两个配置对象。 一旦所有记录器都被重定向到新配置,旧配置将被停止并丢弃。
记录仪 Logger
如前所述,Loggers 是通过调用 LogManager.getLogger 创建的。 Logger 本身不执行任何直接操作。 它只有一个名称并与 LoggerConfig 相关联。 它扩展了 AbstractLogger 并实现了所需的方法。 随着配置的修改,Loggers 可能会与不同的 LoggerConfig 相关联,从而导致其行为被修改。
检索记录器 Retrieving Loggers
使用相同名称调用 LogManager.getLogger 方法将始终返回对完全相同的 Logger 对象的引用。
例如,在
Logger x = LogManager.getLogger("wombat");
Logger y = LogManager.getLogger("wombat");
x 和 y 指的是完全相同的 Logger 对象。
log4j 环境的配置通常在应用程序初始化时完成。
首选方法是读取配置文件。 这在配置中讨论。
Log4j 使得通过软件组件命名记录器变得容易。
这可以通过在每个类中实例化一个记录器来实现,记录器名称等于类的完全限定名称。
这是定义记录器的一种有用且直接的方法。 由于日志输出带有生成 Logger 的名称,因此这种命名策略可以轻松识别日志消息的来源。
然而,这只是一种可能的命名记录器的策略,尽管很常见。 Log4j 不限制可能的记录器集。 开发人员可以根据需要自由命名记录器。
由于在其所属类之后命名 Loggers 是一种常见的习惯用法,因此提供了方便的方法 LogManager.getLogger() 以自动使用调用类的完全限定类名作为 Logger 名称。
尽管如此,以记录器所在的类命名记录器似乎是迄今为止已知的最佳策略。
记录器配置 LoggerConfig
LoggerConfig 对象是在日志记录配置中声明 Loggers 时创建的。
LoggerConfig 包含一组过滤器,这些过滤器必须允许 LogEvent 在传递给任何 Appender 之前通过。 它包含对应用于处理事件的 Appenders 集的引用。
日志级别
LoggerConfigs 将被分配一个日志级别。
内置级别集包括 ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL 和 OFF。
Log4j 2 还支持自定义日志级别。 获得更多粒度的另一种机制是改用标记。
OFF 和 ALL 级别不适用于调用日志记录 API。
在配置中指定 OFF 意味着没有日志记录事件应该匹配,而指定 ALL 意味着所有事件都匹配,包括自定义事件。
但是,OFF 可用于在特殊情况下记录 API 调用,在这种情况下,无论配置如何,都应始终记录事件。
但是,通常建议改用具有相应全局标记过滤器的标记。
Log4j 1.x 和 Logback 都有“Level Inheritance”的概念。
在 Log4j 2 中,Loggers 和 LoggerConfigs 是两个不同的对象,因此这个概念的实现方式不同。
每个 Logger 引用适当的 LoggerConfig,后者又可以引用其父级,从而达到相同的效果。
下面是五个表,其中包含各种分配的级别值以及将与每个 Logger 关联的结果级别。
请注意,在所有这些情况下,如果未配置根 LoggerConfig,则会为其分配默认级别。
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
root | root | DEBUG | DEBUG |
X | root | DEBUG | DEBUG |
X.Y | root | DEBUG | DEBUG |
X.Y.Z | root | DEBUG | DEBUG |
Filter 过滤器
除了如前一节所述发生的自动日志级别过滤之外,Log4j 还提供了可以在控制传递给任何 LoggerConfig 之前、在控制传递给 LoggerConfig 之后但在调用任何 Appender 之前、在控制传递之后应用的过滤器 到 LoggerConfig 但在调用特定 Appender 之前,以及在每个 Appender 上。 以与防火墙过滤器非常相似的方式,每个过滤器可以返回三种结果之一:接受、拒绝或中立。 Accept 的响应意味着不应调用其他过滤器并且事件应该进行。 Deny 的响应意味着应该立即忽略事件并将控制权返回给调用者。 Neutral 的响应表示应该将事件传递给其他过滤器。 如果没有其他过滤器,将处理该事件。
尽管过滤器可能会接受事件,但事件仍可能不会被记录。 当事件被 pre-LoggerConfig 过滤器接受但随后被 LoggerConfig 过滤器拒绝或被所有 Appender 拒绝时,可能会发生这种情况。
Appender 追加器
根据他们的记录器有选择地启用或禁用日志记录请求的能力只是图片的一部分。
Log4j 允许记录请求打印到多个目的地。
在 log4j 中,输出目的地称为 Appender。
目前,存在用于控制台、文件、远程套接字服务器、Apache Flume、JMS、远程 UNIX Syslog 守护程序和各种数据库 API 的附加程序。
有关可用的各种类型的更多详细信息,请参阅 Appenders 部分。 多个 Appender 可以附加到一个 Logger。
通过调用当前 Configuration 的 addLoggerAppender 方法,可以将 Appender 添加到 Logger。 如果与 Logger 名称匹配的 LoggerConfig 不存在,将创建一个,Appender 将附加到它,然后将通知所有 Loggers 更新其 LoggerConfig 引用。
给定记录器的每个启用的记录请求都将转发到该记录器的 LoggerConfig 中的所有附加程序以及 LoggerConfig 父项的附加程序。
换句话说,Appenders 是从 LoggerConfig 层次结构中附加继承的。 例如,如果将控制台附加程序添加到根记录器,则所有启用的日志记录请求至少会在控制台上打印。 如果另外将文件附加程序添加到 LoggerConfig,比如 C,那么为 C 和 C 的子级启用的日志记录请求将打印在文件中和控制台上。 可以通过在配置文件中的 Logger 声明上设置 additivity=”false” 来覆盖此默认行为,以便 Appender 累积不再累加。
管理 appender 可加性的规则总结如下。
Appender 可加性
Logger L 的日志语句的输出将转到与 L 关联的 LoggerConfig 中的所有 Appender 以及该 LoggerConfig 的祖先。 这就是术语“appender additivity”的含义。
但是,如果与 Logger L 关联的 LoggerConfig 的祖先,比如 P,将可加性标志设置为 false,那么 L 的输出将被定向到 L 的 LoggerConfig 中的所有附加程序,并且它的祖先直到并包括 P 但不是 P的任何祖先。
默认情况下,记录器将其可加性标志设置为 true。
layout 布局
通常情况下,用户不仅希望自定义输出目标,还希望自定义输出格式。
这是通过将 Layout 与 Appender 相关联来实现的。
Layout 负责根据用户的意愿格式化 LogEvent,而 appender 负责将格式化的输出发送到其目的地。
PatternLayout 是标准 log4j 发行版的一部分,它允许用户根据类似于 C 语言 printf 函数的转换模式指定输出格式。
例如,具有转换模式 %r [%t] %-5p %c - %m%n
的 PatternLayout 将输出类似于:
176 [main] INFO org.foo.Bar - Located nearest gas station.
第一个字段是自程序启动以来经过的毫秒数。 第二个字段是发出日志请求的线程。 第三个字段是日志语句的级别。 第四个字段是与日志请求关联的记录器的名称。 ‘-‘ 之后的文本是语句的消息。
Log4j 为各种用例提供了许多不同的布局,例如 JSON、XML、HTML 和 Syslog(包括新的 RFC 5424 版本)。
其他附加程序(例如数据库连接器)填充指定的字段而不是特定的文本布局。
同样重要的是,log4j 将根据用户指定的标准呈现日志消息的内容。
例如,如果您经常需要记录 Oranges,这是您当前项目中使用的一种对象类型,那么您可以创建一个接受 Orange 实例的 OrangeMessage 并将其传递给 Log4j,这样当 Orange 对象可以格式化为适当的字节数组时 必需的。
StrSubstitutor 和 StrLookup
StrSubstitutor 类和 StrLookup 接口是从 Apache Commons Lang 借来的,然后进行了修改以支持评估 LogEvents。
此外,Interpolator 类是从 Apache Commons Configuration 借用的,以允许 StrSubstitutor 评估来自多个 StrLookup 的变量。
它也被修改为支持评估 LogEvents。
这些一起提供了一种机制,允许配置引用来自系统属性、配置文件、ThreadContext Map、LogEvent 中的 StructuredData 的变量。
如果组件能够处理变量,则可以在处理配置时或在处理每个事件时解析变量。