因为项目需求,需要保存项目日志。项目的并发量不大,所以这里直接通过flume保存到oracle

源码地址:https://github.com/jaxlove/fks/tree/master/src/main/java/com

日志系统设置

  url:以select、save、update、remove开头。

  通过filter记录请求功的url。格式为json格式,字段包括channel(来源渠道web、wap、app等)、operate_type(操作类型)、first_model(菜单第一模块)、second_model(菜单第二模块)、data(url传递的参数)、ip(请求者ip)、account_id(用户账号id)、time(时间,有系统自动生成),url(请求的url地址)、remark(自定义备注)

  表结构相同。

flume配置:

  由于flume没有直接sink到oracle的jar包,这里自己自定义sink,偷懒,直接通过mybatis保存到数据库。。。

  flume在conf里配置设置

a1.sinks.k1.type = com.myflume.OracleSink
a1.sinks.k1.jdbc_url = jdbc:oracle:thin:@ip:port:实例名
a1.sinks.k1.jdbc_username = username
a1.sinks.k1.jdbc_password = password
#设置多少跳数据提交一次。数据量大,数据精度要求不高可以设置高一点
a1.sinks.k1.jdbc_batchsize = 5
#需要保存的表名
a1.sinks.k1.jdbc_tablename =tablename

java代码的实现说明:

1、获取日志的 { 与 } 之间的数据,将其转为json。

2、json的key必须和table的字段相同。只有这样才能保存,否则该字段不会入库。

3、由于java无法识别日志过多的数据格式,所以只能保存数字与字符串类型。同样数据也必须设置为相同类型。否则会报错。

以下是代码:

com.myflume.OracleSink
package com.myflume;

import com.common.SpringContextHolder;
import com.service.LogInfoService;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.flume.*;
import org.apache.flume.conf.Configurable;
import org.apache.flume.sink.AbstractSink;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.ArrayList;
import java.util.List;
import java.util.Map; /**
* 自定义sink
*
* @author wdj on 2018/6/8
*/
public class OracleSink extends AbstractSink implements Configurable{ private Logger logger = LoggerFactory.getLogger(getClass()); private Integer tryCount = 0; //MAX_TRY_COUNT 次尝试提交之后若数据个数还未达到batchSize,则试着提交
private final Integer MAX_TRY_COUNT = 2; private String jdbcurl;
private String username;
private String password;
private Integer batchSize;
private String tablename;
private DataSource dataSource;
LogInfoService logInfoService;
private List<Map<String,Object>> datas = new ArrayList<>(); // 获取flume的配置参数
@Override
public void configure(Context context) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
new String[] { "classpath:spring-context.xml" });
applicationContext.start();
    //通过spring管理bean
logInfoService = SpringContextHolder.getBean("logInfoService");
dataSource = SpringContextHolder.getBean("dataSource");
jdbcurl=context.getString("jdbc_url");
username=context.getString("jdbc_username");
password=context.getString("jdbc_password");
batchSize = context.getInteger("jdbc_batchsize",10);
tablename = context.getString("jdbc_tablename");
logger.info("初始化数据 ==== tablename:"+tablename+";jdbcurl:"+jdbcurl+";username:"+username+";batchSize"+batchSize);
} // Initialize the connection to the external repository (e.g. HDFS) that
// this Sink will forward Events to
@Override
public synchronized void start() {
if(!StringUtils.isBlank(jdbcurl) && !StringUtils.isBlank(username) && !StringUtils.isBlank(password)){
dataSource = new DataSource();
dataSource.setUrl(jdbcurl);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setInitialSize(5);
dataSource.setMaxActive(20);
dataSource.setMinIdle(5);
dataSource.setMaxIdle(20);
dataSource.setMaxWait(30000);
}
} // Disconnect from the external respository and do any
// additional cleanup
@Override
public synchronized void stop() {
logger.info("sink关闭。。。。。。。。保存缓存中的剩余数据");
if(datas != null && !datas.isEmpty()){
logInfoService.save(tablename,datas);
logger.info("提交"+datas.size()+"条数据");
}
dataSource.close();
super.stop();
} @Override
public Status process() throws EventDeliveryException {
Status status = null;
// Start transaction
Channel ch = getChannel();
Transaction txn = ch.getTransaction();
txn.begin();
try {
if(StringUtils.isBlank(tablename)){
throw new Exception("tablename不能为空!");
}
// This try clause includes whatever Channel operations you want to do
long processedEvent = 0;
for (; processedEvent < batchSize; processedEvent++) {
Event event = ch.take();
byte[] eventBody;
if(event != null){
eventBody = event.getBody();
String line= new String(eventBody,"UTF-8");
if (line.length() > 0 ){
int start = line.indexOf('{');
int end = line.lastIndexOf('}');
if(start != -1 && end!= -1){
String dataStr = line.substring(start,end+1);
Map<String,Object> map = JSONObject.fromObject(dataStr);
datas.add(map);
}
}
}else{
logger.info("even为空,回退。。。");
status = Status.BACKOFF;
break;
}
}
boolean canCommit = (status != Status.BACKOFF && datas!=null && !datas.isEmpty())
|| (tryCount >= MAX_TRY_COUNT && datas!=null && !datas.isEmpty());
// 将数据复制到临时变量,将data去空,当时若flume在datas浮空后未保存数据就关闭,则还是会丢失一部分数据
List<Map<String,Object>> tem = new ArrayList<>();
tem.addAll(datas);
datas = new ArrayList<>();
if(canCommit){
logInfoService.save(tablename,tem);
logger.info("提交"+datas.size()+"条数据");
status = Status.READY;
tryCount=0;
txn.commit();
}else if(status == Status.BACKOFF){
txn.rollback();
tryCount++;
}else{
logger.info("数据为空!");
status = Status.BACKOFF;
txn.rollback();
tryCount=0;
}
} catch (Exception e) {
txn.rollback();
// Log exception, handle individual exceptions as needed
logger.error("保存数据出错:",e);
status = Status.BACKOFF;
}
txn.close();
return status;
} public static void main(String[] args){
OracleSink oracleSink = new OracleSink();
oracleSink.configure(null);
oracleSink.start();
try {
oracleSink.process();
} catch (EventDeliveryException e) {
e.printStackTrace();
} }
}
com.service.LogInfoService
package com.service;

import com.dao.LogInfoDao;
import com.entity.ColumnDataBean;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service; import javax.annotation.Resource;
import java.util.*; /**
* description
*
* @author wdj on 2018/6/9
*/
@Service
public class LogInfoService { @Resource
LogInfoDao logInfoDao; public void save(String tablename,List<Map<String,Object>> datas){
//除了id所有列
List<Map<String,String>> columnList = logInfoDao.getColumn(tablename.toUpperCase());
//使用linkedHashMap保存原有的顺序
Map<String,String> columns = new LinkedHashMap();
for (Map<String, String> stringStringMap : columnList) {
columns.put(stringStringMap.get("COLUMN_NAME"),getJdbcType(stringStringMap.get("DATA_TYPE")));
}
List<Map> dataMap = new ArrayList<>();
for (Map<String, Object> data : datas) {
data =transformUpperCase(data);
Map map = new LinkedHashMap();
for (String s : columns.keySet()) {
ColumnDataBean dataBean = new ColumnDataBean();
dataBean.setValue(data.get(s));
dataBean.setType(columns.get(s));
//保存字段值,及字段类型
map.put(s,dataBean);
}
dataMap.add(map);
}
logInfoDao.save(tablename,dataMap);
} /**
* 将map的key转为大写
* @param orgMap
* @return
*/
public Map<String, Object> transformUpperCase(Map<String, Object> orgMap) {
Map<String, Object> resultMap = new HashMap<>(); if (orgMap == null || orgMap.isEmpty()) {
return resultMap;
} Set<String> keySet = orgMap.keySet();
for (String key : keySet) {
String newKey = key.toUpperCase(); resultMap.put(newKey, orgMap.get(key));
}
return resultMap;
} /**
* 根据数据库类型,获取jdbcType,粗略版
* @param dataSourceType
* @return
*/
public String getJdbcType(String dataSourceType){
if(StringUtils.isBlank(dataSourceType)){
return "VARCHAR";//默认字符串
}else if(dataSourceType.indexOf("TIMESTAMP")>-1){
return "TIMESTAMP";
}else if(dataSourceType.indexOf("CHAR")>-1){
return "VARCHAR";
}else if(dataSourceType.indexOf("NUMBER")>-1){
return "NUMERIC";
}else{
return "VARCHAR";
}
} }
ColumnDataBean就俩个参数,private Object value;private String type;不粘代码了。(PS一下,本来打算直接用map的。但是在dao的save方法里,通过c[VALUE]和c[KEY]只能获取map中固定的一个,不知道是为什么)

dao实现的xml

<mapper namespace="com.dao.LogInfoDao">

    <select id="getColumn" resultType="map">
select COLUMN_NAME,DATA_TYPE from USER_TAB_COLUMNS WHERE TABLE_NAME=#{tablename} and COLUMN_NAME !='ID'
</select> <insert id="save">
insert into ${tablename}
select * from
<foreach collection="data" item="d" open="(" close=")" separator="union all">
select sys_guid(),
<foreach collection="d" index="k" item="c" separator=",">
#{c.value,jdbcType=${c.type}} as ${k}
</foreach>
from dual
</foreach>
</insert> </mapper>

over!byebye,继续努力!


 

flume收集日志直接sink到oracle数据库的更多相关文章

  1. 配置好Nginx后,通过flume收集日志到hdfs(记得生成本地log时,不要生成一个文件,)

    生成本地log最好生成多个文件放在一个文件夹里,特别多的时候一个小时一个文件 配置好Nginx后,通过flume收集日志到hdfs 可参考flume的文件 用flume的案例二 执行的注意点 avro ...

  2. ELK收集日志到mysql数据库

    场景需求 在使用ELK对日志进行收集的时候,如果需要对数据进行存档,可以考虑使用数据库的方式.为了便于查询,可以同时写一份数据到Elasticsearch 中. 环境准备 CentOS7系统: 192 ...

  3. nginx日志切割并使用flume-ng收集日志

    nginx的日志文件没有rotate功能.如果你不处理,日志文件将变得越来越大,还好我们可以写一个nginx日志切割脚本来自动切割日志文件.第一步就是重命名日志文件,不用担心重命名后nginx找不到日 ...

  4. Oracle数据库的后备和恢复————关于检查点的一些知识

    当我们使用一个数据库时,总希望数据库的内容是可靠的.正确的,但由于计算机系统的故障(硬件故障.软件故障.网络故障.进程故障和系统故障)影响数据库系统的操作,影响数据库中数据的正确性,甚至破坏数据库,使 ...

  5. ELK收集日志到mysql

    场景需求 在使用ELK对日志进行收集的时候,如果需要对数据进行存档,可以考虑使用数据库的方式.为了便于查询,可以同时写一份数据到Elasticsearch 中. 环境准备 CentOS7系统: 192 ...

  6. Oracle数据库的入门之一

    Oracle的介绍: Oracle Database,又名Oracle RDBMS,或简称Oracle.是甲骨文公司的一款关系数据库管理系统.它是在数据库领域一直处于领先地位的产品.可以说Oracle ...

  7. nbu异地备份实施前,数据收集日志

    1.修改bp.conf配置文件显示重删率 BPDBJOBS_COLDEFS = JOBID 5 true BPDBJOBS_COLDEFS = TYPE 7 false BPDBJOBS_COLDEF ...

  8. Oracle 数据库备份实战

    最近公司的客户希望使用oracle数据库,所以我们只好将数据从mysql数据库迁移到oracle数据库,并对oracle数据库制定了一个备份策略,之前虽然对oracle很熟悉,但做备份策略还是第一次, ...

  9. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

随机推荐

  1. Golang 函数耗时统计

    当时候需要去计算一段代码或一个程序所消耗时间的时候,就需要进行统计时间,用程序去计算某一段代码的执行所需要的时间远比用眼睛直接去看程序运行时间高很多. go语言中的time包中提供了函数来提供计算消耗 ...

  2. Flutter移动电商实战 --(2)建立项目和编写入口文件

    1.创建项目 采用AndroidStudio构建本项目,FIle>New>New Flutter Project… 创建后的项目如下图所示: 我们着重需要注意一下几个文件夹,其他的暂时不用 ...

  3. [Java]使用Collections.Max,Min方法取集合类的最大最小值

    代码: package com.hy; import java.util.Arrays; import java.util.Collections; import java.util.List; pu ...

  4. Rvm 进行gem安装时必须输入密码Your user account isn't allowed to install to the system RubyGems 解决方案

    今天开发过程中,从master拉下代码后重启项目,想用控制台时,却发现需要密码??并且三次密码确认后还是疯狂报错. 当时第一想到是rvm版本不一致,随即则检查了版本跟gem生成,当确认rvm版本无误时 ...

  5. Selenium 2自动化测试实战40(单线程)

    单线程 #onethread.py #coding:utf-8 from time import sleep,ctime #听音乐任务 def music(): print('i was listen ...

  6. nodejs获取常见疾病数据示例

    日常生活中有一些常见的疾病,这个可以通过百度等搜索到,但是如果你要完成一款app或者小程序.网站之类的该如何来获取常见疾病的信息呢?首先想到的是通过爬虫爬取数据,然后整理搜索....其实这种方法还是太 ...

  7. 大数据HIve

    1. 题目说明 设计题:MySQL数据库A有1000w条数据,完成统计再输入到另外的B表中 A表 test1 0.2,3.5,1,1test1 1.2,2.3,4.56test2 2.1,0.3,9. ...

  8. JAVA 基础编程练习题14 【程序 14 求日期】

    14 [程序 14 求日期] 题目:输入某年某月某日,判断这一天是这一年的第几天? 程序分析:以 3 月 5 日为例,应该先把前两个月的加起来,然后再加上 5 天即本年的第几天,特殊情况, 闰年且输入 ...

  9. Cloudera Manager 常见异常

    1. Cloudera Management Service服务全部无法启动 观察到的现象: (1)cm service 组件无法启动,启动时服务请求超时终止:(2)主机信息也无法获取到,一直提示“无 ...

  10. lumen怎么得到当前Uri的控制器、Action、路由规则

    <?php namespace App\Http\Controllers; class HelloController extends Controller { public function ...