1. 程式人生 > >java-grok通過正則表示式解析日誌

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


     相關依賴包包括: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檔案中新增下面的依賴,不過其他依賴包仍然需要自己去引入進來:

<dependency>
  <groupId>io.thekraken</groupId>
  <artifactId>grok</artifactId>
  <version>0.1.5</version>
</dependency>

     好了,現在我們開始對相關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<String, Object> 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<String,Object> 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資料!有了這個功能,我們可以解析的日誌無論多麼複雜多變,只要它裡面的資料遵循一定的正則表示式匹配規則,那麼就能轉換為我們需要的資料!