java正则表达式从大量日志中筛选有用数据

Posted 六楼外的风景

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java正则表达式从大量日志中筛选有用数据相关的知识,希望对你有一定的参考价值。

  1. 背景;
    事情发生在3天前自己带的同事上线一个旧业务的修改,到开户预提交订购多个资费包时,出问题了,当用户订购多个资费id时,最后一个资费id总会把前一个覆盖,最终得到入库的资费id永远只有一个;然后这个问题已存在3天时间,到数据库一查后台用户开户数近19000,资费id全是只有一个,还好这个业务开的都是后付费用户,下月一号才生效,这让我们还有时间补救;
    面临另一个问题就是,这19000开户数都是另一个系统通过接口来调用的,非咱们营业厅前台开的, 经多方协调,对方无法给我们提供开户原始的订购数据,相当于后台补数据的数据源断了;领导又开始难为我们程序员了;让我好好想办法;
    这个问题还是挺麻烦的,大BOSS连接几个电话过来,说这个影响大很可能引起集体投诉,要马上,立即解决!

  2. 把搞出bug的小弟拉了过来,第一时间是让他把生产的这个有问题的代码回退到上一版本;其次一起把引起这个问题的代码修改过来(主要是在一个4层的xml节点for循环没有把最深那层资费id节点用外层变量来赋值),反复测试后和领导申请做了紧急版本升级!

  3. 解决了源头问题,不再有增量了,现在就是解决存量问题了;那么多数据,又没有源数据,怎么修复;想了很久,这种数据都是通过预提交接口过来的,接口日志应该有所有记录,可以想办法从日志中找出;但看到这三天的日志,大得惊人;要捞出有问题的数据还真是大海捞针,但也得捞;

    1. 分拆日志,知道这个资费id的xml标识:

<NumId>手机号</NumId>
..................
        <ProductInfo>
                <ProductID>产品id2</ProductID>
                <ProCompInfo>
                    <ProComponentID>xxx</ProComponentID>
                    <ProComItemInfo>
                        <ProComItemType>D</ProComItemType>
                        <ProComItemID>资费id1</ProComItemID>
                    </ProComItemInfo>
                </ProCompInfo>
            </ProductInfo>
            <ProductInfo>
                <ProductID>产品id2</ProductID>
                <ProCompInfo>
                    <ProComponentID>xxx</ProComponentID>
                    <ProComItemInfo>
                        <ProComItemType>D</ProComItemType>
                        <ProComItemID>资费id2</ProComItemID>
                    </ProComItemInfo>
                </ProCompInfo>
            </ProductInfo>
..................


(这个编辑器居然不能在代码以外写xml标记,只能用图片代规范文本了)

方法1 :想过用linux的sed和awk组合从new.log 里面筛选出一个的机号下两个产品中的资费id,但每笔日志相差很大,不是很有规律的;再加上自已对这两大命令不是特别熟悉,最终还是放弃这个方案

方法2 :得到new.log 有时想用ue来手工分离这部分数据,但也可行,还是因为规律,因为要分拆出一笔请求日志中手机号下的产品id下的资费id,UE好像也很难做到

方法3 :把得到日志想办法截出请求中完整的xml,然后用java一行行读出来,然后按dom4j来操作xml,就能得到所有节点下所有数据再放到一个map写到数据库的新建表中,最后就可以用sql很容易得到相应要补录的数据了;只要能把数据入表形成格式化的数据,一切都好办,这个好像可行!
最终在解释xml中遇到很多问题,因为这个是xml的解释依赖东西很多,不是我们平时很通用的xml,涉及到报文头,xml特殊节点又要走特殊方法,最重要的是new.log中还杂有很多非xml的东西,如换行,日期,经过每个函数都会打印一些改变过的变量,写完之后没跑到1000条日志就报错(new.log中有很多重复打印的数据,约有12w笔日志,实际有用的数据才13708条)
……………………….
方法还试过很多其它的,基本从上班试到了下午3:00多,最终还是决定用正则来做,最终在19:00点时把所有数据入库,然后开始漫长的数据比对分类,最终用排除法,一步步用sql把没用的数据去掉(怎么sql筛选数据这个还是很值得写下,但因涉及到太多表,就算写了你们也不一定愿意看),最终形成了1327条数据需补录,然后发给相应的运维部门,一看时间已是零晨的2:30了,加上这是个冬天,确实有点冷!一个人走在回家的路上,却没有感到一点点困意

Java正则的解决方法
正面用代码+注释的方法来解释,比较好的学习是查jdk-api文档,要看实例这篇还行http://blog.csdn.net/kofandlizi/article/details/7323863

java代码:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * java 正则解释日志中文件的数值
 *
 */
public class JavaPatternXml  
    //日志文件目录:从生产把日志分切成若干文件,因为太大在window下根本就打不开
    static final String pendingPath="D://errorFile/";
    private int i = 1;
    static int count = 1;
    public static void main(String[] args) throws Exception 
        File pendingDir = new File(pendingPath);
        //取得日志目录下所有日志文件名
        String[] files = pendingDir.list();
        int count = 1;
        if (files.length == 0) 
            System.err.println("There isn't any file in directory:" + pendingDir);
            System.exit(1);
        else 
            System.out.println(files.length);
            //所有日志文件一个一个for来读,从每个日志文件中然后再一行行读到java中
            for (String fn : files) 
                System.out.println(pendingPath + fn);
                getData(pendingPath + fn);
            
        
    
    public static void getData(String file) throws IOException 
        //UTF-8解决读进来中文乱码
         BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));  
         String temp;
         //每读一行数据就调用stringSub()来处理每行
         while((temp=br.readLine())!=null)
            stringSub(temp);
         
         br.close();
    


    public static void stringSub(String str) 

    if (str != null && str.length() > 0) 
            //用map来装解释出来的所有数据
            Map<String, String> hsMap = new HashMap<String, String>();
            //\\w 单词字符:[a-zA-Z_0-9]   \\W 非单词字符:[^\\w] 

            //先把手机号解释出来
            Pattern p = Pattern.compile("<NumID>([\\\\w/\\\\.]*)</NumID>");
            Matcher m = p.matcher(str);
            //hitEnd()如果匹配器执行的最后匹配操作中搜索引擎遇到输入结尾,则返回 true。其实在这里不用m.hitEnd()也是可以的
            //find()尝试查找与该模式匹配的输入序列的下一个子序列
            //m.group(1) 是取得每个NumID中的数据,如有多个就要用多个map的键来装
            while (!m.hitEnd() && m.find()) 
                hsMap.put("NumID", m.group(1));
                m.group(1);
                System.out.println(m.group(1));
            

            //产品id  productId 多加<Producttype>01</Producttype> 是因为只要主产品id 业务需要,只要一个主产品id即可
            Pattern p3 = Pattern.compile("<ProductID>([\\\\w/\\\\.]*)</ProductID><Producttype>01</Producttype>");
            Matcher m3 = p3.matcher(str);
            while (!m3.hitEnd() && m3.find()) 
                hsMap.put("product_id", m3.group(1));
                //System.out.println(m3.group(2));//不能用group(2)会报错的
            

            //这次真正主角,资费id
            Pattern p1 = Pattern.compile("<ProComItemID>([\\\\w/\\\\.]*)</ProComItemID>");
            Matcher m1 = p1.matcher(str);
            // System.out.println("有数据:"+m1.groupCount());
            int iN = 1;
            while (!m1.hitEnd() && m1.find()) 
                hsMap.put("ItemID"+iN, m1.group(1));//多个资费id时放到ItemID* 
                iN++;
                //System.out.println(m1.group(1));
                //System.out.println(m1.group(2));
            

            /**
             * 调用日间processTime 这个也是业务需要,在后期发现同一号会出现多次调用
             * 就不知以哪个为准,加入时间可以在sql中用时间大小来去掉这部分数据
             */
            Pattern p4 = Pattern.compile("<ProcessTime>([\\\\w/\\\\.]*)</ProcessTime>");
            Matcher m4 = p4.matcher(str);
            // System.out.println("有数据:"+m1.groupCount());
            while (!m4.hitEnd() && m4.find()) 
                hsMap.put("pro_time", m4.group(1));
            
            mysqlOper(hsMap);

  
    
    /**
     * 连接mysql并写表,因为当天网络有问题,无法写到测试的oracle数据库存中,暂时用这个本地的mysql来存数据
     * 最终导出sql再插入oracle中,哎!后来发现2w条sql写oracle要20分钟,用了很多方法都差不多
     * create table wo_open_pre3(
       pre_id int primary key,
       NumID VARCHAR(20) NOT NULL,
       product_id VARCHAR(20),
       pro_time VARCHAR(20),
       ItemID1 VARCHAR(20),
       ItemID2 VARCHAR(20),
       ItemID3 VARCHAR(20),
       ItemID4 VARCHAR(20)
);
     */
    public static int  mySqlOper(Map<String, String> hsMap)

        System.out.println("hsMap:"+hsMap);
        int flag = 2;
        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://127.0.0.1:3306/test";
        String user = "root"; 
        String password = "root";
        Connection conn = null;
        Statement st = null;
        String NumID = hsMap.get("NumID");
        String product_id = hsMap.get("product_id");
        String pro_time = hsMap.get("pro_time");
        String ItemID1 = hsMap.get("ItemID1");
        String ItemID2 = hsMap.get("ItemID2");
        String ItemID3 = hsMap.get("ItemID3");
        String ItemID4 = hsMap.get("ItemID4");
        try 
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            if(!conn.isClosed()) 
                 System.out.println("Succeeded connecting to the Database!");
            
             st = conn.createStatement();

             StringBuffer sb = new StringBuffer();
            // sb.append("insert into wo_open_pre values (");
             sb.append("insert into wo_open_pre2 values (");
             sb.append(count++);
             sb.append(",'").append(NumID).append("'");
             sb.append(",'").append(product_id).append("'");
             sb.append(",'").append(pro_time).append("'");
             sb.append(",'").append(ItemID1).append("'");
             sb.append(",'").append(ItemID2).append("'");
             sb.append(",'").append(ItemID3).append("'");
             sb.append(",'").append(ItemID4).append("')");
             String sql = sb.toString();
             System.out.println(sql);
            boolean ints = st.execute(sql);
            System.out.println(ints);
            st.close();
            conn.close();
         catch (ClassNotFoundException e) 
            e.printStackTrace();
         catch (SQLException e) 
            e.printStackTrace();
        finally

        

        return flag;
    

以上是关于java正则表达式从大量日志中筛选有用数据的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式,查找筛选数据的又一利器!

JAVA-正则例子记录

java 用正则 筛选掉特殊符号

正则表达式过滤日志

正则表达式小结

在猪脚本中使用正则表达式从日志中提取字符串