Log4j2-Kafka日志传输方案

懵懂的女人 提交于 2020-02-27 10:23:05

Log4j2-Kafka日志Kafka传输方案

今天我们来介绍一下Log4j2与Kafka的集成日志解决方案。log4j2记录的日志可以直接传输给kafka,然后可以再输入到elastic-search,或者使用其他方式处理。

 

日志组件简介

熟悉Java开发的都知道,我们的日志组件的演变过程是:log4j->logback->log4j2

这里我们先简单介绍一下日志组件

日志接口:slf4j

slf4j是针对不同的日志框架的统一的抽象接口,我们可以看一下官网的介绍:

The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks, such as java.util.logging, logback and log4j. SLF4J allows the end-user to plug in the desired logging framework at deployment time. Note that SLF4J-enabling your library/application implies the addition of only a single mandatory dependency, namely slf4j-api-2.0.0-alpha2-SNAPSHOT.jar.

 

常用的日志组件

我们常用的日志组件有log4jlogbacklog4j2这三种,做过研究的同学可能会清楚这三者的关系。这三者都是同一个作者完成的。

log4j2是基于log4j和logback的一些问题开发出来的新的日志框架,引入了无锁异步等机制,使得性能大大增加。

我们看官网的介绍就可以明白:

Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture.

 

Kafka简介

Apache-Kafka是一个分布式的,基于发布/订阅的消息系统。Kafka的性能非常的强,什么场景下使用Kafka呢?

  • 系统解耦

  • 异步处理

  • 削峰

  • ...

 

Log4j2整合Kafka

这里我们用一个springboot的列子来看一下如何实现这个方法。

我们的springboot项目pom.xml里面要把logback给排除掉,然后引入log4j2

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

引入spring-kafka依赖

        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

接下来就是log4j2的配置,在resources目录下面创建一个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="1800">
    <Properties>
        <!-- 日志默认存放的位置,这里设置为项目根路径下,也可指定绝对路径 -->
        <!-- ${web:rootDir}是web项目根路径,java项目没有这个变量,需要删掉,否则会报异常 -->
        <!--<property name="basePath">D://log4j2Logs</property>-->
        <property name="basePath">logs</property>

        <!-- 控制台默认输出格式,"%-5level":日志级别,"%l":输出完整的错误位置,是小写的L,因为有行号显示,所以影响日志输出的性能 -->
        <property name="console_log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %l - %m%n</property>
        <!-- 日志文件默认输出格式,不带行号输出(行号显示会影响日志输出性能);%C:大写,类名;%M:方法名;%m:错误信息;%n:换行 -->
        <property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %C.%M - %m%n</property>

        <!-- 日志默认切割的最小单位 -->
        <property name="every_file_size">20MB</property>
        <!-- 日志默认输出级别 -->
        <property name="output_log_level">DEBUG</property>

        <!-- 日志默认存放路径(所有级别日志) -->
        <property name="rolling_fileName">${basePath}/all.log</property>
        <!-- 日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,作为存档 -->
        <property name="rolling_filePattern">${basePath}/%d{yyyy-MM}/all-%d{yyyy-MM-dd}-%i.log.gz</property>
        <!-- 日志默认同类型日志,同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
        <property name="rolling_max">50</property>

        <!-- Info日志默认存放路径(Info级别日志) -->
        <property name="info_fileName">${basePath}/info.log</property>
        <!-- Info日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,作为存档 -->
        <property name="info_filePattern">${basePath}/%d{yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz</property>
        <!-- Info日志默认同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
        <property name="info_max">10</property>

        <!-- Warn日志默认存放路径(Warn级别日志) -->
        <property name="warn_fileName">${basePath}/warn.log</property>
        <!-- Warn日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,作为存档 -->
        <property name="warn_filePattern">${basePath}/%d{yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz</property>
        <!-- Warn日志默认同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
        <property name="warn_max">10</property>

        <!-- Error日志默认存放路径(Error级别日志) -->
        <property name="error_fileName">${basePath}/error.log</property>
        <!-- Error日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,作为存档 -->
        <property name="error_filePattern">${basePath}/%d{yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz</property>
        <!-- Error日志默认同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
        <property name="error_max">10</property>

        <!-- 控制台显示的日志最低级别 -->
        <property name="console_print_level">DEBUG</property>

    </Properties>

    <!--定义appender -->
    <appenders>
        <!-- 用来定义输出到控制台的配置 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!-- 设置控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="${console_print_level}" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 设置输出格式,不设置默认为:%m%n -->
            <PatternLayout pattern="${console_log_pattern}"/>
        </Console>

        <!-- 打印root中指定的level级别以上的日志到文件 -->
        <RollingFile name="RollingFile" fileName="${rolling_fileName}" filePattern="${rolling_filePattern}">
            <PatternLayout pattern="${log_pattern}"/>
            <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            <!-- 设置同类型日志,同一文件夹下可以存放的数量,如果不设置此属性则默认存放7个文件 -->
            <DefaultRolloverStrategy max="${rolling_max}" />
            <!-- 匹配INFO以及以上级别 -->
            <Filters>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingFile>

        <!-- 打印INFO级别的日志到文件 -->
        <RollingFile name="InfoFile" fileName="${info_fileName}" filePattern="${info_filePattern}">
            <PatternLayout pattern="${log_pattern}"/>
            <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            <DefaultRolloverStrategy max="${info_max}" />
            <!-- 匹配INFO级别 -->
            <Filters>
                <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingFile>

        <!-- 打印WARN级别的日志到文件 -->
        <RollingFile name="WarnFile" fileName="${warn_fileName}" filePattern="${warn_filePattern}">
            <PatternLayout pattern="${log_pattern}"/>
            <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            <DefaultRolloverStrategy max="${warn_max}" />
            <!-- 匹配WARN级别 -->
            <Filters>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingFile>

        <!-- 打印ERROR级别的日志到文件 -->
        <RollingFile name="ErrorFile" fileName="${error_fileName}" filePattern="${error_filePattern}">
            <PatternLayout pattern="${log_pattern}"/>
            <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            <DefaultRolloverStrategy max="${error_max}" />
            <!-- 匹配ERROR级别 -->
            <Filters>
                <ThresholdFilter level="FATAL" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingFile>

        <Kafka name="Kafka" topic="log-test" ignoreExceptions="false">
            <PatternLayout pattern="[%-4level]_|_%d{YYYY-MM-dd HH:mm:ss}_|_%m_|_${sys:ip}"/>
            <Property name="bootstrap.servers">192.168.254.202:9092</Property>
            <Property name="max.block.ms">2000</Property>
        </Kafka>
        <RollingFile name="failoverKafkaLog" 
                     fileName="${basePath}/failoverKafka/request.log"
                     filePattern="${basePath}/failoverKafka/request.%d{yyyy-MM-dd}.log">
            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout>
                <Pattern>[%-4level]_|_%d{YYYY-MM-dd HH:mm:ss}_|_%m_|_${sys:ip}%n</Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy />
            </Policies>
        </RollingFile>
        <Failover name="Failover" primary="Kafka" retryIntervalSeconds="600">
            <Failovers>
                <AppenderRef ref="failoverKafkaLog"/>
            </Failovers>
        </Failover>

        <!--异步-->
        <Async name="kafkaLogger" level="INFO" additivity="false">
            <appender-ref ref="Failover"/>
        </Async>
        
    </appenders>

    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!-- 设置对打印sql语句的支持 -->
        <logger name="java.sql" level="debug" additivity="false">
            <appender-ref ref="Console"/>
        </logger>
        <!--建立一个默认的root的logger-->
        <root level="${output_log_level}">
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="Console"/>
            <appender-ref ref="InfoFile"/>
            <appender-ref ref="WarnFile"/>
            <appender-ref ref="ErrorFile"/>
            <AppenderRef ref="Kafka"/>
        </root>
        <Logger name="org.apache.kafka" level="INFO" /> <!-- avoid recursive logging -->
    </loggers>
</configuration>

log4j2已经有了Kafka的插件,在这个Appenders里面需要添加上KafkaAppender的配置就可以。

然后在application.yml加入配置:

logging:
  config: classpath:log4j2.xml

或者application.properties加入配置:

logging.config=classpath:log4j2.xml

到此配置完成。

 

需要注意的地方是:

  • Kafka日志接入要配置成异步的,这样性能才好。

  • Kafka appender ignoreExceptions 必须设置为false,否则无法触发Failover。

  • 响应要求比较高的系统接入第三方系统,必须依赖解耦,此处的Failover Appender就是解耦对Kafka的依赖,当Kafka Crash时,日志触发Failover,写本地即可。

  • 这里有个比较大的坑是max.block.ms Property,KafkaClient包里默认值是60000ms,当Kafka宕机时,尝试写Kafka需要1分钟才能返回Exception,之后才会触发Failover,当请求量大时,log4j2 队列很快就会打满,之后写日志就Blocking,严重影响到主服务响应。所以要设置足够短,队列长度足够长。

  • log4j2 Failover appender retryIntervalSeconds的默认值是1分钟,是通过异常来切换的,所以可以适量加大间隔,比如的10分钟。

 

参考文章:https://www.jianshu.com/p/ba1aa0c52942

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!