分布式日志治理:Log4j2自定义Appender写日志到RocketMQ
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,
15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea


文章目录
- Log4j2自定义Appender写日志到RocketMQ
- 引言:分布式系统下的日志治理新范式——基于Log4j2与RocketMQ的高效实践
- 1. 添加Maven依赖
- 2. 实现自定义Appender
- 3. 自定义Appender插件注册
- 4. 配置log4j2.xml
- 5. 关键点说明
- 6. 注意事项
Log4j2自定义Appender写日志到RocketMQ
引言:分布式系统下的日志治理新范式——基于Log4j2与RocketMQ的高效实践
在云原生与微服务架构大行其道的今天,日志管理已从简单的本地文件存储演化为支撑系统可观测性的核心支柱。传统日志处理方式在面对日均TB级的日志量、跨地域服务调用链追踪、实时异常检测等场景时,往往陷入存储碎片化、检索效率低下、处理延迟高的困境。尤其在金融交易、物联网、在线教育等高并发领域,日志数据不仅是问题排查的"黑匣子",更是业务洞察的"数据金矿",亟需一种能够兼顾实时性、可靠性和可扩展性的新型日志处理方案。
Apache RocketMQ作为阿里巴巴开源的高性能分布式消息中间件,凭借其毫秒级消息投递、万亿级消息堆积能力和完善的事务机制,为日志数据的异步化处理提供了理想通道。而Log4j2作为Java生态中最主流的日志框架,其插件化架构和异步日志特性,使得开发者能够通过自定义Appender将日志生产与传输逻辑解耦。二者的结合,不仅实现了日志从"被动记录"到"主动流转"的范式升级,更构建起日志采集、传输、存储、分析的全链路解决方案。
本文深入探讨如何基于Log4j2最新架构扩展日志输出能力,通过构建自定义RocketMQAppender实现日志数据的实时投递。该方案突破传统日志文件的物理边界,使日志数据可无缝对接Elasticsearch、Flink、Spark等大数据处理平台,为实时监控、安全审计、用户行为分析等场景提供高时效数据源。
本文从Maven依赖配置、Appender线程模型设计、RocketMQ生产者最佳实践等维度展开,详细解析如何在高并发场景下保障日志传输的可靠性与性能平衡,并针对消息压缩、失败重试、资源监控等关键问题给出工程级解决方案。通过此实践,开发者可将日志系统的吞吐量提升1-2个数量级,同时显著降低日志丢失风险,为构建企业级可观测性平台奠定坚实基础。
以下是基于Java Log4j2自定义Appender将日志写入RocketMQ的步骤:
1. 添加Maven依赖
<!-- Log4j2 核心依赖 -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.23.1</version>
</dependency><!-- RocketMQ客户端 -->
<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client</artifactId><version>5.1.4</version>
</dependency>
2. 实现自定义Appender
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.*;
import org.apache.rocketmq.client.apis.*;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.ProducerBuilder;
import org.apache.rocketmq.client.apis.producer.SendResult;import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;@Plugin(name = "RocketMQAppender",category = Core.CATEGORY_NAME,elementType = Appender.ELEMENT_TYPE,printObject = true
)
public final class RocketMQAppender extends AbstractAppender {private Producer producer;private final String namesrvAddr;private final String topic;private final String producerGroup;private final int sendTimeout;protected RocketMQAppender(String name, Filter filter, Layout<? extends Serializable> layout,String namesrvAddr, String topic, String producerGroup, int sendTimeout) {super(name, filter, layout, true, Property.EMPTY_ARRAY);this.namesrvAddr = namesrvAddr;this.topic = topic;this.producerGroup = producerGroup;this.sendTimeout = sendTimeout;}@Overridepublic void start() {try {final ClientServiceProvider provider = ClientServiceProvider.loadService();ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(namesrvAddr);ProducerBuilder producerBuilder = provider.newProducerBuilder().setClientConfiguration(builder.build()).setTopics(topic);if (producerGroup != null) {producerBuilder.setProducerGroup(producerGroup);}producer = producerBuilder.build();} catch (ClientException e) {LOGGER.error("Initialize RocketMQ Producer failed", e);}super.start();}@Overridepublic void append(LogEvent event) {if (producer == null) return;try {byte[] body = getLayout().toByteArray(event);String messageBody = new String(body, StandardCharsets.UTF_8);final ClientServiceProvider provider = ClientServiceProvider.loadService();Message message = provider.newMessageBuilder().setTopic(topic).setBody(body).build();SendResult sendResult = producer.send(message);// 可添加发送结果处理逻辑} catch (Exception e) {LOGGER.error("Send log to RocketMQ failed", e);}}@Overridepublic void stop() {super.stop();if (producer != null) {try {producer.close();} catch (Exception e) {LOGGER.error("Close RocketMQ Producer failed", e);}}}@PluginFactorypublic static RocketMQAppender createAppender(@PluginAttribute("name") String name,@PluginElement("Filter") Filter filter,@PluginElement("Layout") Layout<? extends Serializable> layout,@PluginAttribute("namesrvAddr") String namesrvAddr,@PluginAttribute("topic") String topic,@PluginAttribute(value = "producerGroup", defaultString = "LogProducerGroup") String producerGroup,@PluginAttribute(value = "sendTimeout", defaultInt = 3000) int sendTimeout) {if (name == null) {LOGGER.error("No name provided for RocketMQAppender");return null;}return new RocketMQAppender(name, filter, layout, namesrvAddr, topic, producerGroup, sendTimeout);}
}
3. 自定义Appender插件注册
log4j2最新版本的插件注册通过将Log4j插件描述文件(即Log4j2Plugins.dat)放入类路径完成。该文件在编译时通过PluginProcessor注解处理器生成。
需按以下方式配置构建工具以启用PluginProcessor:
Maven配置
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><executions><execution><id>generate-log4j-plugin-descriptor</id><goals><goal>compile</goal></goals><phase>process-classes</phase><configuration><proc>only</proc><annotationProcessorPaths><!-- 引入包含`PluginProcessor`的`log4j-core`,用于生成`Log4j2Plugins.dat` --><path><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.24.3</version></path></annotationProcessorPaths><annotationProcessors><!-- 使用`PluginProcessor`处理源码并生成`Log4j2Plugins.dat` --><processor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</processor></annotationProcessors></configuration></execution></executions></plugin></plugins>
</build>
编译后,classes/META-INF目录下会多出一个org目录

该目录下存放的就是插件的注册信息:

4. 配置log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><RocketMQAppender name="RocketMQ"namesrvAddr="localhost:8081"topic="LOG_TOPIC"producerGroup="LOG_PRODUCER_GROUP"sendTimeout="5000"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/></RocketMQAppender></Appenders><Loggers><Root level="info"><AppenderRef ref="RocketMQ"/></Root></Loggers>
</Configuration>
5. 关键点说明
-
线程安全设计:
RocketMQ Producer是线程安全的,可以复用实例- 在start()中初始化,stop()中销毁
-
异常处理:
- 在send方法中添加try-catch防止日志记录阻塞主线程
- 建议添加失败重试机制(示例未展示)
-
性能优化建议:
// 可添加批量发送支持 producer.send(List<Message> messages, SendReceipt sendReceipt);// 或使用异步发送 CompletableFuture<SendResult> future = producer.sendAsync(message); -
扩展功能建议:
- 添加消息Tag支持
- 支持自定义Key/Value属性
- 添加消息压缩功能
- 支持同步/异步发送模式切换
6. 注意事项
-
版本兼容性:
- RocketMQ 5.x+ 使用新的客户端API
- 旧版本(4.x)需要调整客户端实现
-
资源管理:
- 确保Producer在JVM关闭时正确关闭
- 建议添加发送队列积压监控
-
安全配置:
// 如果需要认证 ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(namesrvAddr).setCredentialProvider(new StaticSessionTokenCredentialProvider("accessKey", "secretKey")); -
日志格式化:
- 建议使用JSON格式方便后续处理
- 可添加TraceID等全链路追踪信息
相关文章:
分布式日志治理:Log4j2自定义Appender写日志到RocketMQ
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
【口腔粘膜鳞状细胞癌】文献阅读3
文献 Single-cell transcriptomic analysis uncovers the origin and intratumoral heterogeneity of parotid pleomorphic adenoma 单细胞转录组学分析揭示了腮腺多形性腺瘤的起源和瘤内异质性 IF:10.8中科院分区:1区 医学WOS分区:Q1 摘要 多形性腺瘤 (PA&#…...
【2025“华中杯”大学生数学建模挑战赛】C题:就业状态分析与预测 详细解题思路
目录 2025“华中杯”大学生数学建模挑战赛C题 详细解题思路一、问题一1.1 问题分析1.2 数学模型 1.3 Python代码1.4 Matlab代码 二、问题二2.1 问题分析2.2 数学模型 2.3 Python代码2.4 Matlab代码 三、问题三3.1 问题分析 四、问题四4.1 问题分析与数学模型 2025“华中杯”大学…...
扫雷-C语言版
C语言扫雷游戏设计(完整版) 游戏背景 扫雷是一款经典的益智类单人电脑游戏,最早出现在1960年代,并在1990年代随着Windows操作系统而广为人知。游戏目标是在不触发任何地雷的情况下,揭开所有非地雷的格子。玩家需要根…...
【fisco bcos】基于ABI调用智能合约
参考官方文档:https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/sdk/java_sdk/assemble_transaction.html 先放一下智能合约: (就是一个很简单的插入和查找嗯) pragma solidity ^0.4.25; pragma experimental…...
Delphi Ini文件对UTF8支持不爽的极简替代方案
如题,没太多废话,直接复制走即可。 unit uConfig;interfaceuses classes, Sysutils;typeTConfig class privateFFileName: String;FConfig:TStringList; protectedpublicconstructor Create(ConfigFile:String);destructor Destroy;property FileName…...
【LangChain实战】构建下一代智能问答系统:从RAG架构到生产级优化
打破传统问答系统的次元壁 当ChatGPT在2022年掀起AI革命时,开发者们很快发现一个残酷现实:通用大模型在专业领域的表现如同拿着地图的盲人,既无法理解企业私有数据,也无法保证事实准确性。这催生了RAG(检索增强生成&a…...
C++编译与链接:从源码到可执行文件的魔法之旅(Visual Studio实践)
文章目录 **C++编译与链接:从源码到可执行文件的魔法之旅(Visual Studio实践)****一、C++编译器的工作流程****二、Visual Studio环境配置实战****三、示例项目:Hello World全流程解析****四、高级技巧与工具链****五、总结与参考资料**C++编译与链接:从源码到可执行文件的…...
RL中的rollout和episode的区别请问是啥
很好的问题兄弟,rollout 和 episode 在强化学习(RL)里经常一起出现,虽然有重叠,但含义和使用语境还是有区别的: ✅ 一句话总结: Episode 是一个完整的任务过程(从起点到终点…...
个人博客系统后端 - 用户信息管理功能实现指南(上)
本文记录了如何实现用获取户信息,用户信息更新,用户头像上传三大基础功能 先上接口实现截图: 一、项目结构概览 先介绍一下 个人博客系统采用了标准的 Spring Boot 项目结构,用户功能相关的文件主要分布在以下几个目录:…...
判断一个整数是否为素数
#include <stdio.h> #include <stdbool.h> // 引入布尔类型// 函数声明:判断一个整数是否为素数 bool isPrime(int num);int main() {int number;// 提示用户输入一个整数printf("请输入一个整数:");scanf("%d", &n…...
具身智能机器人学习路线全解析
一、引言 具身智能机器人作为融合了机器人学、人工智能、认知科学等多领域知识的前沿技术,正逐渐改变着我们的生活和工作方式。从工业制造到家庭服务,从医疗护理到太空探索,具身智能机器人都展现出了巨大的潜力。对于想要深入了解和学习这一…...
虚幻基础:ue引擎的碰撞
文章目录 碰撞:碰撞体间 运动后 产生碰撞的行为——由引擎负责,并向各自发送事件忽略重叠阻挡 碰撞体类型模式纯查询:不清楚具体作用可以阻挡 actor碰撞(武器:刀/子弹)子组件可以产生阻挡 角色的碰撞只有根组件可以阻挡࿰…...
写项目时一些疑惑:组件间的通信、createDownloadUrl和DownloadUrl,ArrayBuffer与Blob等
目录 一、[vite] Internal server error: No known conditions for "./lib/locale/lang/zh-cn" specifier in "element-plus" package 二、可以用vue和JS的代码片段,但是用不了html的代码片段 三、meta是什么东西 四、为什么代码保持一致,但是时间轴始…...
TAS启动与卸载
3. 启动TAS(Thin-Agent服务) TAS在安装完成后通常会自动启动,并在系统重启时自启。如需手动启动,请按以下步骤操作:  3.1 在Windows上启动TAS 1. 打开 Windows服务管理器: ◦ 按下 Win R&…...
对抗生成进化:基于DNA算法的AIGC检测绕过——让AI创作真正“隐形“
一、技术背景与核心思想 2025年,AIGC检测工具(如Originality.AI 5.0)的识别准确率已达99.3%。本研究提出基于染色体编码的对抗进化框架(CAEF),通过模拟生物进化过程动态优化生成模型,成功将检测…...
手动关闭ArcGIS与ArcGIS Online连接的方法
【关闭软件启动时ArcGIS与ArcGIS Online连接方法】 打开C盘找到文件夹“C:\Program Files (x86)\Common Files\ArcGIS\bin”,如下图,删除“ArcGISConnection.exe”与“ArcGISConnectionTest.exe”文件,软件下次启动的时候就不会建立与ArcGIS …...
SpringBoot条件注解全解析:核心作用与使用场景详解
目录 引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、ConditionalOnClass和ConditionalOnMissingClass2、ConditionalOnBean和ConditionalOnMissingBean3、ConditionalOnProperty4、ConditionalOnWebApplication和ConditionalOnNotWebApplication5、ConditionalO…...
android11通过白名单卸载安装应用
目录 1.源码路径: 2.准备文件package.conf: 3.安装方法installPackagesLI 4.卸载方法deletePackageX 1.源码路径: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java public static final String WHITELIST_PATH="/data/misc/pa…...
大M法处理非线性约束线性化
在电力系统优化问题中,大M法(Big M Method)是一种经典的处理非线性约束线性化的技术,尤其适用于混合整数线性规划(MILP)问题。 其核心思想是通过引入足够大的常数M和辅助变量(如二元变量或松弛…...
【网络安全】谁入侵了我的调制解调器?(一)
文章目录 我被黑了159.65.76.209,你是谁?黑客攻击黑客?交出证据三年后我被黑了 两年前,在我家里使用家庭网络远程办公时,遇到了一件非常诡异的事情。当时,我正在利用一个“盲 XXE 漏洞”,这个漏洞需要借助一个外部 HTTP 服务器来“走私”文件。为了实现这一点,我在 AW…...
【Nokia 7360 ISAM局端】7360局端升级步骤
引言 Nokia 7360 ISAM局端是当前主流的OLT局端之一,在测试ONT产品中经常需要对接7360局端,特别是欧美等海外运营商。测试过程中经常需要升级OLT版本,以便对齐前方客户的现网环境。本文介绍将Nokia 7360 ISAM局端升级到L6GPAA65.669版本的详细步骤。 连接带外管理口 将维护…...
await 在多线程,子线程中的使用
await 在多线程,子线程中的使用 await self.send_reply(user, user, user, auto_content, reply) 这行代码是在一个异步函数里调用类的实例方法 send_reply 代码含义 1. await 关键字 在 Python 的异步编程里,await 关键字的作用是暂停当前异步函数的执行,直到 await 后…...
主数据管理:企业数字化转型的 “数据基石“ 如何为 AI 筑基?
引言:当数据成为新石油,谁在炼制 "高纯度燃料"? 在数字化转型的浪潮中,企业宛如行驶在数据海洋中的巨轮,AI 则是驱动巨轮破浪前行的引擎。但引擎能否高效运转,取决于燃料的纯度 —— 这正是主数…...
使用源码编译安装golang的docker版
编译规则 1.4之前用C写的,1.4可编译后续一直到1.9版本,后续版本实现了自举,后续版本是go写的,基本上相互低2个版本能编译出新版本。 Go < 1.4:C 工具链。 1.5 < Go < 1.19:Go 1.4 编译器。 1.20…...
使用 chromedriver 实现网络爬虫【手抄】
1、引用 selenium 包 <dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.29.0</version> </dependency> <dependency><groupId>org.seleniumhq.seleniu…...
Linux之 grep、find、ls、wc 命令
Linux之 grep、find、ls、wc 命令 “ 在 Linux 世界中,命令行是不可或缺的一部分,而掌握一些常用的命令可以帮助你更有效率地管理文件和系统。本文将为你介绍四個基礎而强大的 Linux 命令:grep、find、ls 和 wc,带你开启高效文件…...
AI 模型高效化:推理加速与训练优化的技术原理与理论解析
AI 模型高效化:推理加速与训练优化的技术原理与理论解析 文章目录 AI 模型高效化:推理加速与训练优化的技术原理与理论解析一、推理加速:让模型跑得更快的“程序员魔法”(一)动态结构自适应推理:像人类一样…...
c++STL——vector的使用和模拟实现
文章目录 vector的使用和模拟实现vector的使用vector介绍重点接口的讲解迭代器部分默认成员函数空间操作增删查改操作迭代器失效问题(重要)调整迭代器 vector的模拟实现实现的版本模拟实现结构预先处理的函数尾插函数push_backswap函数赋值重载size函数reserve函数 迭代器默认成…...
git更新的bug
文章目录 1. 问题2. 分析 1. 问题 拉取了一个项目后遇到了这个问题, nvocation failed Server returned invalid Response. java.lang.RuntimeException: Invocation failed Server returned invalid Response. at git4idea.GitAppUtil.sendXmlRequest(GitAppUtil…...
