自定义log4j2中的Appender来获取日志内容的示例代码

 更新时间:2024年02月06日 09:21:50   作者:咖啡杯里的茶_  
在 Log4j2 中,Appender 是负责将日志事件输出到目标地点的组件,本文讲述的是通过 log4j 中自定义的 Appender 来获取需要打印的日志信息,文中有详细的代码示例供大家参考,需要的朋友可以参考下

springboot版本:2.6.15 log4j版本:2.17.2

Log4j中的Appender 是什么

本文讲述的是通过 log4j 中自定义的 Appender 来获取需要打印的日志信息,那么 Appender 是什么东西呢?先简单了解一下

在 Log4j2 中,Appender 是负责将日志事件输出到目标地点的组件。Appender 可以将日志事件输出到控制台、文件、网络等不同的目的地。 Log4j2 提供了多种内置的 Appender,例如 ConsoleAppender、FileAppender、AsyncAppender 等,同时支持自定义 Appender。 Appender 的主要职责是将日志事件按照指定的格式和目的地输出。每个 Appender 都有自己的名称和类型,可以根据需要配置不同的属性。例如,FileAppender 用于将日志事件输出到文件中,其属性包括文件名、追加模式(是否覆盖已存在的文件或追加到文件末尾)等。

需求背景

有两个系统,系统 A 和系统 B, 系统 A 会发送指令给系统 B, 系统 B 会响应结果给系统 A,这两个系统之间的通信方式是 MQ, 即 A 通过 MQ 发送数据给 B(数据中有 UUID 字段值),系统 B处理完成后将结果通过 MQ 发送给 A,请求和响应的对应关系则可以通过 UUID 来进行匹配

现在有一个需求是监控在某个时间段内发送了多少指令,以及多少指令失败(在给定时间内没有收到响应或者响应中给了错误结果认为失败)

对于这种需求一般都是通过 AOP 来解决,但是在部分场景下 AOP 也比较难处理,比如上面场景中其实这个监控处理是有两部分的,第一部分是发送请求,第二部分是获取请求对应的结果,而且不是 http 调用,是通过 MQ 调用,即业务逻辑实现是分布在两个方法中的 (可以抽取一层将发送请求和响应结果逻辑放在一起,并且返回原始结果即可)

下面基于已有实现提供另外一种更简单的解决方式,已有实现的特点:

  • 指令发送和结果接收都被抽取成独立的方法,只要是两个系统间的 MQ 通信最终都会调用这两个方法(一个发送,一个接收结果)
  • 在这两个方法中都将原始的参数以及最终的响应结果给打印出来了 基于以上两点,如果可以拿到打印的日志信息就可以比较简单汇总处理

修改前测试代码

log4j2 配置文件

下面 log4j2-dev.xml 配置文件很简单,就是将所有日志打印到控制台,日志级别为 info

<?xml version="1.0" encoding="UTF-8"?>  
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->  
<configuration monitorInterval="60">  
    <Properties>  
        <property name="LOG_PATTERN_CONSOLE"  
                  value="%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx"/>  
    </Properties>  
  
    <appenders>  
        <!-- Console 是将日志信息打印打控制台-->  
        <console name="Console" target="SYSTEM_OUT">  
            <PatternLayout pattern="${LOG_PATTERN_CONSOLE}"/>  
            <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>  
        </console>  
    </appenders>  
  
    <loggers>    
        <root level="info">  
            <AppenderRef ref="Console"/>  
        </root>  
    </loggers>  
  
</configuration>

两个测试类代码

给出两个测试类是为了对比,后续自定义的 Appender 只会作用在一个测试类中,对于其他类的日志打印不会获取到对应的日志信息

@Component  
public class DemoService {  
    private static final Logger logger = LoggerFactory.getLogger(DemoService.class);  
    public void doSomething(String param) {  
        logger.info("test demo");  
    }  
}

@Component  
public class DemoService2 {  
    private static final Logger logger = LoggerFactory.getLogger(DemoService2.class);  
    public void doSomething(String param) {  
        logger.info("test demo 2");  
    }  
}

自定义Appender

自定义的 Appender 不需要被 Spring 管理,所以不需要 @Component 等注解

package com.example;  
  
import org.apache.logging.log4j.core.Appender;  
import org.apache.logging.log4j.core.Filter;  
import org.apache.logging.log4j.core.Layout;  
import org.apache.logging.log4j.core.LogEvent;  
import org.apache.logging.log4j.core.appender.AbstractAppender;  
import org.apache.logging.log4j.core.config.plugins.Plugin;  
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;  
import org.apache.logging.log4j.core.config.plugins.PluginElement;  
import org.apache.logging.log4j.core.config.plugins.PluginFactory;  
import org.apache.logging.log4j.core.layout.PatternLayout;  
  
import java.io.Serializable;  
  
@Plugin(name = "CusAppender", category = "Core", elementType = Appender.ELEMENT_TYPE)  
public class CustomAppender extends AbstractAppender {  
  
    protected CustomAppender(String name, Filter filter, Layout<? extends Serializable> layout) {  
        super(name, filter, layout);  
    }  
  
    @Override  
    public void append(LogEvent event) {  
        // 在这里获取日志信息  
        String message = event.getMessage().getFormattedMessage();  
        // 打印日志信息  
        System.out.println("拦截到的消息" + message);  
    }  
  
    @PluginFactory  
    public static CustomAppender createAppender(@PluginAttribute("name") String name,  
                                                @PluginElement("Layout") Layout<? extends Serializable> layout) {  
        if (name == null) {  
            throw new IllegalArgumentException("No name provided for CustomAppender");  
        }  
        if (layout == null) {  
            layout = PatternLayout.createDefaultLayout();  
        }  
        return new CustomAppender(name, null, layout);  
    }  
}

将自定义 Appender 配置到 Log4j 配置文件中

<?xml version="1.0" encoding="UTF-8"?>  
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->  
<configuration monitorInterval="60">  
    <Properties>  
        <property name="LOG_PATTERN_CONSOLE"  
                  value="%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx"/>  
    </Properties>  
  
    <appenders>  
	    <!-- 添加自定义的 Appender -->  
        <CusAppender name="CustomAppender" />  
        <!-- Console 是将日志信息打印打控制台-->  
        <console name="Console" target="SYSTEM_OUT">  
            <PatternLayout pattern="${LOG_PATTERN_CONSOLE}"/>  
            <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>  
        </console>  
    </appenders>  
  
    <loggers>  
        <!-- 对某个具体类进行单独的配置, 需要关注 additivity 参数-->  
        <Logger name="com.example.DemoService2" level="info" additivity="true">  
            <AppenderRef ref="CustomAppender"/>  
        </Logger>  
  
        <root level="info">  
            <AppenderRef ref="Console"/>  
        </root>  
    </loggers>  
  
</configuration>

上述配置文件针对原始配置文件主要有两个修改点

  • <appenders> 标签下面添加了 <CusAppender name="CustomAppender" /> 标签, 这里使用 CusAppender 标签是因为 @Plugin(name = "CusAppender" 中配置的是这个名字
  • <loggers> 标签下添加了如下内容
<!-- 对某个具体类进行单独的配置, 需要关注 additivity 参数-->  
<Logger name="com.example.DemoService2" level="info" additivity="true">  
	<AppenderRef ref="CustomAppender"/>  
</Logger>  

通过这种方式,当 DemoService2 类有日志打印并且级别在 info 及以上时就会调用到自定义 Appender 的 append 方法中

public void append(LogEvent event) {  
        // 在这里获取日志信息  
	String message = event.getMessage().getFormattedMessage();  
	// 打印日志信息  
	System.out.println("拦截到的消息" + message);  
}  

additivity="true" 配置的作用

先看下 loggers 配置

<loggers>  
	<!-- 对某个具体类进行单独的配置, 需要关注 additivity 参数-->  
	<Logger name="com.example.DemoService2" level="info" additivity="true">  
		<AppenderRef ref="CustomAppender"/>  
	</Logger>  

	<root level="info">  
		<AppenderRef ref="Console"/>  
	</root>  
</loggers>  

这里的 root 就是顶层的配置,对于 DemoService2 类来说,因为单独配置了 Logger, 所以会先走单独配置的 Logger 中的 Appender, 也就是 CustomAppender,但是 CustomAppender 中只是获取日志信息,并没有往控制台或者文件写日志,这样日志就会丢失了,additivity 参数就是用来控制是否继续使用父级的 Appender, additivity=true 时,日志会先经过 CustomAppender 处理,然后会让父级( root ) 的 Console 处理。

结果

先调用 DemoService2 方法,然后调用 DemoService 的方法,日志打印如下

拦截到的消息test demo 2
2024-02-05 16:48:21.641  INFO 2760 --- [command-execute] c.e.DemoService2                         : test demo 2
2024-02-05 16:48:34.424  INFO 2760 --- [command-execute] c.e.DemoService                          : test demo

说明只有调用了 DemoService2 类中日志方法时才会调用到自定义的 Appender 中

以上就是自定义log4j2中的Appender来获取日志内容的示例代码的详细内容,更多关于自定义Appender获取日志内容的资料请关注脚本之家其它相关文章!

相关文章

  • Spring Boot应用Docker化的步骤详解

    Spring Boot应用Docker化的步骤详解

    这篇文章主要给大家介绍了关于Spring Boot应用Docker化的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-04-04
  • protobuf简介及使用流程

    protobuf简介及使用流程

    本文介绍了Protocol Buffers(protobuf)的数据结构序列化和反序列化框架,包括其特点、使用流程和快速上手,通过一个简单的通讯录示例,展示了如何创建.proto文件、添加注释、编写消息定义、编译.proto文件以及进行序列化和反序列化操作,感兴趣的朋友一起看看吧
    2025-02-02
  • Java Object定义三个点实现代码

    Java Object定义三个点实现代码

    这篇文章主要介绍了Java Object定义三个点实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • Java多线程下载的实现方法

    Java多线程下载的实现方法

    复习多线程的时候,练习了下,顺便记录一下:
    2013-03-03
  • Lombok不生效,提示java: 找不到符号的解决方案

    Lombok不生效,提示java: 找不到符号的解决方案

    这篇文章主要介绍了Lombok不生效,提示java: 找不到符号的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • 记一次集成swagger2(Knife4j)在线文档提示:Knude4j文档请求异常的解决办法

    记一次集成swagger2(Knife4j)在线文档提示:Knude4j文档请求异常的解决办法

    Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案,下面这篇文章主要给大家介绍了关于一次集成swagger2(Knife4j)在线文档提示:Knude4j文档请求异常的解决办法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-02-02
  • SpringBoot在idea中的 .idea和 .iml文件的作用

    SpringBoot在idea中的 .idea和 .iml文件的作用

    本文主要介绍了SpringBoot在idea中的 .idea和 .iml文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • spring boot 自动更新静态文件和后台代码的实例

    spring boot 自动更新静态文件和后台代码的实例

    下面小编就为大家分享一篇spring boot 自动更新静态文件和后台代码的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • Java中的StringTokenizer实现字符串切割详解

    Java中的StringTokenizer实现字符串切割详解

    这篇文章主要介绍了Java中的StringTokenizer实现字符串切割详解,java.util工具包提供了字符串切割的工具类StringTokenizer,Spring等常见框架的字符串工具类(如Spring的StringUtils),需要的朋友可以参考下
    2024-01-01
  • Java将对象保存到文件中/从文件中读取对象的方法

    Java将对象保存到文件中/从文件中读取对象的方法

    下面小编就为大家带来一篇Java将对象保存到文件中/从文件中读取对象的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12

最新评论