前言:

  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. python 遍历文件夹下的所有文件

    基础 import os # 遍历文件夹 def walkFile(file): for root, dirs, files in os.walk(file): # root 表示当前正在访问的文件夹 ...

  2. AVR单片机教程——UART进阶

    本文隶属于AVR单片机教程系列.   在第一期中,我们已经开始使用UART来实现单片机开发板与计算机之间的通信,但只是简单地讲了讲一些概念和库函数的使用.在这一篇教程中,我们将从硬件与软件等各方面更深 ...

  3. Tomcat9乱码解决

    在tomcat的解压目录下找到conf,打开进入,logging.properties文件,在该文件中,修改 java.util.logging.ConsoleHandler.encoding = U ...

  4. 面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他

    初识 MyBatis MyBatis 是第一个支持自定义 SQL.存储过程和高级映射的类持久框架.MyBatis 消除了大部分 JDBC 的样板代码.手动设置参数以及检索结果.MyBatis 能够支持 ...

  5. 返回数据中提取数据的方法(JSON数据取其中某一个值的方法)

    返回数据中提取数据的方法 比如下面的案例是,取店铺名称 接口返回数据如下: {"Code":0,"Msg":"ok","Data& ...

  6. 关于STM32串口接收中断中只能接收一个字节

    最近调试STM32的串口接收时发现例程中只能接收一个字节 例程如下: //初始化串口1 void uart_init(u32 bound){ //GPIO端口设置 GPIO_InitTypeDef G ...

  7. Shell常用命令之echo

    echo 字符串的输出 选项 -n:不换行输出 -e:启用反斜杠转义符 -E:禁用反斜杠转义符 反斜杠转义符 \a:发出警告声 \b:删除前一个字符 \c:最后不加上换行符号 \f:换行但光标仍然停留 ...

  8. mongodb centos7 安装

    安装MongoDB的方法有很多种,可以源代码安装,在CentOS也可以用yum源安装的方法.由于MongoDB更新得比较快,我比较喜欢用yum源安装的方法.64位Centos下的安装步骤如下: 1.准 ...

  9. kubernetes secret 和 serviceaccount删除

    背景 今天通过配置创建了一个serviceaccounts和secret,后面由于某种原因想再次创建发现已存在一个serviceaccounts和rolebindings.rbac.authoriza ...

  10. 初识Redis,看这一篇就够了

    环境的搭建和安装网上有很多教程,在这里就不再重复了. 1. Redis是什么? Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写. ...