前言:

  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. moco响应中文乱码

    moco版本为:moco-runner-standalone-0.11.1.jar 在一次使用moco框架的时候,浏览器查看响应时,发现返回来的中文是乱码. 按照网上的操作,在响应结果中加上heade ...

  2. Dart语言学习( 一) 为什么学习Dart?

    为什么学习Dart? Google及全球的其他开发者,使用 Dart 开发了一系列高质量. 关键的 iOS.Android 和 web 应用. Dart 非常适合移动和 web 应用的开发. 高效 D ...

  3. 【Docker】 windows10 docker 使用

    原文地址:https://www.aliyun.com/jiaocheng/872144.html 摘要:docker之前在Windows和MacOS上的版本都是通过Linux虚拟机的形式,而现在Do ...

  4. NOI2.5 1490:A Knight's Journey

    描述 Background The knight is getting bored of seeing the same black and white squares again and again ...

  5. Windows Server 2016 Active Directory 图文搭建指南

    1. 首先打开Manage --> Add Roles and Features 2. 点击Next 3. 不做修改,点击Next 4. 不做修改,点击Next 5. 选择Active Dire ...

  6. c#数字图像处理(十一)图像旋转

    如果平面上的点绕原点逆时针旋转θº,则其坐标变换公式为: x'=xcosθ+ysinθ   y=-xsinθ+ycosθ 其中,(x, y)为原图坐标,(x’, y’)为旋转后的坐标.它的逆变换公式为 ...

  7. 使用xpath总是找不到

    今天使用使用xpath,直接从网页上复制的 /html/body/div[3]/div[2]/div[2]/div[3]/table/tbody/tr[2]/td[3]/a 但是在代码中总是找不到文件 ...

  8. 表单提交,action 地址问题

    jsp 页面如下: <%@ page contentType="text/html;charset=UTF-8" language="java" %> ...

  9. win10搭建本地服务器(IIS)

    若想外网也可以访问使用NATAPP:https://natapp.cn/article/natapp_newbie 参考文章: https://segmentfault.com/a/119000001 ...

  10. qt QSplitter分割窗口

    #include <QApplication> #include <QFont> #include <QTextEdit> #include <QSplitt ...