作為一個日志中心,它會收集各種各樣的日志,可以用于問題排查,數(shù)據(jù)監(jiān)控,統(tǒng)計分析等等。那么對于繁多的日志,它們都有各自的存儲格式,我們?nèi)绾蝸韰^(qū)分它們,對于不同的日志格式,我們又是如何去解析的呢?
一長串沒有結(jié)構(gòu)化的日志,給人的感覺很凌亂。我們需要的是提取日志中的有效字段,并以我們期望的形式進行展現(xiàn)。下面我將和大家一起來探究日志解析的奧秘。
原理
依照前文,使用filebeat來上傳日志數(shù)據(jù),logstash進行日志收集與處理,elasticsearch作為日志存儲與搜索引擎,最后使用kibana展現(xiàn)日志的可視化輸出。所以不難發(fā)現(xiàn),日志解析主要還是logstash做的事情。
說到logstash,它到底有哪些東西呢?我們來簡單看下:

從上圖中可以看到,logstash主要包含三大模塊:
- INPUTS: 收集所有數(shù)據(jù)源的日志數(shù)據(jù)([源有file、redis、beats等,filebeat就是使用了beats源*);
- FILTERS: 解析、整理日志數(shù)據(jù)(本文重點);
- OUTPUTS: 將解析的日志數(shù)據(jù)輸出至存儲器([elasticseach、file、syslog等);
看來FILTERS是我們探究的重點,先來來看看它常用到的幾個插件(后面日志解析會用到):
- grok:采用正則的方式,解析原始日志格式,使其結(jié)構(gòu)化;
- geoip:根據(jù)IP字段,解析出對應(yīng)的地理位置、經(jīng)緯度等;
- date:解析選定時間字段,將其時間作為logstash每條記錄產(chǎn)生的時間(若沒有指定該字段,默認(rèn)使用read line的時間作為該條記錄時間);
*注意:codec也是經(jīng)常會使用到的,它主要作用在INPUTS和OUTPUTS中,[提供有json的格式轉(zhuǎn)換、multiline的多行日志合并等
場景
說了這么多,到底怎么用呢?我們還是通過幾個例子,具體來看看是怎么實現(xiàn)的吧。
秉承先易后難的原則,希望大家全部看完后,對以后遇到更復(fù)雜的日志,也能處理的游刃有余。
1. NodeJS 日志
- 日志格式
$time - $remote_addr $log_level $path - $msg
- 日志內(nèi)容
2017-03-15 18:34:14.535 - 112.65.171.98 INFO /root/ws/socketIo.js - xxxxxx與ws server斷開連接
- filebeat配置(建議filebeat使用rpm安裝,以systemctl start filebeat方式啟動)
filebeat:
prospectors:
- document_type: nodejs #申明type字段為nodejs,默認(rèn)為log
paths:
- /var/log/nodejs/log #日志文件地址
input_type: log #從文件中讀取
tail_files: true #以文件末尾開始讀取數(shù)據(jù)
output:
logstash:
hosts: ["${LOGSTASH_IP}:5044"]
#General Setting
name: "server1" #設(shè)置beat的名稱,默認(rèn)為主機hostname
- logstash中FILTERS配置
filter {
if [type] == "nodejs" { #根據(jù)filebeat中設(shè)置的type字段,來過濾不同的解析規(guī)則
grok{
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} - %{IPORHOST:clientip} %{LOGLEVEL:level} %{PATH:path} - %{GREEDYDATA:msg}" }
}
geoip {
source => "clientip" #填寫IP字段
}
}
}
- 結(jié)果(為方便演示,數(shù)據(jù)有刪減)

-
Filter配置講解
- grok中的match內(nèi)容:
- key:表示所需解析的內(nèi)容;
- value:表示解析的匹配規(guī)則,提取出對應(yīng)的字段;
- 解析語法:%{正則模板:自定義字段},其中TIMESTAMP_ISO8601、IPORHOST等都是grok提供的正則模板;
- geoip:通過分析IP值,產(chǎn)生IP對應(yīng)的地理位置信息;
這里是否發(fā)現(xiàn)@timestamp與timestamp不一致,@timestamp表示該日志的讀取時間,在elasticsearch中作為時間檢索索引。下面講解Nginx日志時,會去修正這一問題。
- grok中的match內(nèi)容:
2. Nginx 訪問日志
- 日志格式
$remote_addr - $remote_user [$time_local]
"$request" $status $body_bytes_sent "$http_referer"
"$http_user_agent" "$http_x_forwarded_for"
- 日志內(nèi)容
112.65.171.98 - - [15/Mar/2017:18:18:06 +0800] "GET /index.html HTTP/1.1" 200 1150 "http://www.yourdomain.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36" "-"
- filebeat中prospectors的配置
- document_type: nginx
paths:
- /var/log/nginx/access.log #日志文件地址
input_type: log #從文件中讀取
tail_files: true #以文件末尾開始讀取數(shù)據(jù)
- logstash中FILTERS配置
filter {
if [type] == "nginx" {
grok{
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp" , "dd/MMM/yyyy:HH:mm:ss Z", "ISO8601" ]
target => "@timestamp" #可省略
}
}
}
-
結(jié)果
3.png
-
Filter配置講解
- grok:
- 是不是很不可思議,上一示例中我們匹配規(guī)則寫了一長串,這個僅僅一個COMBINEDAPACHELOG就搞定了!
- grok除了提供上面那種基礎(chǔ)的正則規(guī)則,還對常用的日志(java,http,syslog等)提供的相應(yīng)解析模板,本質(zhì)還是那么一長串正則,[詳情見grok的120中正則模板;
- date:
- match:數(shù)組中第一個值為要匹配的時間字段,后面的n個是匹配規(guī)則,它們的關(guān)系是or的關(guān)系,滿足一個即可;
- target:將match中匹配的時間替換該字段,默認(rèn)替換@timestamp;
目前為止我們解析的都是單行的日志,向JAVA這樣的,若果是多行的日志我們又該怎么做呢?
- grok:
3. JAVA Log4j 日志
- 日志內(nèi)容
'2017-03-16 15:52:39,580 ERROR TestController:26 - test:
java.lang.NullPointerException
at com.test.TestController.tests(TestController.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)'
- filebeat中prospectors的配置
- document_type: tomcat
paths:
- /var/log/java/log #日志文件地址
input_type: log #從文件中讀取
tail_files: true #以文件末尾開始讀取數(shù)據(jù)
multiline:
pattern: ^\d{4}
match: after
negate: true
- logstash中FILTERS配置
filter {
if [type] == "tomcat" {
grok{
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{JAVALOGMESSAGE:msg}" }
}
date {
match => [ "timestamp" , "yyyy-MM-dd HH:mm:ss,S", "ISO8601" ]
}
}
}I
-
結(jié)果
4.png -
Filebeat配置講解
-
multiline 合并多行日志:
- pattern:匹配規(guī)則,這里指匹配每條日志開始的年份;
- match:有before與after,這里指從該行開始向后匹配;
- negate:是否開始一個新記錄,這里指當(dāng)pattern匹配后,結(jié)束之前的記錄,創(chuàng)建一條新日志記錄;
當(dāng)然在logstash input中使用codec multiline設(shè)置是一樣的
-
小技巧:關(guān)于grok的正則匹配,官方有給出Grok Constructor方法,在這上面提供了debugger、自動匹配等工具,方便大家編寫匹配規(guī)則
獲取更多免費資料加群:554355695
如果你想學(xué)習(xí)Java工程化、高性能及分布式、高性能、深入淺出。性能調(diào)優(yōu)、Spring,MyBatis,Netty源
碼分析和大數(shù)據(jù)等知識點可以來找我。
而現(xiàn)在我就有一個平臺可以提供給你們學(xué)習(xí),讓你在實踐中積累經(jīng)驗掌握原理。主要方向是JAVA架構(gòu)
師。如果你想拿高薪,想突破瓶頸,想跟別人競爭能取得優(yōu)勢的,想進BAT但是有擔(dān)心面試不過的,可以
加我的Java架構(gòu)進階群:554355695
總結(jié)
本文開始簡單介紹了logstash的三大模塊:INPUTS、FILTERS、OUTPUTS。之后通過Demo了3個小示例,給大家講解了FILTERS中g(shù)rok、geoip、date三個常用插件的使用,以及在處理多行日志上的做法。
在描述的過程中可能不能面面俱到,但我還是始終堅持“知其然知其所以然”的理念。寫的每一行代碼,你都得心中有數(shù)。功能的實現(xiàn)不意味著結(jié)束,我們何不多折磨自己一下,走好最后的一公里。
最后,有興趣可以去看一下它的官方手冊,對這三大模塊,各自都提供了非常多的插件支持。我這里只是一個簡單的使用,希望對大家有所幫助。

