使用Log4j将程序日志实时写入Kafka
第一部分 搭建Kafka环境
安装Kafka
下载:http://kafka.apache.org/downloads.html
tar zxf kafka-<VERSION>.tgz
cd kafka-<VERSION>
启动Zookeeper
启动Zookeeper前需要配置一下config/zookeeper.properties:
接下来启动Zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties
启动Kafka Server
启动Kafka Server前需要配置一下config/server.properties。主要配置以下几项,内容就不说了,注释里都很详细:
然后启动Kafka Server:
bin/kafka-server-start.sh config/server.properties
创建Topic
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
查看创建的Topic
bin/kafka-topics.sh --list --zookeeper localhost:2181
启动控制台Producer,向Kafka发送消息
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
This is a message
This is another message
启动控制台Consumer,消费刚刚发送的消息
bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning
This is a message
This is another message
删除Topic
bin/kafka-topics.sh --delete --zookeeper localhost:2181 --topic test
注:只有当delete.topic.enable=true时,该操作才有效
配置Kafka集群(单台机器上)
首先拷贝server.properties文件为多份(这里演示4个节点的Kafka集群,因此还需要拷贝3份配置文件):
cp config/server.properties config/server1.properties
cp config/server.properties config/server2.properties
cp config/server.properties config/server3.properties
修改server1.properties的以下内容:
broker.id=1
port=9093
log.dir=/tmp/kafka-logs-1
同理修改server2.properties和server3.properties的这些内容,并保持所有配置文件的zookeeper.connect属性都指向运行在本机的zookeeper地址localhost:2181。注意,由于这几个Kafka节点都将运行在同一台机器上,因此需要保证这几个值不同,这里以累加的方式处理。例如在server2.properties上:
broker.id=2
port=9094
log.dir=/tmp/kafka-logs-2
把server3.properties也配置好以后,依次启动这些节点:
bin/kafka-server-start.sh config/server1.properties &
bin/kafka-server-start.sh config/server2.properties &
bin/kafka-server-start.sh config/server3.properties &
Topic & Partition
Topic在逻辑上可以被认为是一个queue,每条消费都必须指定它的Topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对应一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件。
现在在Kafka集群上创建备份因子为3,分区数为4的Topic:
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 4 --topic kafka
说明:备份因子replication-factor越大,则说明集群容错性越强,就是当集群down掉后,数据恢复的可能性越大。所有的分区数里的内容共同组成了一份数据,分区数partions越大,则该topic的消息就越分散,集群中的消息分布就越均匀。
然后使用kafka-topics.sh的--describe参数查看一下Topic为kafka的详情:
输出的第一行是所有分区的概要,接下来的每一行是一个分区的描述。可以看到Topic为kafka的消息,PartionCount=4,ReplicationFactor=3正是我们创建时指定的分区数和备份因子。
另外:Leader是指负责这个分区所有读写的节点;Replicas是指这个分区所在的所有节点(不论它是否活着);ISR是Replicas的子集,代表存有这个分区信息而且当前活着的节点。
拿partition:0这个分区来说,该分区的Leader是server0,分布在id为0,1,2这三个节点上,而且这三个节点都活着。
再来看下Kafka集群的日志:
其中kafka-logs-0代表server0的日志,kafka-logs-1代表server1的日志,以此类推。
从上面的配置可知,id为0,1,2,3的节点分别对应server0, server1, server2, server3。而上例中的partition:0分布在id为0, 1, 2这三个节点上,因此可以在server0, server1, server2这三个节点上看到有kafka-0这个文件夹。这个kafka-0就代表Topic为kafka的partion0。
第二部分 Kafka+Log4j项目整合
先来看下Maven项目结构图:
pom.xml引入的jar包:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency> <dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.10.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.10</artifactId>
<version>0.10.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-log4j-appender</artifactId>
<version>0.10.2.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
</dependencies>
重要的内容是log4j.properties:
log4j.rootLogger=debug,Console # appender kafka
log4j.appender.kafka=org.apache.kafka.log4jappender.KafkaLog4jAppender
log4j.appender.kafka.topic=kafkaTest
log4j.appender.kafka.syncSend=false
# multiple brokers are separated by comma ",".
log4j.appender.kafka.brokerList=192.168.1.163:9092
log4j.appender.kafka.layout=org.apache.log4j.PatternLayout
log4j.appender.kafka.layout.ConversionPattern=%d [%-5p] [%t] - [%l] %m%n #输出日志到控制台
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Threshold=all
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%c\:%L]-[%p] %m%n #kafka
log4j.logger.com.demo.kafka.Log4jToKafka=info,kafka
#关闭spring低级别日志
log4j.logger.org.springside.examples.miniweb=ERROR
log4j.logger.com.octo.captcha.service.image.DefaultManageableImageCaptchaService=ERROR
log4j.logger.com.mchange.v2.resourcepool.BasicResourcePool=ERROR
log4j.logger.com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool=ERROR
log4j.logger.com.mchange.v2.c3p0.impl.NewPooledConnection=ERROR
log4j.logger.com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean=ERROR
log4j.logger.com.mchange.v2.c3p0.C3P0Registry=ERROR
log4j.logger.com.mchange.v2.log.MLog=ERROR
log4j.logger.com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource=ERROR
log4j输出日志:
package com.demo.kafka;
import org.apache.log4j.Logger; /**
* INFO: info User: xuchao Date: 2017/3/17 Version: 1.0 History:
* <p>
* 如果有修改过程,请记录
* </P>
*/ public class Log4jToKafka {
private static Logger logger = Logger.getLogger(Log4jToKafka.class); public static void main(String args[]) {
System.out.println("hello word!");
int start = 1;
while (true) {
start++;
logger.info(start + "hello Log4jToKafka test !");
try {
Thread.sleep(50l);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
消费kafka中的信息:
package com.demo.kafka; /**
* INFO: info
* User: zhaokai
* Date: 2017/3/17
* Version: 1.0
* History: <p>如果有修改过程,请记录</P>
*/ import java.util.Arrays;
import java.util.Properties; import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer; public class Consumer { public static void main(String[] args) {
System.out.println("begin consumer");
connectionKafka();
System.out.println("finish consumer");
} @SuppressWarnings("resource")
public static void connectionKafka() { Properties props = new Properties();
props.put("bootstrap.servers", "192.168.1.163:9092");
props.put("group.id", "testConsumer");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("session.timeout.ms", "30000");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("kafkaTest"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (ConsumerRecord<String, String> record : records) {
System.out.printf("===================offset = %d, key = %s, value = %s", record.offset(), record.key(),
record.value());
}
}
}
}
MyProducer.java用于向Kafka发送消息,但不通过log4j的appender发送。此案例中可以不要。但是我还是放在这里:
package com.demo.kafka; import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig; public class MyProducer {
private static final String TOPIC = "kafka";
private static final String CONTENT = "This is a single message";
private static final String BROKER_LIST = "localhost:9092";
private static final String SERIALIZER_CLASS = "kafka.serializer.StringEncoder"; public static void main(String[] args) {
Properties props = new Properties();
props.put("serializer.class", SERIALIZER_CLASS);
props.put("metadata.broker.list", BROKER_LIST); ProducerConfig config = new ProducerConfig(props);
Producer<String, String> producer = new Producer<String, String>(config); // Send one message.
KeyedMessage<String, String> message = new KeyedMessage<String, String>(TOPIC, CONTENT);
producer.send(message); // Send multiple messages.
List<KeyedMessage<String, String>> messages = new ArrayList<KeyedMessage<String, String>>();
for (int i = 0; i < 5; i++) {
messages.add(new KeyedMessage<String, String>(TOPIC, "Multiple message at a time. " + i));
}
producer.send(messages);
}
}
到这里,代码就结束了。
第三部分 运行与验证
先运行Consumer,使其处于监听状态。同时,还可以启动Kafka自带的ConsoleConsumer来验证是否跟Consumer的结果一致。最后运行Log4jToKafka.java。
先来看看Consumer的输出:
再来看看ConsoleConsumer的输出:
可以看到,尽管发往Kafka的消息去往了不同的地方,但是内容是一样的,而且一条也不少。最后再来看看Kafka的日志。
我们知道,Topic为kafka的消息有4个partion,从之前的截图可知这4个partion均匀分布在4个kafka节点上,于是我对每一个partion随机选取一个节点查看了日志内容。
上图中黄色选中部分依次代表在server0上查看partion0,在server1上查看partion1,以此类推。
而红色部分是日志内容,由于在创建Topic时准备将20条日志分成4个区存储,可以很清楚的看到,这20条日志确实是很均匀的存储在了几个partion上。
摘一点Infoq上的话:每个日志文件都是一个log entrie序列,每个log entrie包含一个4字节整型数值(值为N+5),1个字节的"magic value",4个字节的CRC校验码,其后跟N个字节的消息体。每条消息都有一个当前Partition下唯一的64字节的offset,它指明了这条消息的起始位置。磁盘上存储的消息格式如下:
message length : 4 bytes (value: 1+4+n)
"magic" value : 1 byte
crc : 4 bytes
payload : n bytes
这里我们看到的日志文件的每一行,就是一个log entrie,每一行前面无法显示的字符(蓝色选中部分),就是(message length + magic value + crc)了。而log entrie的后部分,则是消息体的内容了。
本文转自:https://my.oschina.net/itblog/blog/540918
使用Log4j将程序日志实时写入Kafka的更多相关文章
- 使用Log4j将程序日志实时写入Kafka(转)
原文链接:使用Log4j将程序日志实时写入Kafka 很多应用程序使用Log4j记录日志,如何使用Kafka实时的收集与存储这些Log4j产生的日志呢?一种方案是使用其他组件(比如Flume,或者自己 ...
- (一个)kafka-jstorm集群实时日志分析 它 ---------kafka实时日志处理
package com.doctor.logbackextend; import java.util.HashMap; import java.util.List; import java.util. ...
- java实时监听日志写入kafka(转)
原文链接:http://www.sjsjw.com/kf_cloud/article/020376ABA013802.asp 目的 实时监听某目录下的日志文件,如有新文件切换到新文件,并同步写入kaf ...
- java实时监听日志写入kafka(多目录)
目的 实时监听多个目录下的日志文件,如有新文件切换到新文件,并同步写入kafka,同时记录日志文件的行位置,以应对进程异常退出,能从上次的文件位置开始读取(考虑到效率,这里是每100条记一次,可调整) ...
- java实时监听日志写入kafka
目的 实时监听某目录下的日志文件,如有新文件切换到新文件,并同步写入kafka,同时记录日志文件的行位置,以应对进程异常退出,能从上次的文件位置开始读取(考虑到效率,这里是每100条记一次,可调整) ...
- 大数据学习day32-----spark12-----1. sparkstreaming(1.1简介,1.2 sparkstreaming入门程序(统计单词个数,updateStageByKey的用法,1.3 SparkStreaming整合Kafka,1.4 SparkStreaming获取KafkaRDD的偏移量,并将偏移量写入kafka中)
1. Spark Streaming 1.1 简介(来源:spark官网介绍) Spark Streaming是Spark Core API的扩展,其是支持可伸缩.高吞吐量.容错的实时数据流处理.Sp ...
- flume学习(三):flume将log4j日志数据写入到hdfs(转)
原文链接:flume学习(三):flume将log4j日志数据写入到hdfs 在第一篇文章中我们是将log4j的日志输出到了agent的日志文件当中.配置文件如下: tier1.sources=sou ...
- 【Python】Python日志无延迟实时写入
我在用python生成日志时,发现无论怎么flush(),文件内容总是不能实时写入,导致程序意外中断时一无所获. 以下是查到的解决方案(亲测可行): open 函数中有一个bufferin的参数,默认 ...
- weblogic开启http访问日志并实时写入日志文件
由于http访问会产生大量日志,耗去不少IO和CPU所以在生产一般是不启用的:但有时我们会想启用http访问日志,尤其是在系统上线调试的时候. weblogic的日志默认在domain_name/se ...
随机推荐
- Spring Boot 热部署的实现 - 原创
实现方式有两大种(其中包含3种): 一.基于springloaded 1.1)Maven启动方式 第一步:在pom.xml中的“plugin节点”里面添加如下依赖: <dependencies& ...
- Find Min In Rotated Sorted Array,寻找反转序列中最小的元素。
问题描述:寻找反转序列中最小的元素. 算法分析:和寻找某个数是一个道理,还是利用二分查找,总体上分两种情况.nums[left]<=nums[mid],else.但是,在截取子序列的时候,有可能 ...
- Pandas索引和选择数据
在本章中,我们将讨论如何切割和丢弃日期,并获取Pandas中大对象的子集. Python和NumPy索引运算符"[]"和属性运算符".". 可以在广泛的用例中快 ...
- 10 个深恶痛绝的 Java 异常
异常是 Java 程序中经常遇到的问题,我想每一个 Java 程序员都讨厌异常,一 个异常就是一个 BUG,就要花很多时间来定位异常问题. 今天,来列一下 Java 中经常遇到的前 10 个异常,排名 ...
- node 模块部分介绍
chai 断言框架 mocha mochawesome 对mocha 定制报告,生成完整成熟的报告. node-fetch 服务器版fetch superagent 是node 客户端请求代理 ...
- Http请求原理与相关知识
1.在浏览器地址栏输入URL,按回车后经过了哪些步骤 1-1. 浏览器向DNS服务器请求解析该URL中的域名及所对应的IP地址; 1-2. 解析出IP地址后,根据该IP地址和默认端口80与服务器建立 ...
- Spring获取bean的几种方式
工作中需要对一个原本加载属性文件的工具类修改成对数据库的操作当然,ado层已经写好,但是需要从Spring中获取bean,然而,工具类并没有交给Spring来管理,所以需要通过方法获取所需要的bean ...
- hdu4348区间更新的主席树+标记永久化
http://acm.hdu.edu.cn/showproblem.php?pid=4348 sb的标记永久化即可,刚开始add和sum没复制过来wa了两发...,操作和原来的都一样,出来单点变成区间 ...
- ARM体系结构总结
特殊功能寄存器与外设绑定,通用寄存器是与CPU绑定. ARM是RISC架构 常用ARM汇编指令只有二三十条 ARM是低功耗CPU ARM的架构非常适合单片机.嵌入式.尤其是物联网领域:而服务器等高性能 ...
- LeetCode OJ:Valid Anagram(有效字谜问题)
Given two strings s and t, write a function to determine if t is an anagram of s. For example,s = &q ...