Flume 自定义拦截器 多行读取日志+截断
前言:
Flume百度定义如下:
Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。
搭建并使用flume不是特别难,而且网上也有技术文章分享,我不再赘述了。本文主要建立在已经搭建并使用flume的情况。
业务场景:
flume读取日志是按行读取,无法进行多行读取,当出现如下日志时将无法读到日志的正确时间与类型信息,所以我们需要有一种可以多行读取日志信息的办法,这里采用自定义拦截器的方法:
1 2019-08-02 14:34:13.153 [DEBUG][tomcatThreadPool-7][com.xxx.xxx.xxx.xxx.web.CommonHandlerExceptionResolver] (CommonHandlerExceptionResolver.java:134) \n Exception:------------------------------------------------------------------\ncom.xxx.xxx.xx.exceptions.XxxException: \n### Error querying database. Cause: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (5638500 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.\n### The error may exist in com/xxx/xxx/xxx/basic/mapper/custom/CsWmFrtRateExtMapper.xml\n### The error may involve defaultParameterMap\n### The error occurred while setting parameters\n### SQL: SELECT * FROM ((SELECT cwfr.CS_WM_FRT_RATE_ID AS CS_WM_FRT_RATE_ID, cwfr.ACTIVE_DATE_BEGIN AS ACTIVE_DATE_BEGIN, cwfr.FRT_ITEM_CODE AS FRT_ITEM_CODE, cwfr.FRT_ITEM_NAME AS FRT_ITEM_NAME, cwfr.RP_FLAG AS RP_FLAG, cwfr.FRT_MODE AS FRT_MODE, cwfr.CALCULATION_ITEM AS CALCULATION_ITEM, cwfr.CHARGE_UOM_CODE AS CHARGE_UOM_CODE, cwfr.CHARGE_UOM_NAME AS
对于一些业务系统的日志可能会比较大,超1M,2M甚至更多,可以根据实际情况只截取前面一部分保留下来即可,为了让功能更具有灵活性,在实现上增加开关属性,默认打开着,不需要时设置关闭。
自定义拦截器实现的属性:过滤正则,截断标识(即开关),总截取最大长度,单个截取最大长度,最后一个事件流。最后一个事件流的作用保留下来与下一批次一起,按正则匹配后才发送出去,因为flume是按批次读取的,默认是100行,而这个配置又与flume运行内存有关系。这个是属于参数调优的话题。
特别注意:代码打包后是需要放到flume安装目录下的lib下。放进去后需要重新才会生效。
代码实现如下:
package org.apache.flume.custom; import com.google.common.collect.Lists;
import org.apache.commons.codec.Charsets;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern; /**
* 自定义拦截器 参考 Author: xiufen.huang Create Data: 2019/8/12 15:46
*/
public class MultInterceptor implements Interceptor { // 过滤正则
private static Pattern regex = null;
// 截取标志
private static Boolean cutFlag = true;
// 总截取最大长度
private static Integer cutMax = null;
// 单个截取最大长度
private static Integer singleCut = null;
// 最后一个事件流
private static List<Event> lastList = Lists.newArrayList(); @Override
public void initialize() { } @Override
public Event intercept(Event event) {
// System.out.println("----------intercept(Event event)方法执行,处理单个event");
return event;
} @Override
public List<Event> intercept(List<Event> list) {
// System.out.println("进来方法了吗?"); // 处理结果 event list
List<Event> intercepted = null; int addnum = 0;// 记录上一个正确匹配的event在队列中的位置,以便下一event有和它连接的需要 if (lastList != null && lastList.size() >0){
// 初始化
int initCapacity = list.size() + lastList.size();
intercepted = Lists.newArrayListWithCapacity(initCapacity);
// 添加
intercepted.addAll(lastList); // 清空
lastList = Lists.newArrayList();
}else {
intercepted = Lists.newArrayListWithCapacity(list.size());
} // 有正则的情况
for (int i = 0; i < list.size(); i++) {
Event interceptedEvent = null;
Matcher matcher = regex.matcher(new String(list.get(i).getBody(), Charsets.UTF_8));
if (matcher.find()) {
interceptedEvent = intercept((Event)list.get(i));
// 单个的body
String singleBody = new String(interceptedEvent.getBody(), Charsets.UTF_8);
int singleBodyLen = singleBody.length();
System.out.println("正则匹配-原始body---------:" + singleBody);
if (cutFlag) {
// 处理最大截取数边界条件--一定要重新一个变量接收
int lsSingleCut = singleCut > singleBodyLen ? singleBodyLen : singleCut;
// 截取字符串--新变量
String singleCutBody = new String(singleBody.substring(0, lsSingleCut)); System.out.println("单个截取-截取后body=============:" + singleCutBody);
// 重新赋值body
interceptedEvent.setBody(singleCutBody.getBytes());
} intercepted.add(interceptedEvent);
addnum = addnum +1;
// System.out.println("matcher.find() 下的:addnum:" + addnum);
} else {
if (intercepted.size() == 0) {
// 表示本次没有匹配上
continue;
} addnum = addnum >= intercepted.size() ? intercepted.size() - 1 : addnum; String body = new String(intercepted.get(addnum).getBody(), Charsets.UTF_8) + "\n"
+ new String(list.get(i).getBody(), Charsets.UTF_8); System.out.println("总截取-原始body---------:" + body);
int bodyLen = body.length();
// 截取body-新变量
String cutBody = body;
if (cutFlag) { // 处理最大截取数边界条件--新变量
int lsCutMax = cutMax > bodyLen ? bodyLen : cutMax;
// 截取字符串
cutBody = new String(body.substring(0, lsCutMax));
System.out.println("-处理截取-截取后body=============: " + body);
} intercepted.get(addnum).setBody(cutBody.getBytes());
}
} // 最后一个保存在静态变量,等待下一批次
if (intercepted != null && intercepted.size() > 0){
int lastIndex = intercepted.size() -1;
lastList.add(intercepted.get(lastIndex));
// 移除最后一个索引
intercepted.remove(lastIndex);
} return intercepted;
} @Override
public void close() {
System.out.println("----------自定义拦截器close方法执行");
} public static class Builder implements Interceptor.Builder {
@Override
public Interceptor build() {
System.out.println("----------build方法执行");
return new MultInterceptor();
} @Override
public void configure(Context context) {
String regexStr = context.getString("regex", null);
cutFlag = context.getBoolean("cutFlag", true);
cutMax = context.getInteger("cutMax", 0);
singleCut = context.getInteger("singleCut", 0);
System.out.println("参数regexStr:" + regexStr + ",参数cutMax: " + cutMax + ",cutFlag: " + cutFlag
+ " ,singleCut: " + singleCut); // 由于外面传过来的单位是kb,所以这边需要乘以1024
cutMax = cutMax * 1024;
System.out.println("总截取最大值:" + cutMax);
singleCut = singleCut * 1024;
System.out.println("单个截取最大值:" + singleCut); if (null != regexStr) {
// 转换正则
regex = Pattern.compile(regexStr);
} }
}
}
使用说明:
在flume启动配置文件增加以下内容:
#匹配时间并转换为时间戳到header中
a1.sources.tail.interceptors.i2.type=org.apache.flume.custom.MultInterceptor$Builder
#正则表达式,按需求定
a1.sources.tail.interceptors.i2.regex=(((?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)|([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29))
#开启日志长度截取标志,默认true,开启
a1.sources.tail.interceptors.i2.cutFlag = true
#最大截取字符串长度,整数,尽量控制在2M以内,单位:kb,1M=1024
a1.sources.tail.interceptors.i2.cutMax = 2048
#单个截取字符串长度,整数,尽量控制在1.5M以内,单位:kb,1M=1024
a1.sources.tail.interceptors.i2.singleCut=1024
a1.sources.tail.interceptors.i2.serializers=se1
a1.sources.tail.interceptors.i2.serializers.se1.type=org.apache.flume.interceptor.RegexExtractorInterceptorMillisSerializer
a1.sources.tail.interceptors.i2.serializers.se1.name=timestamp
a1.sources.tail.interceptors.i2.serializers.se1.pattern=yyyy-MM-dd
参考实现:
flume 自定义拦截器实现多行读取日志 https://blog.csdn.net/nougats/article/details/71188920
Flume 自定义拦截器 多行读取日志+截断的更多相关文章
- Flume自定义拦截器(Interceptors)或自带拦截器时的一些经验技巧总结(图文详解)
不多说,直接上干货! 一.自定义拦截器类型必须是:类全名$内部类名,其实就是内部类名称 如:zhouls.bigdata.MySearchAndReplaceInterceptor$Builder 二 ...
- Apache CXF自定义拦截器
为什么设计拦截器?1.为了在webservice请求过程中,能动态操作请求和响应数据,CXF设计了拦截器 拦截器分类: 1.按所处的位置分:服务器端拦截器,客户端拦截器. 2.按消息的方向分:入拦截器 ...
- 第1节 flume:15、flume案例二,通过自定义拦截器实现数据的脱敏
1.7.flume案例二 案例需求: 在数据采集之后,通过flume的拦截器,实现不需要的数据过滤掉,并将指定的第一个字段进行加密,加密之后再往hdfs上面保存 原始数据与处理之后的数据对比 图一 ...
- Hadoop生态圈-Flume的组件之自定义拦截器(interceptor)
Hadoop生态圈-Flume的组件之自定义拦截器(interceptor) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客只是举例了一个自定义拦截器的方法,测试字节传输速 ...
- Flume(二) —— 自定义拦截器、Source、Sink
自定义拦截器 自定义Source 自定义Sink 引入依赖 <dependency> <groupId>org.apache.flume</groupId> < ...
- flume【源码分析】分析Flume的拦截器
h2 { color: #fff; background-color: #7CCD7C; padding: 3px; margin: 10px 0px } h3 { color: #fff; back ...
- 从struts2拦截器到自定义拦截器
拦截器可谓struts2的核心了,最基本的bean的注入就是通过默认的拦截器实现的,一般在struts2.xml的配置中,package内直接或间接继承了struts-default.xml,这样st ...
- [ SSH框架 ] Struts2框架学习之四(自定义拦截器)
一.Struts2的拦截器 1.1 拦截器概述 拦截器,在AOP( Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作.拦截 ...
- Mybatis自定义拦截器与插件开发
在Spring中我们经常会使用到拦截器,在登录验证.日志记录.性能监控等场景中,通过使用拦截器允许我们在不改动业务代码的情况下,执行拦截器的方法来增强现有的逻辑.在mybatis中,同样也有这样的业务 ...
随机推荐
- Http协议 Content-Type
详情:https://www.cnblogs.com/ranyonsue/p/5984001.html *****Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面. ** ...
- 我的ubuntu源18和16
16阿里云的 deb cdrom:[Ubuntu 16.04 LTS _Xenial Xerus_ - Release amd64 (20160420.1)]/ xenial main restric ...
- xpath写法汇总(适用于appium、robotframework)
1.移动端应用,content-des包含“订单号” xpath=//android.view.View[contains(@content-desc,"订单号:")] 2.移动端 ...
- 理想乡题解 (线段树优化dp)
题面 思路概述 首先,不难想到本题可以用动态规划来解,这里就省略是如何想到动态规划的了. 转移方程 f[i]=min(f[j]+1)(max(i-m,0)<=j<i 且j符合士兵限定) 注 ...
- Potplay视频播放画面扭曲
Potplayer是一款非常好用的视频播放器,解码快,功耗低同时相对较好支持保真加速,但是,在使用过程中出现了如下的所谓“Bug” 经过摸索发现是播放器自动检测到此视频是360视频(不明觉厉,貌似需要 ...
- Docker底层架构之网络实现
前言 Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 vethpair). 基本原理 首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来 ...
- Spring boot 学习中代码遇到的几个问题
1.报一大段红错 此时对象还是能创建成功的,解决方案参考链接https://blog.csdn.net/wanglin199709/article/details/99121487 2.无法创建对象 ...
- Spring IoC 容器和 bean 对象
程序的耦合性: 耦合性(Coupling),又叫耦合度,是对模块间关联程度的度量.耦合的强弱取决于模块间接口的复杂性.调用模块的方式以及通过界面传送数据的多少.模块间的耦合度是指模块之间的依赖关系,包 ...
- A Simple Introduction To Computer Networking
Most networking discussions are a jumble of acronyms. Forget the configuration details - what are th ...
- 20194651—自动生成四则运算题第一版报告chris
1.需求分析: (1)自动生成四则运算算式(+ - * /),或两则运算(+ -). (2)剔除重复算式. (3)题目数量可定制. (4)相关参数可控制. (5)生成的运算题存储到外部文件中. 2 ...