zoukankan      html  css  js  c++  java
  • 日志检索关键字并截取上下行关联内容

    一,需求说明

      我们有一个老系统,每天日志量挺大的(有可能是1G左右),比如要搜索某一个订单从生成到关闭完成,其中有很多步骤,而且有时候订单是批量操作的,如果某一个订单出现问题,从大量日志中排查的话,比较困难。1G的日志文件,我们拉下来取分析的话,电脑配置不行会很卡~而且为了找一个订单号要从头到尾,查找一遍,过程十分痛苦。

       所以,想到能不能把日志文件拆分部分来分析,当时使用了一下“文件杀手”这个工具,不过我的电脑配置比较差,虽然分成4小部分,依然觉得卡。后来想能不能根据原文件根据订单号把有关系的部分全部展示出来,中间一大批没关系的部分剔除掉就ok!

    二,实现思路

    首先,我们需要用一个固定大小的集合(队列)来存放我们需要的关键字上下相关部分的内容,特点:先进先出,保持存储的是关键字上下行内容;

    然后,定一个afterCount计数关键字后面紧跟的行数,行数达到阀值(关键字行处于队列中间位置)可以输出整个队列并清空;

    最后,有几种特殊情况,比如两个关键字行相邻比较近,做法是将前一个关键字在队列中填充空白元素行,使前一个关键字移动到队列中间并全部输出队列,后一个关键字继续放到队列中;

    三,代码部分

     固定长度队列

    package utils.queue;
    
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.Queue;
    
    /**
     * 固定长度队列
     *
     * @author gary
     */
    public class LimitQueue<E> implements Queue<E> {
        //队列长度
        private int limit;
    
        Queue<E> queue = new LinkedList<E>();
    
        public LimitQueue(int limit) {
            this.limit = limit;
        }
    
        /**
         * 入队
         *
         * @param e
         */
        @Override
        public boolean offer(E e) {
            if (queue.size() >= limit) {
                //如果超出长度,入队时,先出队
                queue.poll();
            }
            return queue.offer(e);
        }
    
        /**
         * 出队
         *
         * @return
         */
        @Override
        public E poll() {
            return queue.poll();
        }
    
        /**
         * 获取队列
         *
         * @return
         */
        public Queue<E> getQueue() {
            return queue;
        }
    
        /**
         * 获取限制大小
         *
         * @return
         */
        public int getLimit() {
            return limit;
        }
    
        @Override
        public boolean add(E e) {
            return queue.add(e);
        }
    
        @Override
        public E element() {    return queue.element();  }
    
        @Override
        public E peek() {
            return queue.peek();
        }
    
        @Override
        public boolean isEmpty() {
            return queue.size() == 0 ? true : false;
        }
    
        @Override
        public int size() {
            return queue.size();
        }
    
        @Override
        public E remove() {
            return queue.remove();
        }
    
        @Override
        public boolean addAll(Collection<? extends E> c) {
            return queue.addAll(c);
        }
    
        @Override
        public void clear() {
            queue.clear();
        }
    
        @Override
        public boolean contains(Object o) {
            return queue.contains(o);
        }
    
        @Override
        public boolean containsAll(Collection<?> c) {
            return queue.containsAll(c);
        }
    
        @Override
        public Iterator<E> iterator() {
            return queue.iterator();
        }
    
        @Override
        public boolean remove(Object o) {
            return queue.remove(o);
        }
    
        @Override
        public boolean removeAll(Collection<?> c) {
            return queue.removeAll(c);
        }
    
        @Override
        public boolean retainAll(Collection<?> c) {
            return queue.retainAll(c);
        }
    
        @Override
        public Object[] toArray() {
            return queue.toArray();
        }
    
        @Override
        public <T> T[] toArray(T[] a) {
            return queue.toArray(a);
        }
    
    }

    队列写入到新文件

     public static void writeDealInfo(String newFile, LimitQueue<String> allQueue) {
            try {
                FileWriter fw = new FileWriter(newFile, true);
                //队列已满 写入
                for (String item : allQueue) {
                    if (item != null) {
                        // writeDealInfo(item, newFile);  //遍历写入新文件
                        fw.write(item + "
    ");
                    }
                }
                allQueue.clear();     //达到lqueue的大小后,清空一下lqueue
    
                fw.close();
            } catch (Exception e) {
                System.out.println("书写日志发生错误:" + e.toString());
            }
        }

    具体逻辑

    public static void getFile(String srcFile, String newFile, Integer queueSize, String search) throws IOException {
    
            FileInputStream inputStream = new FileInputStream(srcFile);
            //设置inputStreamReader的构造方法并创建对象设置编码方式为gbk
            //BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "gbk"));
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
            String str = null;
            //新建一个指定大小的队列,用来存储指定
            int allQueueSize = queueSize * 2 + 1;
            LimitQueue<String> allQueue = new LimitQueue<>(allQueueSize);  //大的queue
    
            //计行器
            int afterCount = 0;         //用于统计关键字行后面紧跟的添加到queue中的行数,
            boolean hasKey = false;     //默认队列中没有关键字的行
    
            while ((str = bufferedReader.readLine()) != null) {
    
                allQueue.offer(str);  //前置将当前行加入queue,(这里不用管前面有多少个只用管关键字后面是否满足20个)
    
                //1.判断是否包含关键字,
                if (str.indexOf(search) > -1) {
                    // 这里存在可能将前面一大堆都输出情况,比如队列是2*20+1,如果队列前40个都没有关键字,41个有,此时afterCount为0,强行塞入20个空串到队列中来保持前后20行数据
                    //这里要将队列中之前存在的关键字行前后的数据输出
                    if (hasKey) {
                        if (afterCount < queueSize) {  //如果队列中关键字后面行数不够,添加空行
                            addBlank(allQueue, (queueSize - afterCount));
                        }
                        //输出大队列  
                        writeDealInfo(newFile, allQueue);                
                    }
    
                    hasKey = true;
                    afterCount = 0;//归0
    
                } else {   //不包含关键字
                    if (hasKey) {  //如果队列中有关键字才能计数加1
                        afterCount++;
                    }
                }
    
    
                //判断afterCount是否=20,输出
                if (afterCount == queueSize) {                 
                    writeDealInfo(newFile, allQueue); //输出大队列 
                    
                    afterCount = 0;//归0
                    hasKey = false;
                }
            }
    
           //关流
            inputStream.close();
            bufferedReader.close();
        }
    
    
        //给队列添加count个空串元素,直到afterCount=阀值
        private static void addBlank(LimitQueue<String> allQueue, int n) {
            while (n > 0) {
                allQueue.add(null);
                n--;
            }
        }

    测试代码

     @Test
        public void test1() throws IOException {
    
            String srcFile = "TestLog.txt";  //原日志文件
            String newFile = "TestLogNew.txt"; //搜索出来的关键字行
    
            Integer queueSize = 3;  //表示截取 搜索的关键字-前后3行-的文本信息
            String search = "AA";  //关键字
            getFile(srcFile, newFile, queueSize, search);
        }

    日志文本样式 TestLog.txt红色部分是需要截取出来的关键字行)

    1
    2
    3
    AA
    5
    6
    AA
    8
    9
    10
    11
    12
    13
    14
    AA
    16
    17
    18
    19
    20
    21
    22
    23
    24

    测试结果:

      

    四,总结

     1.多学习,从问题提出到我现在解决问题大概有几个月时间,最近一直忙于软考,从中也学习到很多解决问题的方法,比如复杂问题可以化繁为简。

    2.多思考,上面的实现代码,我个人觉得可能有要优化的部分,特别是关于IO操作部分。

    3.多动手,我曾将这个问题抛出给一个同学(Java),问问他能不能实现,他表示这个很麻烦,不愿意动手~ 然而个人能力的提升很重要的就是自己动手和积累! 

    4.上面的代码是用JAVA实现的,如果其他语言的效率更高可以试一试,比如Python。 

      

  • 相关阅读:
    mysql索引数据结构
    29. 使用参数化编写自动化测试用例
    28. Python编写自动化测试用例
    27. Unittest单元测试框架的介绍与使用
    26. 什么是单元测试
    25. Postman的使用
    24. 接口测试的意义
    23. requests安装与使用
    22. 如何编写接口文档
    21. Blog接口开发
  • 原文地址:https://www.cnblogs.com/coloz/p/11871356.html
Copyright © 2011-2022 走看看