log4j2的日志框架(详细,springboot和异步日志的实现)

目录

log4j2的介绍

Log4j2的性能

SpringBoot中的使用Log4j2

log4j2的进阶--异步日志

AsyncAppender方式

AsyncLogger方式


log4j2的介绍

Apache Log4j 2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带 来了一些重大的提升,主要有:

  • 异常处理,在 logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异 常处理机制。
  • 性能提升, log4j2相较于log4j 和logback都具有很明显的性能提升,后面会有官方测试的数据。
  • 自动重载配置,参考了 logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产 上可以动态的修改日志的级别而不需要重启应用。
  • 无垃圾机制, log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集 导致的jvm gc。

官网地址: https://logging.apache.org/log4j/2.x/

Log4j2的性能

Log4j2最牛的地方在于异步输出日志时的性能表现,Log4j2在多线程的环境下吞吐量与Log4j和 Logback的比较如下图。下图比较中Log4j2有三种模式:1)全局使用异步模式;2)部分Logger采用异步模式;3)异步Appender。可以看出在前两种模式下,Log4j2的性能较之Log4j和Logback有很大的 优势。

无垃圾记录

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

许多日志库(包括以前版本的Log4j)在稳态日志记录期间分配临时对象,如日志事件对象,字符串, 字符数组,字节数组等。这会对垃圾收集器造成压力并增加GC暂停发生的频率。

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

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

从图中可以看出log4j2的性能是完全吊打其他日志框架的,而且log4j2的异步日志功能非常的强大,可以大大的减少日志系统对业务系统的负担。基于这些功能,所以现在主流的日志开发架构就是SLF4J+Log4j2这个组合,所以这个log4j2这个日志框架是必须要学会的

SpringBoot中的使用Log4j2

springboot框架在企业中的使用越来越普遍,springboot日志也是开发中常用的日志系统。springboot 默认就是使用SLF4J作为日志门面,logback作为日志实现来记录日志。所以如果我们要在springboot项目中使用Log4j2,需要内置的日志框架给去除。

分成三步,第一移除默认的日志框架,第二加入log4j2的依赖(启动器),第三编写配置文件

  • 移除默认的日志框架
复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
     <!--排除默认spring-boot-starter-logging启动器-->
    <exclusions>
        <exclusion>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
     </exclusions>
</dependency>
  • 加入log4j2的依赖
复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

编写log4j2.xml配置文件'

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <!-- 定义全局变量,日志文件路径和格式 -->
    <Properties>
        <Property name="log.path">./dev</Property>
        <Property name="log.name">forlan-log4j2</Property>
        <Property name="file.pattern">%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{50} - %msg%n</Property>
    </Properties>

    <!-- 控制台输出配置 -->
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="${file.pattern}"/>
            <ThresholdFilter level="DEBUG"/>
        </Console>

        <!-- 文件输出配置 -->
        <RollingFile name="INFO_FILE" fileName="${log.path}/${log.name}.info.log"
                     filePattern="${log.path}/%d{yyyy-MM-dd}/${log.name}.info.%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="${file.pattern}"/>
            <LevelMatchFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 每个文件最大10MB -->
            <SizeBasedTriggeringPolicy size="10MB"/>
            <!-- 最多保留30天的历史记录 -->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!-- 文件输出配置 -->
        <RollingFile name="ERROR_FILE" fileName="${log.path}/${log.name}.error.log"
                     filePattern="${log.path}/%d{yyyy-MM-dd}/${log.name}.error.%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="${file.pattern}"/>
            <LevelMatchFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 每个文件最大10MB -->
            <SizeBasedTriggeringPolicy size="10MB"/>
            <!-- 最多保留30天的历史记录 -->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>

    <!-- 设置root logger -->
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="INFO_FILE"/>
            <AppenderRef ref="ERROR_FILE"/>
        </Root>
    </Loggers>
</Configuration>
  • 运行测试
复制代码
@SpringBootTest
public class Slf4jTest {
    // 声明日志对象
    public final static Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
    @Test
    public void testQuick() throws Exception {
        //打印日志信息
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
        // 使用占位符输出日志信息
        String name = "jack";
        Integer age = 18;
        LOGGER.info("用户:{},{}", name, age);
        // 将系统异常信息写入日志
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            // e.printStackTrace();
            LOGGER.info("出现异常:", e);
        }
    }
}

出现入上图的结果就代表log4j2就配置成功了,主要注意那个dev的日志文件是否生成,这个日志文件是在前面的配置文件中配置的。

到这里log4j2的基本使用其实已经可以实现了,对于一些小的项目,这样子配置就可以了,但对于那些大型项目,log4j2还可以更加强大,那就是使用它的异步日志功能

log4j2的进阶--异步日志

logl4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用 log4j2的异步日志。

Log4j2 提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应 前面我们说的Appender组件和Logger组件。

log4j2的全局异步AsyncLogger性能最好,第二个是混合异步AsyncLogger,性能最差的是AsyncAppender(和同步日志相比没有什么性能提升。和logback性能一样)

如果使用异步日志,全局异步AsyncLogger、混合异步AsyncLogger、AsyncAppender,不要同时使用。否则会使用性能较低的一种异步方式

注意:配置异步日志需要添加依赖

<!--异步日志依赖-->

<dependency>

<groupId>com.lmax</groupId>

<artifactId>disruptor</artifactId>

<version>3.3.4</version>

</dependency>

AsyncAppender方式

**AsyncAppender:**这种使用方式较为简单,只需要在我们上述的Appender中加入以下标签即可:

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">

    <properties>
        <property name="LOG_HOME">C:/Users/dell/Desktop/java11Test/logs</property>
    </properties>



    <Appenders>
        <File name="File" fileName="${LOG_HOME}/myFile.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %l --- %msg%n" />
        </File>

        <!-- AsyncAppender引用appender -->
        <Async name="Async">
            <AppenderRef ref="File"/>
        </Async>
    </Appenders>



    <Loggers>
        <Root level="INFO">
            <!-- 直接引用AsyncAppender -->
            <AppenderRef ref="Async" />
        </Root>
    </Loggers>


</Configuration>
AsyncLogger方式

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

全局异步 就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个 log4j2.component.properties 配置;

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

混合异步就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加 灵活。

XML 复制代码
    <!--logger定义-->
    <Loggers>
        <!--自定义异步logger对象
        includeLocation = "false" 关闭日志记录行好信息
        additivity = "false" 不再继承rootlogger对象-->
        <AsyncLogger name = "com.itcats" level = "trace" includeLocation = "false" additivity = "false">
            <AppenderRef ref="Console"/>
        </AsyncLogger>
 
        <Root level = "trace">
            <AppenderRef ref  ="Console"/>
        </Root>
    </Loggers>

将以上配置配置到我们之前的log4j2.xml配置文件中,将之前的logger定义替换。此时我们com.itcats日志是异步的,root日志是同步的。

使用异步日志需要注意的问题:

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

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

到这里我们的java日志框架基本就介绍完了

相关推荐
ACGkaka_1 天前
Spring Boot实战(三十六)编写单元测试
spring boot·单元测试·log4j
forestsea13 天前
分布式日志治理:Log4j2自定义Appender写日志到RocketMQ
java·log4j·java-rocketmq
遥不可及~~斌15 天前
Spring Boot 项目日志系统全攻略:Logback、Log4j2、Log4j与SLF4J整合指南
spring boot·log4j·logback
weixin_4383354015 天前
SpringBoot依赖冲突引发的 log4j 日志打印问题及解决方法
spring boot·单元测试·log4j
我是坑货16 天前
maven的项目管理和构建生命周期
java·log4j·maven
凭君语未可25 天前
详解Maven的主要生命周期
java·log4j·maven
WIN赢25 天前
单元测试的编写
单元测试·log4j
zerohawk1 个月前
【log4j】配置Slf4j
junit·单元测试·log4j
熬了夜的程序员1 个月前
Go 语言封装邮件发送功能
开发语言·后端·golang·log4j
故事与他6451 个月前
Apache中间件漏洞攻略
java·服务器·安全·网络安全·中间件·log4j·apache