
本部分實踐案例,旨在通過一種場景多種解決方案的對比,選擇出一種最快最好的解決方案。本專題主要講解正則解析方面的場景實踐。
場景:解析Nginx日志
以下以一條Nginx日志為例,向大家展開如何解析Nginx日志的多種方案。
203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET /atom.xml HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
需求
1、從Nginx日志中提取出clientip、ident、auth、timestamp、verb、request、url、httpversion、response、bytes、referrer、agent資訊
2、對解析出來的url進行再提取,提取出url_proto、url_host、url_param
3、對解析出來的url_param進行再提取,提取出url_path、url_query資訊
原始日志
在控制台收集到的日志格式是string格式,如下所示:
__source__: 30.43.16.15
__tag__:__client_ip__: 12.120.75.140
__tag__:__receive_time__: 1563443076
content: 203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
LOG DSL編排
本部分将提供兩種方案,解決以上需求。
方案一:正則解析
1、針對需求1解析Nginx日志的加工編排如下:
e_regex("content",r'(?P<ip>\d+\.\d+\.\d+\.\d+)( - - \[)(?P<datetime>[\s\S]+)\] \"(?P<verb>[A-Z]+) (?P<request>[\S]*) (?P<protocol>[\S]+)["](?P<code>\d+) (?P<sendbytes>\d+) ["](?P<refere>[\S]*)["] ["](?P<useragent>[\S\s]+)["]')
預覽處理日志:
ip: 203.208.60.89
datetime: 04/Jan/2019:16:06:38 +0800
verb: GET
request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0
protocol: HTTP/1.1
code: 200
sendbytes: 273932
refere: -
useragent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
2、針對需求2解析第一步加工後得到的url的加工編排如下:
e_regex('url',r'(?P<url_proto>(\w+)):\/\/(?P<url_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$)')
url_proto: http
url_domain: cdn1cdedge0001.coxlab.net
uri_param: /_astats?application=&inf.name=eth0
3、針對需求3解析第二步得到的url參數的加工編排如下:
e_regex('uri_param',r'(?P<uri_path>\/\_[a-z]+[^?])\?(?<uri_query>(.+)$)')
uri_path: /_astats
uri_query: application=&inf.name=eth0
4、綜上LOG DSL規則可以如以下形式:
"""第一步:初步解析Nginx日志"""
e_regex("content",r'(?P<ip>\d+\.\d+\.\d+\.\d+)( - - \[)(?P<datetime>[\s\S]+)\] \"(?P<verb>[A-Z]+) (?P<request>[\S]*) (?P<protocol>[\S]+)["](?P<code>\d+) (?P<sendbytes>\d+) ["](?P<refere>[\S]*)["] ["](?P<useragent>[\S\s]+)["]')
"""第二步:解析第一步得到的url"""
e_regex('url',r'(?P<url_proto>(\w+)):\/\/(?P<url_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$)')
"""第三步:解析第二步的到的url參數"""
e_regex('uri_param',r'(?P<uri_path>\/\_[a-z]+[^?])\?(?<uri_query>(.+)$)')
預覽綜上處理後的日志如下:
__source__: 30.43.16.15
__tag__:__client_ip__: 12.120.75.140
__tag__:__receive_time__: 1563443076
content: 203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
ip: 203.208.60.89
datetime: 04/Jan/2019:16:06:38 +0800
verb: GET
request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0
protocol: HTTP/1.1
code: 200
sendbytes: 273932
refere: -
useragent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
url_proto: http
url_domain: cdn1cdedge0001.coxlab.net
uri_param: /_astats?application=&inf.name=eth0
uri_path: /_astats
uri_query: application=&inf.name=eth0
方案二:Grok解析
1、使用grok模式解析Nginx日志,隻需要COMBINEDAPACHELOG模式即可。
模式 | 規則 | 說明 | ||
---|---|---|---|---|
COMMONAPACHELOG | `%{IPORHOST:clientip} %{HTTPDUSER:ident} %{USER:auth} [%{HTTPDATE:timestamp}] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})? | %{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes} | -)` | 解析出clientip、ident、auth、timestamp、verb、request、httpversion、response、bytes字段内容 |
COMBINEDAPACHELOG | | 解析出上一行中所有字段,另外還解析出referrer、agent字段 |
針對需求1解析Nginx日志的加工編排如下:
e_regex('content',grok('%{COMBINEDAPACHELOG}'))
clientip: 203.208.60.89
ident: -
auth: -
timestamp: 04/Jan/2019:16:06:38 +0800
verb: GET
request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0
httpversion: 1.1
response: 200
bytes: 273932
referrer: "-"
agent: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
2、解析request隻需要使用grok的以下幾種模式組合即可完成解析:
URIPROTO | | 比對url中的頭部分,如 會比對到http |
USER | | 比對字母、數字和 組合 |
URIHOST | | 比對IPORHOST和POSINT |
URIPATHPARAM | | 比對url參數部分 |
針對需求2解析第一步加工後得到的request的加工編排如下:
e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?"))
url_proto: http
url_domain: cdn1cdedge0001.coxlab.net
uri_param: /_astats?application=&inf.name=eth0
3、解析url_param可以使用grok的以下模式即可完成解析:
GREEDYDATA | | 比對任意或多個除換行符 |
針對需求3解析第二步得到的url參數的加工編排如下:
e_regex('url_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}"))
uri_path: /_astats
uri_query: application=&inf.name=eth0
"""第一步:初步解析Nginx日志"""
e_regex('content',grok('%{COMBINEDAPACHELOG}'))
"""第二步:解析第一步得到的url"""
e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?"))
"""第三步:解析第二步的到的url參數"""
e_regex('url_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}"))
__source__: 30.43.16.15
__tag__:__client_ip__: 12.120.75.140
__tag__:__receive_time__: 1563443076
content: 203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
clientip: 203.208.60.89
ident: -
auth: -
timestamp: 04/Jan/2019:16:06:38 +0800
verb: GET
request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0
httpversion: 1.1
response: 200
bytes: 273932
referrer: "-"
agent: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
url_proto: http
url_domain: cdn1cdedge0001.coxlab.net
uri_param: /_astats?application=&inf.name=eth0
uri_path: /_astats
uri_query: application=&inf.name=eth0
對比
綜上所述,可以看出使用正則解析和Grok模式解析Nginx日志兩種方案優劣。
正則方案
對于不是很熟悉的開發人員使用正則解析日志效率會比較低,而且學習成本會比較大,另外一點是靈活性不夠,比如在request内容改成
http://[email protected]/_astats?application=&inf.name=eth0
那麼還使用以上正則
(?P<url_proto>(\w+)):\/\/(?P<url_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$)
request則會解析成
url_proto: http
url_domain: twiss@
uri_param: cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0
很顯然,如果還使用原來的正則模式的話,解析出來的内容是不符合要求的。是以,還需要修改正則模式才能正常解析。由此可見,靈活的使用正則的解析的難度比較高。
Grok方案
Grok模式解析對于開發人員是友好的,對于非開發人員亦然如此。Grok學習成本低,隻需要了解哪些模式代表的哪些字段類型就可以輕松解析你想解析的日志内容。Grok學習曲線低,可以通過使用者文檔中
GROK參考來學習實踐。
Grok靈活性高,比如還是以上述正則方案中例子為參考:
request内容改成
http://[email protected]/_astats?application=&inf.name=eth0
Grok模式不變
e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?"))
url_proto: http
user: twiss
url_domain: cdn1cdedge0001.coxlab.net
uri_param: /_astats?application=&inf.name=eth0
在Grok模式不變的情況下,request添加user的情況下,還是能夠正确解析出正确的日志内容。
結論
從靈活性、高效性、低成本、學習曲線等方面對比, GROK都要比直接使用正規表達式要有優勢. 但是GROK模式的本質其實還是正規表達式, 但是資料加工已經提供了
400種模式包裝了場景的正則, 建議優先使用. 當然在需要的情況下, 也可以混合使用GROK與正則甚至自行編寫需要的正則.
進一步參考
歡迎掃碼加入官方釘釘群獲得實時更新與阿裡雲工程師的及時直接的支援: