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中,同样也有这样的业务 ...
随机推荐
- C++ vector对象是如何增长的
为了支持快速随机访问,vector将元素连续存储---每个元素紧挨着前一个元素存储. 如果没有空间容纳新元素: 容器必须分配新的内存空间来保存已有元素和新元素,将已有元素从旧位置移动到新空间中,然后添 ...
- 分析一下 原型模式的 UML 类图 。 复制对象, 深浅拷贝 月经贴 ,请回避
- 团队项目——Alpha1版本
团队项目-Alpha版本发布1 一.格式描述 这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/GeographicInformationScience/ ...
- Python学习,第二课 - 字符编码
关于字符编码 python解释器在加载 .py 文件中的代码时,会对内容进行编码(默认ascill) ASCII(American Standard Code for Information Inte ...
- Java 8 Stream流编程学习
本文是自己学习菜鸟教程中总结的笔记,用于快速找代码,完整的文档见菜鸟教程:Java 8 Stream Stream 使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达 ...
- python数据类型之字典操作
Python字典是另一种可变容器模型,且可存储任意类型对象,如字符串.数字.元组等其他容器模型.一.创建字典字典由key和value成对组成.基本语法如下: infos = {"name&q ...
- 文艺平衡树(区间splay)
文艺平衡树(luogu) Description 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列. 其中需要提供以下操作:翻转一个区间,例如原有序序列是 5\ 4\ 3\ 2\ ...
- ThreeJS 物理材质shader源码分析(顶点着色器)
再此之前推荐一款GLTF物理材质在线编辑器https://tinygltf.xyz/ ThreeJS 物理材质shader源码分析(顶点着色器) Threejs将shader代码分为ShaderLib ...
- docker扫盲,面试连这都不会就等着挂吧!
现在很多公司项目部署都是采用K8S docker容器方式,出门面试被问的概率极大,如果被面试官问docker相关知识点直接懵逼,那么基本就是被pass了,除非其他方面技术过硬.所以这种相对前沿的技术, ...
- CTF--HTTP服务--SQL注入-X-Forwarded-For报文头
开门见山 1. 扫描靶场ip,发现PCS 192.168.31.196 2. 扫描靶场开放服务信息 3. 扫描靶场全部信息 4. 探测敏感信息 5. 查看靶场80端口的主界面 6. 使用AVWS工具进 ...