loong - Log4j2 - 配置

loong - Log4j2 - 配置

该文章配置基于springBoot,并添加关于异步日志配置和关于控制台报错问题处理

日志介绍

  • 同步日志
  • 混合同步和异步日志
  • 异步日志(性能最好,推荐使用)异步日志情况下,增加 Disruptor 队列长度并配置队列堵塞丢弃策略从可以增加高并发下的性能,实现如下:
    1. jvm 参数:-DLog4jAsyncQueueFullPolicy=Discard -DLog4j2.asyncLoggerRingBufferSize:指定队列的长度(根据实际压测情况调试,一般不会指定长度)
    2. 或者在log4j2.component.properties中配置丢弃策略:
      • log4j2.asyncLoggerRingBufferSize=根据实际压测情况调试

同步日志

步骤

  1. 添加依赖
  2. 解决依赖冲突
  3. 添加配置文件
  4. 激活配置文件

添加依赖

        <!-- log4j2日志框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <!-- log4j2 异步日志需要引入这个jar包 -->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>

解决依赖冲突

springboot 默认引入spring-boot-starter-logging与我们引入的spring-boot-starter-log4j2的依赖冲突了,需要我手动排除(exclusion)spring-boot-starter-logging依赖

# 这个报错说发现了多个slf4j绑定
SLF4J: Class path contains multiple SLF4J bindings.
# 一个绑定是 slf4j 和 logback-classic
SLF4J: Found binding in [jar:file:/xxx/ch/qos/logback/logback-classic/1.2.11/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
# 还有一个# 一个绑定是 slf4j 和 log4j-slf4j-impl
SLF4J: Found binding in [jar:file:/xxxx/org/apache/logging/log4j/log4j-slf4j-impl/2.17.2/log4j-slf4j-impl-2.17.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
# 生效的binding是logback.classic
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

解决方案

        <!--核心启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <!--如果要使用log4j2,就需要把这个排除,不然控制台会报错-->
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

创建配置文件

在resources文件下创建log4j2.xml文件

配置文件加载顺序

Log4j 包含 4 种 ConfigurationFactory 的实现,分别适用于 JSON、YAML、properties 和 XML 配置文件。在 Log4j 启动时可以按照以下顺序自动加载配置文件:

  1. 查找 log4j.configurationFile 系统属性所指定的配置文件名,如果该系统属性值存在,就尝试使用相应文件扩展名的 ConfigurationFactory 来加载指定的配置文件。通过在代码中调用 System.setProperties("log4j.configurationFile","FILE_PATH") 或者将 -Dlog4jconfigurationFile=file://C:/configuration.xml 参数传递给 JVM;
  2. 如果没有找到,则 properties ConfigurationFactory 就在 classpath 中寻找 log4j2-test.properties 配置文件;
  3. 如果没有找到,则 YAML ConfigurationFactory 就在 classpath 中寻找 log4j2-test.yaml 或 log4j2-test.yml 配置文件;
  4. 如果没有找到,则 JSON ConfigurationFactory 就在 classpath 中寻找 log4j2-test.json 或 log4j2-test.jsn 配置文件;
  5. 如果没有找到,则 XML ConfigurationFactory 就在 classpath 中寻找log4j2-test.xml 配置文件;
  6. 如果没有找到测试配置文件,则 properties ConfigurationFactory 就在 classpath 中寻找 log4j2.properties 配置文件;
  7. 如果没有找到,则 YAML ConfigurationFactory 就在 classpath 中寻找 log4j2.yaml 或 log4j2.yml 配置文件;
  8. 如果没有找到,则 JSON ConfigurationFactory 就在 classpath 中寻找 log4j2.json 或 log4j2.jsn 配置文件;
  9. 如果没有找到,则 XML ConfigurationFactory 就在 classpath 中寻找log4j2.xml 配置文件;
  10. 如果上面的配置文件都没有找到,就使用默认的 DefaultConfiguration 配置。

log4j2.xml配置内容

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="30">

    <!--
       - %d{yyyy-MM-dd HH:mm:ss.SSS} : 日志生成时间,输出格式为“年-月-日 时:分:秒.毫秒”
       - %p                         : 日志输出格式
       - %c                         : logger的名称
       - %T %tid %threadId          : 线程号 非常有必要
       - %t %tn %thread %threadName : 输出日志的线程名称,类似于线程号作用相同
       - %L %line                   : 显示日志输出的代码所在的行数,输出时会检查堆栈信息(耗时操作**)
       - %M %method                 : 输出方法名 输出时会检查堆栈信息(耗时操作**)
       - %m %msg                    : 输出应用中自定义的日志内容,即 logger.info("message")
       - %xEx                       : 异常信息
       - %n                         : 换行符
       -->

    <!--properties:设置全局变量 -->
    <properties>
        <!--LOG_HOME:指定当前日志存放的目录 -->
        <property name="LOG_HOME">D:/loong-log</property>
        <property name="PROJECT_NAME">LOONG-TEST</property>
        <property name="LOG__NAME_TEMP">LOG-TEMP</property>
        <property name="LOG_NAME_INFO">LOG-INFO</property>
        <!--[年月日 时分秒.毫秒] [线程Id] [线程名称] [日志级别] [全限定类名] [日志具体内容] [异常信息]-->
        <property name="LOG_PATTERN">[%d{yyyy-MM-dd HH:mm:ss.SSS}] [threadId=%tid] [%threadName] [%-5level]  [%c] - %msg %xEx %n</property>
    </properties>

    <!-- appender:定义日志输出目的地,内容和格式等 -->
    <!--
        Appender: 定义日志输出的目的地,内容,格式
            -ConsoleAppender	    将日志消息输出到控制台(用到了)
            -FileAppender	        将日志消息输出到文件(用到了)
            -RollingFileAppender	将日志消息输出到文件,并支持日志轮换(用到了)
            -SocketAppender	        将日志消息发送到远程服务器的 Socket 端口
            -JMSAppender	        将日志消息发送到 JMS 队列或主题
            -SMTPAppender	        将日志作为电子邮件发送
            -SyslogAppender	        将日志输出到 Syslog 服务器
            -JDBCAppender	        将日志写入数据库
            -KafkaAppender	        将日志消息发送到 Kafka 主题
        -->
    <appenders>

        <!--target="SYSTEM_OUT" 通过system.out输出的东西 encoding:日志编码 immediateFlush:是否立即刷新到控制台(默认true)-->
        <console name="consoleAppender" target="SYSTEM_OUT" immediateFlush="false">
            <!--输出日志的格式 ${LOG_PATTERN} -->
            <PatternLayout pattern="${LOG_PATTERN}" />
        </console>

        <!--File RollingFile 差不多一般用不到 可以忽略(适合临时测试用)-->
        <!--File节点用来定义输出到指定位置的文件的appender name:指定Appender的名字 fileName:指定输出日志的目的文件带全路径的文件名  append="false" 是否在原有的日志文件中追加日志,如果为false则会清空-->
        <File name="file" fileName="${LOG_HOME}/${PROJECT_NAME}/${LOG__NAME_TEMP}.log" append="false">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </File>

        <!--RollingFile:日志输出到文件 name:定义这个RollingFile的名称 fileName:当前日志输出的文件名称-->
        <!--filePattern:归档日志文件的文件名模式 ${LOG_HOME}/${PROJECT_NAME}/$${date:yyyy-MM-dd}/${LOG_NAME_INFO}-%d{yyyy-MM-dd HH}-%i.log.gz: 文件路径/项目名称/日期(年月日)/日志名称-年月日 小时-第一个.log.zip文件  -->
        <RollingFile name="rollingFileInfo" fileName="${LOG_HOME}/${PROJECT_NAME}/${LOG_NAME_INFO}.log" filePattern="${LOG_HOME}/${PROJECT_NAME}/$${date:yyyy-MM-dd}/${LOG_NAME_INFO}-%d{yyyy-MM-dd-HH}-%i.log.zip">

            <!--PatternLayout:日志内容输出格式-->
            <PatternLayout pattern="${LOG_PATTERN}"/>

            <!--Policies:触发策略决定何时执行备份 -->
            <Policies>
                <!--基于时间的滚动策略: interval="1"默认'1';这个配置需要和filePattern结合使用,filePattern日期格式精确到哪一位,interval也精确到哪一个单位 例如:%d{yyyy-MM-dd HH}-%i.log.zip 最小单位是小时 则interval="1" 则代表1小时-->
                <!--modulate="true" 是否调整 interval 属性值,以便下次滚动发生在 interval 边界处-->
                <!--maxRandomDelay: 滚动操作随机延迟的最长秒数。默认 0 表示无延迟。该设置在有多个应用同时滚动日志的服务器上很有用,可以扩宽滚动日志的的负载时间范围,避免某一个时刻由于滚动日志造成高 I/O 压力。-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" maxRandomDelay="2"/>
                <!--基于指定文件大小的滚动策略:每个文件最大100M -->
                <SizeBasedTriggeringPolicy size="10M"/>
            </Policies>

            <filters>
                <!--level="INFO" 匹配时接受,不匹配时丢弃-->
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </filters>
        </RollingFile>

    </appenders>

    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>

        <!--root 节点会输出所有等级的日志-->
        <root level="INFO">
            <!--表示root节点接收的日志信息 会以'consoleAppender'设置好的格式输出,但是他没有输出到文件-->
            <appender-ref ref="consoleAppender"/>
        </root>

        <!--用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等-->
        <!--name:带包指定包下输出的日志 例如:name="com.loong.test" 表示输出com.loong.test包下的日志-->
        <!--level:表示日志输出的等级-->
        <!--additivity:是否继承root节点,默认是true继承。(开发环境为了方便看控制台设置为true)
            也就是说子Logger会在父Logger的appender里输出。若是additivity设为false,则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输出
            例如:additivity="false" 则 name="com.loong.test" 的日志内容只会输出到 appender-ref ref="rollingFileInfo"中,而不会输出root节点中的appender-ref ref="consoleAppender"-->
        <logger name="com.loong.test" level="INFO" additivity="true">
            <appender-ref ref="rollingFileInfo"/>
        </logger>

    </loggers>

</configuration>

激活log4j2.xml

# 激活日志文件
logging:
  config: classpath:log4j2.xml

至此log4j2同步日志就搭建好了


异步日志

步骤:

  1. 创建配置文件
  2. 设置select和丢弃策略

创建配置文件

在resource目录下创建log4j2.component.properties文件;配置好的log4j2.xml无需修改任何配置,并在文件中添加以下参数就可以了。本文介绍的异步日志是其实现的一种方式,若想使用可自行百度。

# 全局异步日志配置
log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
# 异步队列满后执行策略(丢弃)
log4j2.asyncQueueFullPolicy=Discard
# 日志丢弃阈值(ERROR级别)
log4j2.discardThreshold=ERROR

拓展

异步日志原理

在这里插入图片描述

配置信息
配置名称配置默认值描述
RingBuffer槽位数量log4j2.asyncLoggerConfigRingBufferSize(同步&异步混合使用)256 * 1024 (garbage-free模式下默认为4*1024)RingBuffer槽位数量,最小值为128,在首次使用的时候进行分配,并固定大小
异步日志等待策略log4j2.asyncLoggerConfigWaitStrategy(同步&异步混合使用)Timeout (建议设置为Sleep)Block:I/O线程使用锁和条件变量等待可用的日志事件,建议在CPU资源重要程度大于系统吞吐和延迟的情况下使用该种策略; Timeout:是Block策略的一个变体,可以确保如果线程没有被及时唤醒(awit())的话,线程也不会卡在那里,可以以一个较小的延迟(默认10ms)恢复; Sleep:是一种自旋策略,使用纳秒级别的自旋交叉执行Thread.yield()和LockSupport.parkNanos(),该种策略可以较好的平衡系统性能和CPU资源的使用率,并且对程序线程影响较小。 Yield:相较于Sleep省去了LockSupport.parkNanos(),而是不断执行Thread.yield()来让渡CPU使用权,但是会造成CPU一直处于较高负载,强烈不建议在生产环境使用该种策略
RingBuffer槽位数量log4j2.asyncLoggerRingBufferSize(纯异步)256 * 1024 (garbage-free模式下默认为4*1024)RingBuffer槽位数量,最小值为128(2的指数),在首次使用的时候进行分配,并固定大小
异步日志等待策略log4j2.asyncLoggerWaitStrategy(纯异步)Timeout (建议设置为Sleep)Block:I/O线程使用锁和条件变量等待可用的日志事件,建议在CPU资源重要程度大于系统吞吐和延迟的情况下使用该种策略; Timeout:是Block策略的一个变体,可以确保如果线程没有被及时唤醒(awit())的话,线程也不会卡在那里,可以以一个较小的延迟(默认10ms)恢复; Sleep:是一种自旋策略,使用纳秒级别的自旋交叉执行Thread.yield()和LockSupport.parkNanos(),该种策略可以较好的平衡系统性能和CPU资源的使用率,并且对程序线程影响较小。 Yield:相较于Sleep省去了LockSupport.parkNanos(),而是不断执行Thread.yield()来让渡CPU使用权,但是会造成CPU一直处于较高负载,强烈不建议在生产环境使用该种策略
异步队列满后执行策略log4j2.asyncQueueFullPolicyDefault (强烈建议设置为Discard)当Appender的日志消费速度跟不上日志的生产速度,且队列已满时,指定如何处理尚未正常打印的日志事件,默认为阻塞(Default),强烈建议配置为丢弃(Discard)
日志丢弃阈值log4j2.discardThresholdINFO (强烈建议设置为ERROR)当Appender的消费速度跟不上日志记录的速度,且队列已满时,若log4j2.asyncQueueFullPolicy为Discard,该配置用于指定丢弃的日志事件级别(小于等于),默认为INFO级别(即将INFO,DEBUG,TRACE日志事件丢弃掉), 强烈建议设置为ERROR(FATAL虽然有些极端,但是也可以)
异步日志超时时间log4j2.asyncLoggerSleepTimeNs100异步日志等待策略为Sleep时,线程睡眠时间(单位:ns)
异步日志重试次数log4j2.asyncLoggerRetries200异步日志等待策略为Sleep时,自旋重试次数
队列满时是否将对RingBuffer的访问转为同步AsyncLogger.SynchronizeEnqueueWhenQueueFulltrue当队列满时是否将对RingBuffer的访问转为同步,当Appender日志消费速度跟不上日志的生产速度,且队列已满时,通过限制对队列的访问,可以显著降低CPU资源的使用率,强烈建议使用该默认配置
配置方式
配置源优先级(值越低优先级越高)描述
Spring Boot Properties-100Spring Boot日志配置,需要有【log4j-spring】模块支持
System Properties0在类路径下添加log4j2.system.properties(推荐)
Environment Variables100使用环境变量进行日志配置,注意该系列配置均以LOG4J_作为前缀
log4j2.component.properties file200在类路径下添加log4j2.component.properties(其实是【System Properties】的一种配置方式,但是具有较低的优先级,一般用作默认配置)

以上内容均参考自Log4j2官方文档:Log4j – Configuring Log4j 2 (apache.org)

总结

  • 在日志可以丢弃的情况下,推荐使用log4j2.asyncQueueFullPolicy=Discardlog4j2.discardThreshold=ERROR的组合配置;
  • 不要在生产环境使用可以直接与中间件交互的的Appender,如KafkaAppender。此类Appender一般都会有ack机制,与直接打印到日志文件不同的地方便是需要进行网络交互,如果中间件性能出现问题或者网络出现抖动,那么同样也会造成日志阻塞(话分两头,如果必须要使用KafkaAppender,那么可以考虑将syncSend设置为【false】,该配置的作用是指定Appender是否需要等待Kafka Server的ack,但是需要注意的是该开关配置需要Log4j2版本为2.8+);
  • Appender的【immediateFlush】设置为【false】,使用批量日志写入的方式,避免频繁的执行写磁盘操作。
    此类Appender一般都会有ack机制,与直接打印到日志文件不同的地方便是需要进行网络交互,如果中间件性能出现问题或者网络出现抖动,那么同样也会造成日志阻塞(话分两头,如果必须要使用KafkaAppender,那么可以考虑将syncSend设置为【false】,该配置的作用是指定Appender是否需要等待Kafka Server的ack,但是需要注意的是该开关配置需要Log4j2版本为2.8+);
  • Appender的【immediateFlush】设置为【false】,使用批量日志写入的方式,避免频繁的执行写磁盘操作。
  • 上述三个配置可以保证在极端情况下日志的打印不会成为系统瓶颈,喂系统服下一颗“定心丸”。

参考文章:https://zhuanlan.zhihu.com/p/680317697

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

7-Moon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值