java正则表达式从大量日志中筛选有用数据
Posted 六楼外的风景
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java正则表达式从大量日志中筛选有用数据相关的知识,希望对你有一定的参考价值。
背景;
事情发生在3天前自己带的同事上线一个旧业务的修改,到开户预提交订购多个资费包时,出问题了,当用户订购多个资费id时,最后一个资费id总会把前一个覆盖,最终得到入库的资费id永远只有一个;然后这个问题已存在3天时间,到数据库一查后台用户开户数近19000,资费id全是只有一个,还好这个业务开的都是后付费用户,下月一号才生效,这让我们还有时间补救;
面临另一个问题就是,这19000开户数都是另一个系统通过接口来调用的,非咱们营业厅前台开的, 经多方协调,对方无法给我们提供开户原始的订购数据,相当于后台补数据的数据源断了;领导又开始难为我们程序员了;让我好好想办法;
这个问题还是挺麻烦的,大BOSS连接几个电话过来,说这个影响大很可能引起集体投诉,要马上,立即解决!把搞出bug的小弟拉了过来,第一时间是让他把生产的这个有问题的代码回退到上一版本;其次一起把引起这个问题的代码修改过来(主要是在一个4层的xml节点for循环没有把最深那层资费id节点用外层变量来赋值),反复测试后和领导申请做了紧急版本升级!
解决了源头问题,不再有增量了,现在就是解决存量问题了;那么多数据,又没有源数据,怎么修复;想了很久,这种数据都是通过预提交接口过来的,接口日志应该有所有记录,可以想办法从日志中找出;但看到这三天的日志,大得惊人;要捞出有问题的数据还真是大海捞针,但也得捞;
- 分拆日志,知道这个资费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正则表达式从大量日志中筛选有用数据的主要内容,如果未能解决你的问题,请参考以下文章