java-grok通过正则表达式解析日志

     项目中有一个新的需求,就是需要解析日志,将日志中的部分数据分析获取出来供系统使用,通俗的讲就是抓取日志中的部分有用的信息,比如下面的apache日志信息,我需要解析每行日志,获取每行日志的IP地址、用户、创建时间、请求方式、地址....如果我们单纯使用java的方式,可能会想到通过文件流读取日志信息,然后逐行解析字符串,但是这种方式太过于复杂,而且效率比较低,在网上查询了相关的资料,决定使用logstash的grok工具,在网上也有相对应的java实现,其实现原理就是自定义正则表达式,通过正则表达式来解析日志,好处就是我们可以将日志中不规则的数据转换为规则的数据,例如map或者json数据,还有就是写一次表达式之后,可以在多处运行,如果有什么不同的地方,只需要修改一下正则表达式即可,是不是感觉很方便呢。

64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846
64.242.88.10 - - [07/Mar/2004:16:06:51 -0800] "GET /twiki/bin/rdiff/TWiki/NewUserTemplate?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4523
64.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 6291
64.242.88.10 - - [07/Mar/2004:16:11:58 -0800] "GET /twiki/bin/view/TWiki/WikiSyntax HTTP/1.1" 200 7352
64.242.88.10 - - [07/Mar/2004:16:20:55 -0800] "GET /twiki/bin/view/Main/DCCAndPostFix HTTP/1.1" 200 5253

      网上对grok的定义是:Grok 是 Logstash 最重要的插件。你可以在 grok 里预定义好命名正则表达式,在稍后(grok参数或者其他正则表达式里)引用它。参考博文:http://udn.yyuap.com/doc/logstash-best-practice-cn/filter/grok.html

      用java api的方式来集成grok,在github上面已经有了相关的项目工程实现了,我们可以在github中获取这个工程:https://github.com/thekrakken/java-grok,或者直接使用其打包好了的jar包和相关依赖包,可以在我的资源目录中进行下载:http://download.csdn.net/detail/harderxin/9923587

     相关依赖包包括:commons-beanutils-1.8.3.jar、commons-lang3-3.1.jar、commons-logging-1.1.1.jar、gson-2.2.2.jar、slf4j-api-1.7.5.jar,版本号可以不与上面列举一样。

      如果你是maven工程,可以在pom.xml文件中添加下面的依赖,不过其他依赖包仍然需要自己去引入进来:


  io.thekraken
  grok
  0.1.5

     好了,现在我们开始对相关api进行操作了,新建工程,将相关的jar包引入进来,还有就是引入java-grok工程中默认的正则表达式文件,它里面定义了数字、文本、日期、IP地址等等一些列的基础数据的正则表达式,参考:https://github.com/thekrakken/java-grok/blob/master/patterns/patterns

     要使用java-grok,首先需要通过定义好的正则表达式文件的路径创建Grok对象,我们可以定义为一个单列模式

package com.harderxin.grok.core;

import io.thekraken.grok.api.Grok;
import io.thekraken.grok.api.exception.GrokException;

public class GrokInstance {

	private static Grok grok;

	private GrokInstance() {

	}

	public static Grok getGrokInstance(String grokPatternPath) {
		if (grok == null) {
			try {
				grok = Grok.create(grokPatternPath);
			} catch (GrokException e) {
				e.printStackTrace();
			}
		}
		return grok;
	}
}
      获取到Grok对象后,通过传入我们需要解析的日志的表达式名称和要转换的日志消息,来创建Match对象:

	public static Match getMatch(String pattern, String message) {
		Match match = null;
		try {
			grok.compile(pattern);
			match = grok.match(message);
			match.captures();
		} catch (GrokException e) {
			e.printStackTrace();
			match = null;
		}
		return match;
	}
      得到Match对象后,我们就可以将数据转换为对应的Map或者Json数据了,我写了一个辅助类:

package com.harderxin.grok.core;

import java.util.Map;

import io.thekraken.grok.api.Grok;
import io.thekraken.grok.api.Match;
import io.thekraken.grok.api.exception.GrokException;

public class GrokUtils {

	private static final String GROK_PATTERN_PATH = "conf/agent_patterns";

	private static Grok grok = GrokInstance.getGrokInstance(GROK_PATTERN_PATH);

	public static Map toMap(String pattern, String message) {
		Match match = getMatch(pattern, message);
		if (match != null) {
			return match.toMap();
		}
		return null;
	}

	public static String toJson(String pattern, String message) {
		Match match = getMatch(pattern, message);
		if (match != null) {
			return match.toJson();
		}
		return null;
	}

	private static Match getMatch(String pattern, String message) {
		Match match = null;
		try {
			grok.compile(pattern);
			match = grok.match(message);
			match.captures();
		} catch (GrokException e) {
			e.printStackTrace();
			match = null;
		}
		return match;
	}

}
      创建我们的测试类,注意: 上面的每次解析只能解析日志中的单行数据,当要解析一个日志文件的时候,我们需要逐行解析该文件,我们需要解析下面这一行日志:

64.242.88.10 - - [07/Mar/2004:16:45:56 -0800] \"GET /twiki/bin/attach/Main/PostfixCommands HTTP/1.1\" 401 12846,通过程序解析后,我需要得到clientip =64.242.88.10,timestamp = 07/Mar/2004:16:45:56 -0800,verb=GET,httpversion = 1.1等这样格式化的数据,用我们上面的程序就可以做到。

在patterns文件中定义好我们需要解析日志的正则表达式:

XINTEST %{IPORHOST:clientip} %{USER:ident;boolean} %{USER:auth}[%{HTTPDATE:timestamp;date;dd/MMM/yyyy:HH:mm:ss Z}\] \"(?:%{WORD:verb;string} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion;float})?|%{DATA:rawrequest})\" %{NUMBER:response;int} (?:%{NUMBER:bytes;long}|-)

其中的IPORHOST是pattern中已经定义好的正则表达式,如下面所示,clientip是我们为解析后的数据的Key的别名,如果没有别名,默认名称为正则表达式的名称

IP (?:%{IPV6:UNWANTED}|%{IPV4:UNWANTED})
HOSTNAME \b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\.?|\b)

IPORHOST (?:%{HOSTNAME:UNWANTED}|%{IP:UNWANTED})
我们的pattern表达式编写好了,名称为XINTEST,下面就可以使用我们的代码进行测试了,在测试的时候,pattern表达式需要使用%{}将名称放进去,这个是规定:

package com.harderxin.grok.test;

import java.util.Map;

import com.harderxin.grok.core.GrokUtils;

public class GrokTest2 {
	public static void main(String[] args) {
		String pattern = "%{XINTEST}";
		String message = "64.242.88.10 - - [07/Mar/2004:16:45:56 -0800] \"GET /twiki/bin/attach/Main/PostfixCommands HTTP/1.1\" 401 12846";
		String json = GrokUtils.toJson(pattern, message);
		System.out.println(json);
		Map map = GrokUtils.toMap(pattern, message);
		System.out.println(map.toString());
	}

}

代码输出的json数据如下:

{
    "HOUR": "16", 
    "INT": "-0800", 
    "MINUTE": "45", 
    "MONTH": "Mar", 
    "MONTHDAY": "07", 
    "SECOND": "56", 
    "TIME": "16:45:56", 
    "XINTEST": "64.242.88.10 - - [07/Mar/2004:16:45:56 -0800] \"GET /twiki/bin/attach/Main/PostfixCommands HTTP/1.1\" 401 12846", 
    "YEAR": "2004", 
    "auth": "-", 
    "bytes": 12846, 
    "clientip": "64.242.88.10", 
    "httpversion": 1.1, 
    "ident": false, 
    "request": "/twiki/bin/attach/Main/PostfixCommands", 
    "response": 401, 
    "timestamp": "Mar 8, 2004 8:45:56 AM", 
    "verb": "GET"
}
      日志中相关的数据就被我们程序解析出来了!!其实它的原理就是我们自定义正则表达式,然后通过正则表达式去匹配每一行的数据,转换为用户定义的key:value数据!有了这个功能,我们可以解析的日志无论多么复杂多变,只要它里面的数据遵循一定的正则表达式匹配规则,那么就能转换为我们需要的数据!

你可能感兴趣的:(工作经历)