集中式日志分析平臺 - ELK Stack - Logstash的Grok插件

本文主要介紹 Grok 的基本語法,以及一個(gè)簡單的基于 Filebeat 日志進(jìn)行結(jié)構(gòu)化處理的例子。

Grok的初衷是為了解析異構(gòu)文本并進(jìn)行結(jié)構(gòu)化處理,使其變得可查詢。尤其適合 syslog logs,apache and other webserver logs,mysql logs,以及任何方便人類閱讀而生的日志。Logstash 默認(rèn)就預(yù)定義了 120 種日志模板,可以在這里下載 https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns ,可以通過 patterns_dir 配置進(jìn)行添加。有幾個(gè)經(jīng)常使用的網(wǎng)站:

我們用的最常用的還是 Grok debuggerGrok-Patterns 。在 Grok-Patterns 中查看如何去進(jìn)行利用預(yù)定義模板進(jìn)行匹配,然后在 Grok debugger 中進(jìn)行校驗(yàn)我們寫的基本正則是否正確。本文以 Filebeat 的日志解析為例,闡述如何使用 Logstash + Grok 進(jìn)行日志的解析、過濾。

Grok 基礎(chǔ)

Grok Pattern 的基本語法是 %{SYNTAX:SEMANTIC} 。 SYNTAX 是 Pattern 的名稱。比如說 3.44 會(huì)被匹配匹配到 NUMBER Pattern,55.3.244.1 會(huì)被匹配到 IP Pattern。SEMANTIC 是對文本內(nèi)容的定義,相當(dāng)于 json 中的 key。比如,3.44 可以被定義為 event 的持續(xù)時(shí)間 duration,55.3.244.1 可以被定義為發(fā)起請求的 client 。對于這個(gè)例子來說,Grok 過濾器的定義如下:

%{NUMBER:duration} %{IP:client}

我們還可以使用強(qiáng)制轉(zhuǎn)換,比如我們可以把 num 這個(gè) SEMANTIC 轉(zhuǎn)換成整型:

%{NUMBER:num:int}

舉一個(gè) http 請求日志的簡單例子,日志文本如下:

55.3.244.1 GET /index.html 15824 0.043

我們對應(yīng)的 Grok Pattern 應(yīng)該如下:

%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}

Logstash 的配置如下:

input {
  file {
    path => "/var/log/http.log"
  }
}
filter {
  grok {
    match => { "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}" }
  }
}

最終解析出來的數(shù)據(jù)如下:

client: 55.3.244.1
method: GET
request: /index.html
bytes: 15824
duration: 0.043

正則表達(dá)式

Grok 支持所有正則表達(dá)式,可以參考Oniguruma Regular Expressions。

自定義模板

為了滿足預(yù)定義的 Pattern 無法滿足需求的情況, Logstash 有自定義模板機(jī)制。首先,可以使用 Oniguruma 語法去捕捉一部分文本,并定義為 field:

(?<field_name>the pattern here)

比如,postfix 日志有 queue_id 字段包含了10-11位16進(jìn)制的字符。我們可以通過以下正則進(jìn)行捕捉:

(?<queue_id>[0-9A-F]{10,11})

或者,對于常用的自定義模板,我們可以創(chuàng)建自定義模板文件,并在配置里引用,達(dá)到復(fù)用的目的。創(chuàng)建一個(gè)名為 patterns 的模板,其中包含一個(gè)名為 extra 的文件,在該文件中,對 pattern 進(jìn)行命名,然后編寫該模板的正則表達(dá)式。例如,postfix queue id例子如下:

# contents of ./patterns/postfix:
POSTFIX_QUEUEID [0-9A-F]{10,11}

然后在 Logstash 配置中申明 patterns_dir 告訴 grok 插件去哪里尋找對應(yīng)的自定義模板:

Jan  1 06:25:43 mailserver14 postfix/cleanup[21403]: BEF25A72965: message-id=<20130101142543.5828399CCAF@mailserver14.example.com>
filter {
  grok {
    patterns_dir => ["./patterns"]
    match => { "message" => "%{SYSLOGBASE} %{POSTFIX_QUEUEID:queue_id}: %{GREEDYDATA:syslog_message}" }
  }
}

解析出來的數(shù)據(jù)為:

timestamp: Jan 1 06:25:43
logsource: mailserver14
program: postfix/cleanup
pid: 21403
queue_id: BEF25A72965
syslog_message: message-id=<20130101142543.5828399CCAF@mailserver14.example.com>

其余一些概要性質(zhì)的學(xué)習(xí)請自定看官方文檔,這里就不進(jìn)行翻譯了:Synopsis。

日志分析實(shí)例

接下來我們對 Filebeat 日志進(jìn)行分析,我們先來看看 Filebeat 日志長啥樣兒:

2017-02-20T20:22:42+08:00 INFO Non-zero metrics in the last 30s: publish.events=1 libbeat.logstash.publish.write_bytes=391 libbeat.publisher.published_events=1 libbeat.logstash.call_count.PublishEvents=1 registrar.states.update=1 libbeat.logstash.published_and_acked_events=1 registrar.writes=1 libbeat.logstash.publish.read_bytes=6

2017-02-21T14:05:08+08:00 INFO Non-zero metrics in the last 30s: registrar.writes=1 registrar.states.update=1 libbeat.logstash.published_and_acked_events=1 libbeat.logstash.publish.read_byte
s=6 libbeat.logstash.call_count.PublishEvents=1 libbeat.publisher.published_events=1 libbeat.logstash.publish.write_bytes=383 publish.events=1

Grok 結(jié)構(gòu)化處理

會(huì)發(fā)現(xiàn)大致分為幾個(gè)部分: 日志時(shí)間、日志類型、日志消息、日志詳情。修改 Logstash 的配置,以進(jìn)行 grok 結(jié)構(gòu)化:

input {
  beats {
    port => 5044
  }
}

filter {
  grok {
    match => {
      "message" => "^%{YEAR:year}-%{MONTHNUM:month}-%{MONTHDAY:day}T%{HOUR:hour}:?%{MINUTE:minute}(?::?%{SECOND:second})?%{ISO8601_TIMEZONE:timzone}? %{LOGLEVEL:log_level} %{DATA:log_msg}: %
{GREEDYDATA:log_msg_detail}$"
    }
    overwrite => ["message"]
  }
}

output {
  elasticsearch {
    hosts => "172.16.134.2:9200"
    manage_template => false
    index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}"
    document_type => "%{[@metadata][type]}"
  }
}

重啟 Logstash 后我們在 Kibana 中可以看到 Availiable Fileds 中出現(xiàn)了幾個(gè)新增的 fileds:year month day hour minute second timezone log_level log_msg log_msg_detail 。接著我們需要在 Management -> Index Pattern 中執(zhí)行刷新,把 fields 映射關(guān)系刷入緩存。 這個(gè)時(shí)候我們在 Index Pattern 中發(fā)現(xiàn),類似 hour 這樣的 fields 類型為 string,不符合我們的需求。怎么解決呢?我們利用 mutate 插件來實(shí)現(xiàn):

filter {
  grok {
    match => {
      "message" => "^%{YEAR:year}-%{MONTHNUM:month}-%{MONTHDAY:day}T%{HOUR:hour}:?%{MINUTE:minute}(?::?%{SECOND:second})?%{ISO8601_TIMEZONE:timzone}? %{LOGLEVEL:log_level} %{DATA:log_msg}: %{GREEDYDATA:log_msg_detail}$"
    }
    overwrite => ["message"]
  }
  mutate {
    convert => {"year" => "integer"}
    convert => {"month" => "integer"}
    convert => {"day" => "integer"}
    convert => {"hour" => "integer"}
    convert => {"minute" => "integer"}
    convert => {"second" => "integer"}
  }
}

重啟 Logstash 服務(wù),但是刷新 Index Pattern ,還是看到對于 fields 的類型是 string。原因是 ES 只允許使用 API 去修改,我們需要調(diào)用 Mapping API 去修改 ES 中的數(shù)據(jù)類型。

小插曲 - 刪除 Index Pattern 后的故障

然后有一個(gè)小插曲,愚昧的筆者點(diǎn)擊了這個(gè)按鈕:

Remove_index_pattern.png

刪除 filebeat-* 這個(gè) Index Pattern 之后 Kibana 的 Discover 頁面無法進(jìn)入,查看請求失敗的日志:

{"error":{"root_cause":[{"type":"index_not_found_exception","reason":"no such index","resource.type":"index_or_alias","resource.id":"packetbeat-*","index_uuid":"_na_","index":"packetbeat-*"}],"type":"index_not_found_exception","reason":"no such index","resource.type":"index_or_alias","resource.id":"packetbeat-*","index_uuid":"_na_","index":"packetbeat-*"},"status":404}

找到社區(qū)一篇文章Kibana blank after deleting index找到了解決方案,在終端執(zhí)行以下調(diào)用以強(qiáng)刷數(shù)據(jù)去除不一致:

curl -XPOST '172.16.134.2:9200/.kibana/_update_by_query?pretty&wait_for_completion&refresh' -H 'Content-Type: application/json' -d'
{
  "script": {
    "inline": "ctx._source.defaultIndex = null",
    "lang": "painless"
  },
  "query": {
    "term": {
      "_type": "config"
    }
  }
}
'

這時(shí)候再在 Index Pattern 中把 filebeat-* 添加回,即可看到year month day hour minute second timezone log_level log_msg log_msg_detail 的數(shù)據(jù)類型變成了 number。

Kibana Discover 查詢

Kibana 的 Discover 界面提供了超便利的查詢,以下舉幾個(gè)常用的查詢例子。主要的查詢語法可以看這里。

根據(jù)時(shí)間 range 查詢

我們已經(jīng)結(jié)構(gòu)化了時(shí)間維度的信息,比如我們需要查詢 2017-02-21 19:30:00 ~ 2017-02-21 19:40:00 的日志數(shù)據(jù),可以這么寫,請注意 @timestamp filed 不是日志數(shù)據(jù),是日志數(shù)據(jù)寫入 ES 的時(shí)間戳:

year:2017 AND month:2 AND day:21 AND hour:19 AND minute: [30 TO 40]
按照日志類型 log_level 過濾

我們之前定義了 log_level filed,比如我們要查詢 ERROR 類型的日志,很簡單:

log_level:ERROR

太簡單了,所以筆者不進(jìn)行復(fù)述了,讀者可以結(jié)合 Filter 進(jìn)行各種查詢組合得到自己想要的結(jié)果。然后如上一篇所述,可以定義為 Visulazation 加入 Dashboard。

Grok 注意事項(xiàng)

  • Grok 在匹配失敗的時(shí)候性能可能并不那么好
  • 多留意_grokparsefailures出現(xiàn)的頻率和出現(xiàn)時(shí)候的性能
  • 寫正則的時(shí)候記得打錨點(diǎn)
  • 不使用錨點(diǎn)的時(shí)候分層Grok處理的性能會(huì)比不分層的性能好,不過打了錨點(diǎn)的話兩個(gè)都一樣
  • 多使用LogStash的性能監(jiān)控功能,后續(xù)還可以拿來分析用

具體可以查看這篇中文譯文:你真的了解Grok嗎。

Q & A

Q: 在 Discover 中添加 filebeat-* Index Pattern 時(shí)報(bào)錯(cuò):unable to fetch mapping do you have indices mapping the pattern。

A: 原因是數(shù)據(jù)沒有流入 ES,無法構(gòu)建索引模板。

參考文獻(xiàn)

Do you grok Grok?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容