06、log4j2(日志实现框架)(二)

简介: 06、log4j2(日志实现框架)(二)

四、异步日志


4.1、介绍异步日志


简单介绍

官网(Log4j2异步日志):https://logging.apache.org/log4j/2.x/manual/async.html


log4j2最大的特点就是异步日志,通过使用异步日志能够大大提升性能!


log4j2中提供了两种方式使用异步日志:AsyncAppender方式以及AsyncLogger方式。


其中AsyncLogger可以使用全局异步也可以是混合异步。


同步与异步区别

看下同步日志与异步日志之间的区别:


同步日志:



异步日志:



区别介绍:


对于同步日志操作,先进行Logger中的操作并传输到LogEvent对象中接着再执行Appender操作进行日志输出,主线程会将这些步骤都执行完才算作一条日志结束。

对于异步日志操作,我们可以看到主线程当执行完Logger操作产生出LogEvent对象会放置到一个队列后就结束返回,对应在队列中的LogEvent会交由线程2操作,大大提升了效率。


性能比对

我们再看下官网提供的一个性能图比对图:



主要看64threads的性能表现对比特别突出:


第一个是使用AsyncLogger方式的全局异步,性能最高。

第二个是使用AsyncLogger方式的混合异步,性能会稍差全局异步。

第三个是使用AsyncAppender方式性能与Log4j、Logback都差不多了。


4.2、依赖jar包


引入对应依赖jar包即可使用log4j2的异步日志:


<!--    log4j2并发编程依赖包   -->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>


若是日志使用的是slf4j+log4j2,并使用log4j2的异步日志,直接引入如下依赖


 

<!--使用slf4j作为日志的门面,使用log4j2来记录日志 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.30</version>
    </dependency>
    <!--为slf4j绑定日志实现 log4j2的适配器 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.10.0</version>
    </dependency>
    <!--    Log4j2 门面API    -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.13.3</version>
    </dependency>
    <!--    Log4j2的日志实现   -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.11.1</version>
    </dependency>
    <!--    log4j2并发编程依赖包   -->
    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.3.4</version>
    </dependency>
</dependencies>



4.3、异步日志的实际使用(3种)


①AsyncAppender方式

使用AsyncAppender是设置指定的一个appender为异步方式,如下:


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" monitorInterval="5">
    <properties>
        <property name="LOG_HOME">D:/logs</property>
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
        </Console>
        <!--   ①AsyncAppender方式 引用配置的appender    -->
        <Async name="Async">
            <AppenderRef ref="Console"/>
        </Async>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <!--   ②引用name为Async的AsyncAppender方式    -->
            <AppenderRef ref="Async" />
        </Root>
    </Loggers>
</Configuration>


在Appenders标签中使用Async标签(引用想要进行异步日志的appender),最后将下面对应的RootLogger引用该异步Appender即可。

这种方式不太建议使用,因为其效率与logback差不多,并且日志输出时不显示行号。



②AsyncLogger方式

介绍


AsyncLogger才是log4j2的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log返回的 更快。你可以有两种选择:全局异步和混合异步。


全局异步则是让所有的日志都是异步的操作。

混合异步的话能够指定某个Appender进行异步,某个进行同步,更加灵活。


全局异步

全局异步就是所有的日志都是异步的操作,如何才能让所有的日志进行异步操作呢?我们只需要额外添加一个配置文件log4j2.component.properties并将其放置到resource目录下(Maven项目),配置内容如下:


Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector



之后我们运行项目默认所有的日志都是异步的操作,需要注意打印的日志消息没有行号!!!



混合异步(含自定义logger)

在原本log4j2.xml配置文件中自定义logger对象设置为异步(不需要添加全局异步的配置文件)


log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--日志框架本身日志等级debug  可以打印配置信息-->
<Configuration status="debug" monitorInterval="5">
    <properties>
        <property name="LOG_HOME">D:/logs</property>
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
        </Console>
    </Appenders>
    <Loggers>
        <!--***************************************************-->
        <!--   自定义Logger对象(异步)
               includeLocation="false" 表示关闭日志记录的行号信息
               additivity="false"  不继承rootLogger对象
        -->
        <AsyncLogger name="xyz.changlu" level="trace"
                     includeLocation="false" additivity="false">
            <AppenderRef ref="file"/>
        </AsyncLogger>
        <!--***************************************************-->
        <Root level="trace">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>



在<Loggers>标签中定义了一个AsyncLogger标签表示异步Logger,并且指定引用Appender。

这里配置RootLogger是同步的,自定义Logger是异步。


测试程序:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2Test {
    //获取Logger实例
    public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class);
    public static void main(String[] args) {
        System.out.println(LOGGER.getName());//xyz.changlu.LogTest
        //1、打印日志记录
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
        //2、占位符输出
        String name = "changlu";
        int age = 20;
        LOGGER.info("报错,name:{},age:{}",name,age);
        //3、打印堆栈信息
        try {
            int i = 5/0;
        }catch (Exception e){
            LOGGER.error("报错",e);
        }
    }
}



使用这种方式的日志打印没有行号输出!!!


4.4、注意点


使用异步日志时的注意点如下:


使用异步日志时,打印的日志信息不会显示行号!

如果使用异步日志,AsyncAppender、AsyncLogger和全局日志,不要同时出现。否则性能会和 AsyncAppender一致,降至最低。

设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。


五、Log4j2的无垃圾模式介绍


垃圾收集暂停是延迟峰值的常见原因,并且对于许多系统而言,则需要花费更多大量精力来控制这些暂停。


许多日志库(包括以前版本的Log4j)在稳态日志记录期间分配临时对象,如日志事件对象,字符串, 字符数组,字节数组等。


这会对垃圾收集器造成压力并增加GC暂停发生的频率。


从版本2.6开始,默认情况下Log4j以“无垃圾”模式运行,其中重用对象和缓冲区,并且尽可能不分配临时对象。还有一个“低垃圾”模式,它不是完全无垃圾,但不使用ThreadLocal字段。


Log4j 2.6中的无垃圾日志记录部分通过重用ThreadLocal字段中的对象来实现,部分通过在将文本转换为字节时重用缓冲区来实现。



看下面两个版本的测试比较


使用Log4j 2.5:内存分配速率809 MB /秒,141个无效集合:



Log4j 2.6没有分配临时对象:0(零)垃圾回收:



通过使用无垃圾模式,我们可以看到GC收集数为0,并且每秒分配率为1.58MB/s。



避免创建临时对象机制的两个属性


有两个单独的系统属性可用于手动控制Log4j用于避免创建临时对象的机制:


log4j2.enableThreadlocals - 如果“true”(非Web应用程序的默认值)对象存储在ThreadLocal字段中并重新使用,否则将为每个日志事件创建新对象。

log4j2.enableDirectEncoders - 如果将“true”(默认)日志事件转换为文本,则将此文本转换 为字节而不创建临时对象。注意: 由于共享缓冲区上的同步,在此模式下多线程应用程序的同步日志记录性能可能更差。如果您的应用程序是多线程的并且日志记录性能很重要,请考虑使用异步记录器。


总结


1、Log4j2有6个日志等级,默认日志等级为error。—见第1部分


2、可设置定义配置文件log4j2.xml,包含了各个常使用的Appender以及RootLogger配置。—见第2部分


3、Log4j2也有日志门面,不过建议使用slf4j日志门面来对日志实现框架进行同一管理。—见第3部分


4、Log4j2也推出异步日志,其包含AsyncAppender方式、AsyncLogger方式((全局异步、混合异步))两种方式配置,其中全局异步日志性能最好大约是Logback的18倍,其次就是混合异步,性能最差的是AsyncAppender方式与Logback性能几乎差不多。使用异步日志需要注意的是其无法输出方法中的行号信息!—见4部分


5、在Log4j2的2.6版本之后使用无垃圾模式,通过重用ThreadLocal字段中的对象以及文本转换为字节时重用缓冲区来大大减少内存的使用。—见第5部分

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
监控 容灾 算法
阿里云 SLS 多云日志接入最佳实践:链路、成本与高可用性优化
本文探讨了如何高效、经济且可靠地将海外应用与基础设施日志统一采集至阿里云日志服务(SLS),解决全球化业务扩展中的关键挑战。重点介绍了高性能日志采集Agent(iLogtail/LoongCollector)在海外场景的应用,推荐使用LoongCollector以获得更优的稳定性和网络容错能力。同时分析了多种网络接入方案,包括公网直连、全球加速优化、阿里云内网及专线/CEN/VPN接入等,并提供了成本优化策略和多目标发送配置指导,帮助企业构建稳定、低成本、高可用的全球日志系统。
1267 55
|
存储 缓存 关系型数据库
图解MySQL【日志】——Redo Log
Redo Log(重做日志)是数据库中用于记录数据页修改的物理日志,确保事务的持久性和一致性。其主要作用包括崩溃恢复、提高性能和保证事务一致性。Redo Log 通过先写日志的方式,在内存中缓存修改操作,并在适当时候刷入磁盘,减少随机写入带来的性能损耗。WAL(Write-Ahead Logging)技术的核心思想是先将修改操作记录到日志文件中,再择机写入磁盘,从而实现高效且安全的数据持久化。Redo Log 的持久化过程涉及 Redo Log Buffer 和不同刷盘时机的控制参数(如 `innodb_flush_log_at_trx_commit`),以平衡性能与数据安全性。
921 5
图解MySQL【日志】——Redo Log
|
缓存 Java 编译器
|
监控 Java 应用服务中间件
Tomcat log日志解析
理解和解析Tomcat日志文件对于诊断和解决Web应用中的问题至关重要。通过分析 `catalina.out`、`localhost.log`、`localhost_access_log.*.txt`、`manager.log`和 `host-manager.log`等日志文件,可以快速定位和解决问题,确保Tomcat服务器的稳定运行。掌握这些日志解析技巧,可以显著提高运维和开发效率。
1729 13
|
开发框架 运维 监控
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
673 5
|
SQL 存储 关系型数据库
简单聊聊MySQL的三大日志(Redo Log、Binlog和Undo Log)各有什么区别
在MySQL数据库管理中,理解Redo Log(重做日志)、Binlog(二进制日志)和Undo Log(回滚日志)至关重要。Redo Log确保数据持久性和崩溃恢复;Binlog用于主从复制和数据恢复,记录逻辑操作;Undo Log支持事务的原子性和隔离性,实现回滚与MVCC。三者协同工作,保障事务ACID特性。文章还详细解析了日志写入流程及可能的异常情况,帮助深入理解数据库日志机制。
1925 0
|
存储 关系型数据库 MySQL
图解MySQL【日志】——Undo Log
Undo Log(回滚日志)是 MySQL 中用于实现事务原子性和一致性的关键机制。在默认的自动提交模式下,MySQL 隐式开启事务,每条增删改语句都会记录到 Undo Log 中。其主要作用包括:
799 0
|
监控 Kubernetes Shell
使用阿里云日志服务采集查询kubernetes容器日志
使用阿里云日志收集查询kubernetes容器日志
3222 0