目录
前言
在当今的软件开发领域中,日志记录是调试、监控和审计应用程序不可或缺的一部分。高效的日志记录不仅能帮助开发者理解程序的运行流程,还能在运行时出现问题时提供重要的线索。Apache Log4j 2 是一款广受欢迎的日志框架,它在 Log4j 1.x 的基础上进行了重大改进,并且相较于其他流行的框架如 Logback 也提供了更多的优化。本文旨在介绍 Log4j 2 的主要特点和如何使用它来增强应用程序的日志能力。
一、Log4j2简介与特征
Apache Log4j 2 是对Log4j的升级,它比其前身Log4j 1.x 提供了重大改进,并提供了Logback中可用的许多改进,同时秀修复了Logback架构中的一些问题。Log4j2包含如下特征:
- 性能提升:Log4j2包含基于LMAX Disruptor库的下一代异步记录器。在多线程场景中,异步记录器的吞吐量比Log4j 1.x 和Logback高18倍,延迟低。
- 自动重新加载配置:与Logback一样,Log4j2可以在修改时自动重新加载其配置。与Logback不同,它会在重新配置发生时不会丢失日志事件。
- 高级过滤:与Logback一样,Log4j2支持基于Log事件中的上下文数据,标记,正则表达式和其他组件进行过滤。此外,过滤器还可以与记录器进行关联。与Logback不同,Log4j2可以在任何这些情况下使用通用的Filter类。
- 插件架构:Log4j2使用插件模式配置组件。因此,无需编写代码来创建和配置Appender,Layout,Pattern Converter等。在配置了的情况下,Log4j自动识别插件并使用它们。
- 无垃圾机制:在稳态日志记录期间,Log4j2在独立应用程序中是无垃圾的,在Web应用程序中是低垃圾。这减少了垃圾收集器的压力,并且可以提供过更好的相应性能。
二、快速入门
首先,我们需要导入Log4j2的依赖
<!-- log4j2日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.23.1</version>
</dependency>
<!-- log4j2日志实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
Log4j2和Log4j提供了相同的日志级别输出,默认为Error级别的打印。快速入门的示例代码打印结果如下图所示:
三、SLF4j + Log4j2组合
为了实现SLF4j + Log4j2的组合,我们需要导入SLF4J日志门面的核心依赖以及桥接器,具体如下:
<!-- slf4j日志门面核心依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- 适配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<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>
在这里,使用适配器后,slf4j门面调用的实际上是log4j2的门面,再由log4j2门面调用log4j2的实现。 测试用例的打印效果如下图所示:
四、配置文件
Log4j2是参考Logback创作出来的,所以配置文件也是使用xml,Log4j2同样也是加载类路径(resources)下的log4j2.xml文件中的配置。下面是一个简单的控制台输出配置:
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration>
<Appenders>
<Console name="consoleAppender" target="SYSTEM_ERR">
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="consoleAppender" />
</Root>
</Loggers>
</Configuration>
根标签,所有日志相关信息,都是在根标签中进行配置,<Configuration> status="debug" ></Configuration>。在根标签中,可以加属性status="debug",表示日志框架本身的日志输出级别。monitorInterval="数值"表示自动加载配置文件的间隔时间。
此外,我们可能需要将日志输出到文件中,或者是根据文件的时间或者大小进行一个滚动,下面是对文件输出的相关配置示例:
<properties>
<property name="logDir">log</property>
</properties>
<Appenders>
<!-- 配置文件输出 -->
<File name="fileAppender" fileName="${logDir}/log4j2.log">
<PatternLayout pattern="%m%n"/>
</File>
<!--
$${date:yyyy-MM-dd}:根据日期当天,创建一个文件夹
rolling-%d{yyyy-MM-dd}-%i.log:%i表示序号,从0开始,目的是为了让每一份文件名不会重复
-->
<RollingFile name="rollingFile" fileName="${logDir}/rolling.log"
filePattern="${logDir}/$${date:yyyy-MM-dd}/rolling-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="%m%n"/>
<Policies>
<!-- 在系统启动时,触发拆分规则,产生一个日志文件 -->
<OnStartupTriggeringPolicy />
<!-- 按照文件的大小进行拆分 -->
<SizeBasedTriggeringPolicy size="10KB" />
<!-- 按时间节点进行拆分,拆分规则就是filePattern -->
<TimeBasedTriggeringPolicy />
</Policies>
<!-- 在同一目录下,文件的个数限制,如果超出了设置的数值,则根据时间进行覆盖,新的覆盖旧的规则 -->
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
有关文件输出等相关配置,更多的信息可以参考下面这篇博客:
五、异步日志
异步日志是Log4j2最大的特色,其性能的提升主要也是从异步日志中受益。Log4j2提供了两种是心啊异步日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应前面我们说的Appender组件和Logger组件。注意这是两种不同的实现方式,在设计和源码上都是不同的体现。
1. AsyncAppender方式
是通过引用别的Appender来是实现的,当有日志事件到达时,会开启另外一个线程来处理它们,需要注意的是,如果在Appender的时候出现异常,对应用来说是无法感知的。AsyncAppender应该在它引用的Appender之后配置,默认使用java.util.concurrent.ArrayBlockQueue实现而不需要其他外部的类库。当使用此Appender的时候,在多线程的环境下需要注意,阻塞队列容易受到锁争用的影响,这可能会对性能产生影响。这时候,我们应该考虑使用无锁的异步记录器(AsyncLogger)。
在使用AsyncAppender的时候,我们首先,需要导入异步日志的依赖:
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>4.0.0</version>
</dependency>
接下来,在我们的Appender标签中,对我们的异步进行一个配置,下面是一个配置的示例:
<Appenders>
<!-- 配置控制台输出 -->
<Console name="consoleAppender" target="SYSTEM_ERR">
</Console>
<!-- 配置异步日志 -->
<Async name="myAsync">
<!-- 将控制台的输出做异步的操作 -->
<AppenderRef ref="consoleAppender" />
</Async>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="myAsync" />
</Root>
</Loggers>
2. AsyncLogger方式
AsyncLogger才是Log4j2实现异步最重要的功能体现,也是官方推荐的异步方式。它可以使用调用Logger.log返回的更快。你可以有两种选择:全局异步和混合异步。
全局异步:所有的日志都异步的记录,在配置文件上不用做任何改动,只需要在resources目录下添加log4j2.component.properties文件,配置如下一行参数即可。
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
混合异步:你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。虽然Log4j2提供以一套异常处理机制,可以覆盖大部分的状态,但是还有一小部分的特殊情况是无法完全处理的,比如我们如果是记录审计日志(特殊情况之一),那么官方就推荐使用同步日志的方式,而对于其他的一些仅仅是记录一个程序日志的地方,使用异步日志将大幅提升性能,减少对应用本身的影响。混合异步的方式需要通过修改配置文件来实现,使用AsyncLogger标记配置。混合异步的配置示例:
<Loggers>
<!--
includeLocation="false"
表示去除日志记录中的行号信息,这个行号信息非常的影响日志记录的效率,
严重的时候可能记录的比同步的日志效率还要低。
additivity="false"
表示不继承rootLogger
-->
<!-- 自定义Logger,让自定义的Logger为异步Logger -->
<AsyncLogger name="cn.sumou" level="trace"
includeLocation="false" additivity="false">
<AppenderRef ref="consoleAppender" />
</AsyncLogger>
<Root level="trace">
<AppenderRef ref="consoleAppender" />
</Root>
</Loggers>
有关不同的异步日志的性能分析,可以参考这篇博客:log4j2性能分析+原理_log4j2异步日志原理-CSDN博客
总结
我们展示了如何快速开始使用 Log4j 2,以及如何与 SLF4J 结合使用以实现更灵活的日志管理。此外,文章说明了如何配置日志输出至文件及实现日志滚动。最后,我们探讨了 Log4j 2 中异步日志的不同实现方式,包括 AsyncAppender
和 AsyncLogger。
总之,Apache Log4j 2 是一个强大且高效的日志框架,适合不同规模的应用程序。