用Hbase存储Log4j日志数据:HbaseAppender
业务需求:
需求很简单,就是把多个系统的日志数据统一存储到Hbase数据库中,方便统一查看和监控。
解决思路:
写针对Hbase存储的Log4j Appender,有一个简单的日志储存策略,把Log4j的存储和Hbase的存储分开进行,当到达一定量的时候批量写入Hbase。
Log4j的日志暂时存到一个队列,启动一个计划任务定时检查是否到达指定的量级,到达后批量写入Hbase将队列清空。
带来一个问题是在Log4j最后一次的数据可能未达到量级程序关闭而丢失,所以如果日志非常重要的话请同时开启文件存储!
具体代码
代码部分略掉所有import,请自行导入。
log4j.properties
log4j.rootLogger=INFO,HbaseAppender
#HbaseAppender
log4j.appender.HbaseAppender=cn.bg.log.HbaseAppender
log4j.appender.HbaseAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.HbaseAppender.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
HbaseAppender
package cn.bg.log;
public class HbaseAppender extends AppenderSkeleton implements Runnable {
private int batchSize = 10;
private int period = 1000;
private String hbLogName = "test";
private String hbLogFamily = "bg";
private int hbPools = 2;
private Queue<LoggingEvent> loggingEvents;
private ScheduledExecutorService executor;
private ScheduledFuture<?> task;
private Configuration conf;
private HTablePool hTablePool;
private HTableInterface htable;
/**
* log4j初始设置,启动日志处理计划任务
*/
@Override
public void activateOptions() {
try {
super.activateOptions();
//创建一个计划任务,并自定义线程名
executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("HbaseAppender"));
//日志队列
loggingEvents = new ConcurrentLinkedQueue<LoggingEvent>();
//启动计划任务,如果run函数有异常任务将中断!
task = executor.scheduleWithFixedDelay(this, period, period, TimeUnit.MILLISECONDS);
System.out.println("ActivateOptions ok!");
} catch (Exception e) {
System.err.println("Error during activateOptions: " + e);
}
}
/**
* 初始HBASE
*
* @return
*/
private boolean initHbase() {
try {
if (conf == null) {
//根据classpath下hbase-site.xml创建hbase连接,基于zookeeper
conf = HBaseConfiguration.create();
//htable链接池
hTablePool = new HTablePool(conf, hbPools);
htable = hTablePool.getTable(hbLogName);
System.out.println("Init Hbase OK!");
}
return true;
} catch (Exception e) {
task.cancel(false);
executor.shutdown();
System.err.println("Init Hbase fail !");
return false;
}
}
@Override
public void run() {
if (conf == null || htable == null) {
initHbase();
}
try {
//日志数据超出批量处理大小
if (batchSize <= loggingEvents.size()) {
LoggingEvent event;
List<Put> logs = new ArrayList<Put>();
//循环处理日志队列
while ((event = loggingEvents.poll()) != null) {
try {
//创建日志并指定ROW KEY
Put log = new Put((event.getThreadName() + event.getLevel().toString() + System.currentTimeMillis()).getBytes());
//写日志内容
log.add(hbLogFamily.getBytes(), "log".getBytes(), layout.format(event).getBytes());
logs.add(log);
} catch (Exception e) {
System.err.println("Error logging put " + e);
}
}
//批量写入HBASE
if (logs.size() > 0) htable.put(logs);
}
} catch (Exception e) {
System.err.println("Error run " + e);
}
}
/**
* 日志事件
*
* @param loggingEvent
*/
@Override
protected void append(LoggingEvent loggingEvent) {
try {
populateEvent(loggingEvent);
//添加到日志队列
loggingEvents.add(loggingEvent);
} catch (Exception e) {
System.err.println("Error populating event and adding to queue" + e);
}
}
/**
* 事件测试
*
* @param event
*/
protected void populateEvent(LoggingEvent event) {
event.getThreadName();
event.getRenderedMessage();
event.getNDC();
event.getMDCCopy();
event.getThrowableStrRep();
event.getLocationInformation();
}
@Override
public void close() {
try {
task.cancel(false);
executor.shutdown();
hTablePool.close();
htable.close();
} catch (IOException e) {
System.err.println("Error close " + e);
}
}
@Override
public boolean requiresLayout() {
return true;
}
//设置每一批日志处理数量
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
/**
* 设置计划任务执行间隔
*
* @param period
*/
public void setPeriod(int period) {
this.period = period;
}
/**
* 设置日志存储HBASE表名
*
* @param hbLogName
*/
public void setHbLogName(String hbLogName) {
this.hbLogName = hbLogName;
}
/**
* 日志表的列族名字
* @param hbLogFamily
*/
public void setHbLogFamily(String hbLogFamily) {
this.hbLogFamily = hbLogFamily;
}
}
NamedThreadFactory
package cn.bg.log;
public class NamedThreadFactory implements ThreadFactory {
private final String prefix;
private final ThreadFactory threadFactory;
private final AtomicInteger atomicInteger = new AtomicInteger();
public NamedThreadFactory(final String prefix){
this(prefix, Executors.defaultThreadFactory());
}
public NamedThreadFactory(final String prefix, final ThreadFactory threadFactory){
this.prefix = prefix;
this.threadFactory = threadFactory;
}
@Override
public Thread newThread(Runnable r) {
Thread t = this.threadFactory.newThread(r);
t.setName(this.prefix + this.atomicInteger.incrementAndGet());
return t;
}
}
完!
用Hbase存储Log4j日志数据:HbaseAppender的更多相关文章
- flume学习(三):flume将log4j日志数据写入到hdfs(转)
原文链接:flume学习(三):flume将log4j日志数据写入到hdfs 在第一篇文章中我们是将log4j的日志输出到了agent的日志文件当中.配置文件如下: tier1.sources=sou ...
- HBase存储剖析与数据迁移
1.概述 HBase的存储结构和关系型数据库不一样,HBase面向半结构化数据进行存储.所以,对于结构化的SQL语言查询,HBase自身并没有接口支持.在大数据应用中,虽然也有SQL查询引擎可以查询H ...
- NoSql存储日志数据之Spring+Logback+Hbase深度集成
NoSql存储日志数据之Spring+Logback+Hbase深度集成 关键词:nosql, spring logback, logback hbase appender 技术框架:spring-d ...
- 应用Flume+HBase采集和存储日志数据
1. 在本方案中,我们要将数据存储到HBase中,所以使用flume中提供的hbase sink,同时,为了清洗转换日志数据,我们实现自己的AsyncHbaseEventSerializer. pac ...
- 分布式爬虫系统设计、实现与实战:爬取京东、苏宁易购全网手机商品数据+MySQL、HBase存储
http://blog.51cto.com/xpleaf/2093952 1 概述 在不用爬虫框架的情况,经过多方学习,尝试实现了一个分布式爬虫系统,并且可以将数据保存到不同地方,类似MySQL.HB ...
- MongoDB应用案例:使用 MongoDB 存储日志数据
线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息,通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题,但当产生大量的日志之后,要想从大量日志里挖 ...
- 使用 MongoDB 存储日志数据
使用 MongoDB 存储日志数据 线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息.通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题 ...
- MongoDB 存储日志数据
MongoDB 存储日志数据 https://www.cnblogs.com/nongchaoer/archive/2017/01/11/6274242.html 线上运行的服务会产生大量的运行及访问 ...
- 一次flume exec source采集日志到kafka因为单条日志数据非常大同步失败的踩坑带来的思考
本次遇到的问题描述,日志采集同步时,当单条日志(日志文件中一行日志)超过2M大小,数据无法采集同步到kafka,分析后,共踩到如下几个坑.1.flume采集时,通过shell+EXEC(tail -F ...
随机推荐
- 关于mac book 的启动选项
苹果电脑是用EFI的,没有BIOS,唯一可以设置的是开机的启动选项.可以在基于 Intel 的 Mac 电脑上使用下列启动键组合启动时按住 C 键——从可启动 CD 或 DVD 光盘启动,如随机附带的 ...
- Linux kernel的 Makefile和Kconfig以及Make menuconfig的关系【转】
本文转载自:http://blog.sina.com.cn/s/blog_4ba5b45e0102e6vp.html 熟悉内核的Makefile对开发设备驱动.理解内核代码结构都是非常重要的linux ...
- 161124、Java 异常处理的误区和经验总结
本文着重介绍了 Java 异常选择和使用中的一些误区,希望各位读者能够熟练掌握异常处理的一些注意点和原则,注意总结和归纳.只有处理好了异常,才能提升开发人员的基本素养,提高系统的健壮性,提升用户体验, ...
- React笔记_(7)_react路由
路由 路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程. 路由器当然是作为一个转发设备出现的,主要是转发数据包来实现网络互联. 那么react的路由到底指的是什么呢? 举个 ...
- mysql多实例部署
mysql 多实例常规来讲,主要有二种方案可以实现,这二种方案各有利弊,如下:1.基于多配置文件 通过使用多个配置文件来启动不同的进程,以此来实现多实例. 优点:逻辑简单,配置简单 缺点:管理起来不方 ...
- 【原创】纯干货,Spring-data-jpa详解,全方位介绍。
本篇进行Spring-data-jpa的介绍,几乎涵盖该框架的所有方面,在日常的开发当中,基本上能满足所有需求.这里不讲解JPA和Spring-data-jpa单独使用,所有的内容都是在和Spring ...
- 转:redis常用命令
一 Redis介绍 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发 ...
- ExtJS4.2 根据数据库记录构建树形菜单
背景:最近用ExtJS4.2做一个系统,需要在前端展示资源菜单,为树形结构,该树形结构是从数据库动态加载的. ExtJS的树形结构大致有两种情况: 1.静态树形结构,此处不多说,看API就能简单明白: ...
- Linux设置FQDN
FQDN是Fully Qualified Domain Name的缩写, 含义是完整的域名. 例如, 一台机器主机名(hostname)是www, 域后缀(domain)是example.com, 那 ...
- Keepalived原理及配置应用总结
Keepalived——保持存活,在网络里的含义就是保持在线.Keepalived提供高可用和热备的功能,用来防止单点故障的发生. 1.VRRP协议基本原理介绍 Keepalived实现的基础是VRR ...