log4j2发送消息至Kafka
title: 自定义log4j2发送日志到Kafka
tags: log4j2,kafka
为了给公司的大数据平台提供各项目组的日志,而又使各项目组在改动上无感知。做了一番调研后才发现log4j2默认有支持将日志发送到kafka的功能,惊喜之下赶紧看了下log4j对其的实现源码!发现默认的实现是同步阻塞的,如果kafka服务一旦挂掉会阻塞正常服务的日志打印,为此本人在参考源码的基础上做了一些修改。
log4j日志工作流程
log4j2对于log4j在性能上有着显著的提升,这点官方上已经有了明确的说明和测试,所以不多赘述。在为了更熟练的使用,还是有必要了解其内部的工作流程。这是官网log4j的一张类图
Applications using the Log4j 2 API will request a Logger with a specific name from the LogManager. The LogManager will locate the appropriate LoggerContext and then obtain the Logger from it. If the Logger must be created it will be associated with the LoggerConfig that contains either a) the same name as the Logger, b) the name of a parent package, or c) the root LoggerConfig. LoggerConfig objects are created from Logger declarations in the configuration. The LoggerConfig is associated with the Appenders that actually deliver the LogEvents.
官网已经解释他们之间的关系了,这里不再对每个类的功能和作用做具体介绍,今天的重点是Appender类,因为他将决定将日志输出至何方。
- Appender
The ability to selectively enable or disable logging requests based on their logger is only part of the picture. Log4j allows logging requests to print to multiple destinations. In log4j speak, an output destination is called an Appender. Currently, appenders exist for the console, files, remote socket servers, Apache Flume, JMS, remote UNIX Syslog daemons, and various database APIs. See the section on Appenders for more details on the various types available. More than one Appender can be attached to a Logger.
核心配置
上图是log4j2发送日志到kafka的核心类,其实最主要的KafkaAppender,其他的几个类是连接kafka服务的。
- KafkaAppender核心配置
@Plugin(name = "Kafka", category = "Core", elementType = "appender", printObject = true)
public final class KafkaAppender extends AbstractAppender {
/**
*
*/
private static final long serialVersionUID = 1L;
@PluginFactory
public static KafkaAppender createAppender(
@PluginElement("Layout") final Layout<? extends Serializable> layout,
@PluginElement("Filter") final Filter filter,
@Required(message = "No name provided for KafkaAppender") @PluginAttribute("name") final String name,
@PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final boolean ignoreExceptions,
@Required(message = "No topic provided for KafkaAppender") @PluginAttribute("topic") final String topic,
@PluginElement("Properties") final Property[] properties) {
final KafkaManager kafkaManager = new KafkaManager(name, topic, properties);
return new KafkaAppender(name, layout, filter, ignoreExceptions, kafkaManager);
}
private final KafkaManager manager;
private KafkaAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, final boolean ignoreExceptions, final KafkaManager manager) {
super(name, filter, layout, ignoreExceptions);
this.manager = manager;
}
@Override
public void append(final LogEvent event) {
if (event.getLoggerName().startsWith("org.apache.kafka")) {
LOGGER.warn("Recursive logging from [{}] for appender [{}].", event.getLoggerName(), getName());
} else {
try {
if (getLayout() != null) {
manager.send(getLayout().toByteArray(event));
} else {
manager.send(event.getMessage().getFormattedMessage().getBytes(StandardCharsets.UTF_8));
}
} catch (final Exception e) {
LOGGER.error("Unable to write to Kafka [{}] for appender [{}].", manager.getName(), getName(), e);
throw new AppenderLoggingException("Unable to write to Kafka in appender: " + e.getMessage(), e);
}
}
}
@Override
public void start() {
super.start();
manager.startup();
}
@Override
public void stop() {
super.stop();
manager.release();
}
- log4j2.xml简单配置
<?xml version="1.0" encoding="UTF-8"?>
...
<Appenders>
<Kafka name="Kafka" topic="log-test">
<PatternLayout pattern="%date %message"/>
<Property name="bootstrap.servers">localhost:9092</Property>
</Kafka>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Kafka"/>
</Root>
<Logger name="org.apache.kafka" level="INFO" /> <!-- avoid recursive logging -->
</Loggers>
其中
@Plugin的name属性对应的xml配置文件里面Kafka标签,当然这个也可以自定义。与此同时,也需要将@Plugin的name属性改为MyKafka。如下配置:
<MyKafka name="Kafka" topic="log-test">
自定义配置
有时候我们会用到的属性由于默认的KafkaAppender不一定支持,所以需要一定程度的改写。但是改写也比较方便,只需要从构造器的Properties kafkaProps属性中取值即可。为了满足项目要求,我这边定义了platform和serviceName两个属性。
通过KafkaAppender的源码可知,他发送消息采取的是同步阻塞的方式。经过测试,一旦kafka服务挂掉,那么将会影响项目服务正常的日志输出,而这不是我希望看到的,所以我对他做了一定的程度的修改。
feature::
kafka服务一直正常
这种情况属于最理想的情况,消息将源源不断的发送至kafka broker
kafka服务挂掉,过一段时间后恢复正常
当kafka服务在挂掉的那一刻,后续所有的消息将会输出至
ConcurrentLinkedQueue队列里面去。同时该队列的消息也会不断的被消费,输出至本地文件。当心跳检测到kafka broker恢复正常了,本地文件的内容将会被读取,然后发送至kafka broker。需要注意的时候,此时会有大量消息被实例化为ProducerRecord对象,堆内存的占用率非常高,所以我用线程阻塞了一下!- kafka服务一直挂
所有的消息都会被输出至本地文件。
源码点我
log4j2发送消息至Kafka的更多相关文章
- Golang之发送消息至kafka
windows下安装zookeeper 1.安装JAVA-JDK,从oracle下载最新的SDK安装(我用的是1.8的) 2.安装zookeeper3.3.6,下载地址:http://apache.f ...
- Canal Server发送binlog消息到Kafka消息队列中
Canal Server发送binlog消息到Kafka消息队列中 一.背景 二.需要修改的地方 1.canal.properties 配置文件修改 1.修改canal.serverMode的值 2. ...
- Kafka发送消息失败原因
Kafka发送消息方法如下: Properties properties = new Properties(); properties.put("zookeeper.connect" ...
- Kafka生产者发送消息的三种方式
Kafka是一种分布式的基于发布/订阅的消息系统,它的高吞吐量.灵活的offset是其它消息系统所没有的. Kafka发送消息主要有三种方式: 1.发送并忘记 2.同步发送 3.异步发送+回调函数 下 ...
- kafka producer batch 发送消息
1. 使用 KafkaProducer 发送消息,是按 batch 发送的,producer 首先把消息放入 ProducerBatch 中: org.apache.kafka.clients.pro ...
- kafka producer 发送消息简介
kafka 的 topic 由 partition 组成,producer 会根据 key,选择一个 partition 发送消息,而 partition 有多个副本,副本有 leader 和 fol ...
- 物联网架构成长之路(8)-EMQ-Hook了解、连接Kafka发送消息
1. 前言 按照我自己设计的物联网框架,对于MQTT集群中的所有消息,是要持久化到磁盘的,这里采用一个消息队列中间件Kafka作为数据缓冲,缓冲结果存到数据仓库中,以供后续作为数据分析.由于MQTT集 ...
- SPRING 集成 KAFKA 发送消息
准备工作 1.安装kafka+zookeeper环境 2.利用命令创建好topic,创建一个topic my-topic 集成步骤 1.配置生产者 <?xml version="1.0 ...
- Kafka学习笔记(6)----Kafka使用Producer发送消息
1. Kafka的Producer 不论将kafka作为什么样的用途,都少不了的向Broker发送数据或接受数据,Producer就是用于向Kafka发送数据.如下: 2. 添加依赖 pom.xml文 ...
随机推荐
- 【Arduino】开源开发板说明
来自世界各地的新型微控制器层出不穷,这类开发板多数都是通过Arduino改进的版本,例如由Arduino所改良的Yún一样,主要是针对网状网路进行改进或升级了其它无线功能. 但一些开发板也有着其独到的 ...
- net core体系-web应用程序-4asp.net core2.0 项目实战(1)-4项目前端说明
本文目录1. 摘要2. UI界面展示 3. 主要技术点4. 总结 1. 摘要 平时比较忙,写一篇文章可能跨度好几天,希望各位多多包涵.闲言少叙直接进入正题. 2. UI界面 NCMVC用的就图二, ...
- Codeforces 229E Gifts 概率dp (看题解)
Gifts 感觉题解写的就是坨不知道什么东西.. 看得这个题解. #include<bits/stdc++.h> #define LL long long #define LD long ...
- Codeforces round FF
Problem A:水题,直接gan #include<bits/stdc++.h> using namespace std; ; int vis[N]; int main() { int ...
- python--闭包、装饰器
1.闭包 ①.python中一切皆对象,函数中嵌套函数肯定也是合理的,这种函数也叫作内嵌函数(内部函数) ②.内嵌函数只能被外部函数调用,被外部函数以外的区域被调用,就会出错!!! ③.若内部函数中引 ...
- day8数据类型补充,集合,深浅拷贝
思维导图: 集合的补充:下面的思维导图有一个点搞错了,在这里纠正一下,没有合集,是反交集,^这个是反差集的意思 . 交集&,反交集^,差集-,并集|,然后就是子集和超集 数据类型补充: ''' ...
- Python编程基础[条件语句if 循环语句 for,while](二)
ython条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. 可以通过下图来简单了解条件语句的执行过程: if 判断条件: 执行语句……else: 执行语句…… x= ...
- thinkphp5 行为(钩子)扩展
行为整理链接 浅谈PHP中的钩子 钩子相当于一个插件,在某些执行顺序上插入进去. 行为可以在写app接口中对所有请求执行到控制器前 执行用户权限判断,sign验证等,这样就不用在每个接口中判断了 注意 ...
- Scala-Unit4-Scala数组/集合
一.Scala数组 1.数组的定义: 方法一:val arr = new Array[String](3) String代表数据的元素类型.3表示数组的长度 方法二:val arr = Array[I ...
- logback实践笔记
前言 每次看公司配置好的logback文件的时候,都不知道什么意思.导致有的时候,一些项目发到测试环境的时候,有的项目没有打印日志,自己都不知道哪里有问题.所以自己新建一个springboot项目 ...