• 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) 
     Exception:------------------------------------------------------------------
    com.xxx.xxx.xx.exceptions.XxxException: 
    ### 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.
    ### The error may exist in com/xxx/xxx/xxx/basic/mapper/custom/CsWmFrtRateExtMapper.xml
    ### The error may involve defaultParameterMap
    ### The error occurred while setting parameters
    ### 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下。放进去后需要重新才会生效。

    代码实现如下:

      1 package org.apache.flume.custom;
      2 
      3 import com.google.common.collect.Lists;
      4 import org.apache.commons.codec.Charsets;
      5 import org.apache.flume.Context;
      6 import org.apache.flume.Event;
      7 import org.apache.flume.interceptor.Interceptor;
      8 
      9 import java.util.List;
     10 import java.util.regex.Matcher;
     11 import java.util.regex.Pattern;
     12 
     13 /**
     14  * 自定义拦截器 参考 Author: xiufen.huang Create Data: 2019/8/12 15:46
     15  */
     16 public class MultInterceptor implements Interceptor {
     17 
     18     // 过滤正则
     19     private static Pattern regex = null;
     20     // 截取标志
     21     private static Boolean cutFlag = true;
     22     // 总截取最大长度
     23     private static Integer cutMax = null;
     24     // 单个截取最大长度
     25     private static Integer singleCut = null;
     26     // 最后一个事件流
     27     private static List<Event> lastList = Lists.newArrayList();
     28 
     29     @Override
     30     public void initialize() {
     31 
     32     }
     33 
     34     @Override
     35     public Event intercept(Event event) {
     36 //        System.out.println("----------intercept(Event event)方法执行,处理单个event");  
     37         return event;
     38     }
     39 
     40     @Override
     41     public List<Event> intercept(List<Event> list) {
     42 //        System.out.println("进来方法了吗?");
     43 
     44         // 处理结果 event list
     45         List<Event> intercepted = null;
     46 
     47         int addnum = 0;// 记录上一个正确匹配的event在队列中的位置,以便下一event有和它连接的需要
     48 
     49         if (lastList != null && lastList.size() >0){
     50             // 初始化
     51             int initCapacity = list.size() + lastList.size();
     52             intercepted = Lists.newArrayListWithCapacity(initCapacity);
     53             // 添加
     54             intercepted.addAll(lastList);
     55 
     56             // 清空
     57             lastList = Lists.newArrayList();
     58         }else {
     59             intercepted = Lists.newArrayListWithCapacity(list.size());
     60         }
     61 
     62         // 有正则的情况
     63         for (int i = 0; i < list.size(); i++) {
     64             Event interceptedEvent  = null;
     65             Matcher matcher = regex.matcher(new String(list.get(i).getBody(), Charsets.UTF_8));
     66             if (matcher.find()) {
     67                 interceptedEvent = intercept((Event)list.get(i));
     68                 // 单个的body
     69                 String singleBody = new String(interceptedEvent.getBody(), Charsets.UTF_8);
     70                 int singleBodyLen = singleBody.length();
     71                 System.out.println("正则匹配-原始body---------:" + singleBody);
     72                 if (cutFlag) {
     73                     // 处理最大截取数边界条件--一定要重新一个变量接收
     74                     int lsSingleCut = singleCut > singleBodyLen ? singleBodyLen : singleCut;
     75                     // 截取字符串--新变量
     76                     String singleCutBody = new String(singleBody.substring(0, lsSingleCut));
     77 
     78                     System.out.println("单个截取-截取后body=============:" + singleCutBody);
     79                     // 重新赋值body
     80                     interceptedEvent.setBody(singleCutBody.getBytes());
     81                 }
     82 
     83                 intercepted.add(interceptedEvent);
     84                 addnum = addnum +1;
     85 //                System.out.println("matcher.find() 下的:addnum:" + addnum);
     86             } else {
     87                 if (intercepted.size() == 0) {
     88                     // 表示本次没有匹配上
     89                     continue;
     90                 }
     91 
     92                  addnum = addnum >= intercepted.size() ? intercepted.size() - 1 : addnum;
     93 
     94 
     95                 String body = new String(intercepted.get(addnum).getBody(), Charsets.UTF_8) + "
    "
     96                         + new String(list.get(i).getBody(), Charsets.UTF_8);
     97 
     98                 System.out.println("总截取-原始body---------:" + body);
     99                 int bodyLen = body.length();
    100                 // 截取body-新变量
    101                 String cutBody = body;
    102                 if (cutFlag) {
    103 
    104                     // 处理最大截取数边界条件--新变量
    105                     int lsCutMax = cutMax > bodyLen ? bodyLen : cutMax;
    106                     // 截取字符串
    107                     cutBody = new String(body.substring(0, lsCutMax));
    108                     System.out.println("-处理截取-截取后body=============: " + body);
    109                 }
    110 
    111                 intercepted.get(addnum).setBody(cutBody.getBytes());
    112             }
    113         }
    114 
    115         // 最后一个保存在静态变量,等待下一批次
    116         if (intercepted != null && intercepted.size() > 0){
    117             int lastIndex = intercepted.size() -1;
    118             lastList.add(intercepted.get(lastIndex));
    119             // 移除最后一个索引
    120             intercepted.remove(lastIndex);
    121         }
    122 
    123         return intercepted;
    124     }
    125 
    126     @Override
    127     public void close() {
    128         System.out.println("----------自定义拦截器close方法执行");
    129     }
    130 
    131     public static class Builder implements Interceptor.Builder {
    132         @Override
    133         public Interceptor build() {
    134             System.out.println("----------build方法执行");
    135             return new MultInterceptor();
    136         }
    137 
    138         @Override
    139         public void configure(Context context) {
    140             String regexStr = context.getString("regex", null);
    141             cutFlag = context.getBoolean("cutFlag", true);
    142             cutMax = context.getInteger("cutMax", 0);
    143             singleCut = context.getInteger("singleCut", 0);
    144             System.out.println("参数regexStr:" + regexStr + ",参数cutMax: " + cutMax + ",cutFlag: " + cutFlag
    145                     + " ,singleCut: " + singleCut);
    146 
    147             // 由于外面传过来的单位是kb,所以这边需要乘以1024
    148             cutMax = cutMax * 1024;
    149             System.out.println("总截取最大值:" + cutMax);
    150             singleCut = singleCut * 1024;
    151             System.out.println("单个截取最大值:" + singleCut);
    152 
    153             if (null != regexStr) {
    154                 // 转换正则
    155                 regex = Pattern.compile(regexStr);
    156             }
    157 
    158         }
    159     }
    160 }
    View Code

    使用说明:

    在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

    感谢你的阅读,如果对你有帮助,请支持我!请点[推荐]
    如果有意见或建议,欢迎留言交流!
    欢迎转载,请保留出处,冰慧感谢你的关注与支持!
  • 相关阅读:
    精通正则表达式(JavaScript)
    Go知识点记录
    多线程揭秘
    Python test
    ELinq+T4模版引擎制作多文件实体代码生成器
    浏览器内核
    MongoDb的增删改查
    LINQ执行表达式
    ASP.NET MVC3 读书笔记四(数据注解和验证)
    C#默认以管理员身份运行程序
  • 原文地址:https://www.cnblogs.com/huangxiufen/p/12370044.html
Copyright © 2020-2023  润新知