前言:

  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 自定义拦截器 多行读取日志+截断的更多相关文章

  1. Flume自定义拦截器(Interceptors)或自带拦截器时的一些经验技巧总结(图文详解)

    不多说,直接上干货! 一.自定义拦截器类型必须是:类全名$内部类名,其实就是内部类名称 如:zhouls.bigdata.MySearchAndReplaceInterceptor$Builder 二 ...

  2. Apache CXF自定义拦截器

    为什么设计拦截器?1.为了在webservice请求过程中,能动态操作请求和响应数据,CXF设计了拦截器 拦截器分类: 1.按所处的位置分:服务器端拦截器,客户端拦截器. 2.按消息的方向分:入拦截器 ...

  3. 第1节 flume:15、flume案例二,通过自定义拦截器实现数据的脱敏

    1.7.flume案例二 案例需求: 在数据采集之后,通过flume的拦截器,实现不需要的数据过滤掉,并将指定的第一个字段进行加密,加密之后再往hdfs上面保存 原始数据与处理之后的数据对比 图一  ...

  4. Hadoop生态圈-Flume的组件之自定义拦截器(interceptor)

    Hadoop生态圈-Flume的组件之自定义拦截器(interceptor) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客只是举例了一个自定义拦截器的方法,测试字节传输速 ...

  5. Flume(二) —— 自定义拦截器、Source、Sink

    自定义拦截器 自定义Source 自定义Sink 引入依赖 <dependency> <groupId>org.apache.flume</groupId> < ...

  6. flume【源码分析】分析Flume的拦截器

    h2 { color: #fff; background-color: #7CCD7C; padding: 3px; margin: 10px 0px } h3 { color: #fff; back ...

  7. 从struts2拦截器到自定义拦截器

    拦截器可谓struts2的核心了,最基本的bean的注入就是通过默认的拦截器实现的,一般在struts2.xml的配置中,package内直接或间接继承了struts-default.xml,这样st ...

  8. [ SSH框架 ] Struts2框架学习之四(自定义拦截器)

    一.Struts2的拦截器 1.1 拦截器概述 拦截器,在AOP( Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作.拦截 ...

  9. Mybatis自定义拦截器与插件开发

    在Spring中我们经常会使用到拦截器,在登录验证.日志记录.性能监控等场景中,通过使用拦截器允许我们在不改动业务代码的情况下,执行拦截器的方法来增强现有的逻辑.在mybatis中,同样也有这样的业务 ...

随机推荐

  1. synchronized的使用

    概念: 是利用锁的机制来实现同步的. 锁机制有如下两种特性: 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操 ...

  2. [hdu2255] 奔小康赚大钱

    Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有 \(n\) 间房间,刚好有 \(n\) 家 ...

  3. Beat our dice game and get the flag 击败我们的骰子游戏拿到旗子

    文件名:ebCTF-Teaser-BIN100-Dice.exe 话不多说 用PEID一看没壳 拖进OD 让我们摇出31337这五个数字才能拿到正确的flag cmp dword ptr ss:[eb ...

  4. 2、pycharm安装及相关配置

    PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试. 语法高亮.Project管理.代码跳转.智能提示.自动完成.单元测试.版本控制 ...

  5. LOJ6053 简单的函数

    题目传送门 分析: 对于这道题来说,当\(x\)为质数时: \(~~~~f(x)=x-1+2[x=2]\) 因为除2以外的质数都是奇数,它们与1异或就是减一,然后2就是加一 然后我们先来康康怎么快速求 ...

  6. 团队第一次作业(By七个小矮人)

    一.团队简介 1.团队名称:七个小矮人 2.团队成员列表 201731024137 马驰(队长) 201731021227 于丁 201731024114 杨汶桐 201731024125 李朋珂 2 ...

  7. Java多态之Father f=new Son();

    成员变量静态方法看左边,非静态方法编译看左边,运行看右边. 左边Father f其实是定义了一个Father类的对象,而右边new Son()可以只理解为是一个重写了Father类方法的对象. 因此, ...

  8. 虚拟环境vitualenv的使用

    在使用 Python 开发的过程中,工程一多,难免会碰到不同的工程依赖不同版本的库的问题: 亦或者是在开发过程中不想让物理环境里充斥各种各样的库,引发未来的依赖灾难. 此时,我们需要对于不同的工程使用 ...

  9. JDK源码系列总索引

    一 目标 记录学习jdk源码的一些笔记和心得,jdk版本使用11.0.1,工具idea Class后面序号为优先级1-4,优先级递减 目录转载自博客: https://blog.csdn.net/qq ...

  10. 解决IDEA使用lombok注解无效,@Data不生效问题

    在settings设置启用注解即可: