Nginx訪問日志記錄了用戶訪問的詳細信息,解析Nginx訪問日志對業務運維具有重要意義。本文介紹如何使用正則表達式函數或GROK函數解析Nginx訪問日志。
解析方案簡介
日志服務支持通過數據加工的正則表達式和GROK解析Nginx日志,兩種方案對比如下:
正則表達式方案
對不擅長正則表達式的分析人員,使用正則表達式解析日志時會存在使用難度高、加工效率低、學習成本高和靈活性不足的問題。不推薦優先使用。詳情請參見正則表達式。
GROK方案(推薦)
GROK學習成本低,只需要了解不同模式所代表的字段類型,就可以輕松解析日志內容。從靈活性、高效性、低成本、學習曲線等方面對比,GROK都要比直接使用正則表達式有優勢。當前SLS數據加工已經提供了四百種模式,建議優先使用GROK模式。詳情請參見GROK模式參考。
GROK模式的本質是正則表達式,在有需要的情況下,可以組合使用GROK與正則表達式。
自定義的Nginx日志格式,可以通過正則表達式或GROK方案的調整實現解析。
使用正則表達式解析Nginx成功訪問日志
現以一條Nginx成功訪問日志為例,介紹如何使用正則表達式解析Nginx成功訪問日志。
原始日志
__source__: 192.168.0.1 __tag__:__client_ip__: 192.168.254.254 __tag__:__receive_time__: 1563443076 content: 192.168.0.2 - - [04/Jan/2019:16:06:38 +0800] "GET http://example.aliyundoc.com/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)"
解析需求
需求1:從Nginx日志中提取出code、ip、datetime、protocol、request、sendbytes、referer、useragent、verb信息。
需求2:對request進行再提取,提取出uri_proto、uri_domain、uri_param信息。
需求3:對解析出來的uri_param進行再提取,提取出uri_path、uri_query信息。
SLS 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]+)["]') """第二步:解析第一步得到的request""" e_regex('request',r'(?P<uri_proto>(\w+)):\/\/(?P<uri_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$)') """第三步:解析第二步得到的uri_param參數""" e_regex('uri_param',r'(?P<uri_path>\/\_[a-z]+[^?])\?(?<uri_query>(.+)$)')
細分編排及對應加工結果
針對需求1的加工編排如下。
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]+)["]')
對應加工結果:
__source__:192.168.0.1 __tag__:__receive_time__: 1563443076 code:200 content:192.168.0.2 - - [04/Jan/2019:16:06:38 +0800] "GET http://example.aliyundoc.com/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)"httpversion:1.1 datetime:04/Jan/2019:16:06:38 +0800 ip:192.168.0.2 protocol:HTTP/1.1 refere:- request:http://example.aliyundoc.com/_astats?application=&inf.name=eth0 sendbytes:273932 useragent:Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html) verb:GET
針對需求2解析request,加工編排如下。
e_regex('request',r'(?P<uri_proto>(\w+)):\/\/(?P<uri_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$)')
對應加工結果:
uri_param: /_astats?application=&inf.name=eth0 uri_domain: example.aliyundoc.com uri_proto: http
針對需求3解析uri_param,加工編排如下。
e_regex('uri_param',r'(?P<uri_path>\/\_[a-z]+[^?])\?(?<uri_query>(.+)$)')
對應加工結果:
uri_path: /_astats uri_query: application=&inf.name=eth0
加工結果
__source__:192.168.0.1 __tag__:__receive_time__: 1563443076 code:200 content:192.168.0.2 - - [04/Jan/2019:16:06:38 +0800] "GET http://example.aliyundoc.com/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)"httpversion:1.1 datetime:04/Jan/2019:16:06:38 +0800 ip:192.168.0.2 protocol:HTTP/1.1 refere:- request:http://example.aliyundoc.com/_astats?application=&inf.name=eth0 sendbytes:273932 uri_domain:example.aliyundoc.com uri_proto:http uri_param: /_astats?application=&inf.name=eth0 uri_path: /_astats uri_query: application=&inf.name=eth0 useragent:Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html) verb:GET
使用GROK解析Nginx成功訪問日志
現以一條Nginx成功訪問日志為例,介紹如何使用GROK解析Nginx成功訪問日志。
原始日志
__source__: 192.168.0.1 __tag__:__client_ip__: 192.168.254.254 __tag__:__receive_time__: 1563443076 content: 192.168.0.2 - - [04/Jan/2019:16:06:38 +0800] "GET http://example.aliyundoc.com/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)"
解析需求
需求1:從Nginx日志中提取出clientip、bytes、agent、auth、verb、request、ident、timestamp、httpversion、response、bytes、referrer信息。
需求2:對解析出來的request進行再提取,提取出uri_proto、uri_domain、uri_param信息。
需求3:對解析出來的uri_param進行再提取,提取出uri_path、uri_query信息。
SLS DSL編排
總編排
"""第一步:初步解析Nginx日志""" e_regex('content',grok('%{COMBINEDAPACHELOG}')) """第二步:解析第一步得到的request""" e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?")) """第三步:解析第二步得到的uri_param參數""" e_regex('uri_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}"))
使用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
%{COMMONAPACHELOG} %
{QS:referrer} %{QS:agent}
解析出上一行中所有字段,另外還解析出referrer、agent字段。
細分編排及對應加工結果
針對需求1解析Nginx日志的加工編排如下。
e_regex('content',grok('%{COMBINEDAPACHELOG}'))
對應加工結果:
clientip: 192.168.0.1 __tag__:__receive_time__: 1563443076 agent:"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)" auth:- bytes:273932 clientip:192.168.0.2 content:192.168.0.2 - - [04/Jan/2019:16:06:38 +0800] "GET http://example.aliyundoc.com/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)" httpversion:1.1 ident:- referrer:"-" request:http://example.aliyundoc.com/_astats?application=&inf.name=eth0 response:200 timestamp:04/Jan/2019:16:06:38 +0800 verb:GET
針對需求2解析request,加工編排如下。
e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?"))
對應加工結果:
uri_proto: http uri_domain: example.aliyundoc.com uri_param: /_astats?application=&inf.name=eth0
特別說明,只需要使用GROK的以下數種模式組合即可對request完成解析,如下表所示。
模式
規則
說明
URIPROTO
[A-Za-z]+(\+[A-Za-z+]+)?
匹配uri中的頭部分。例如
http://hostname.domain.tld/_astats?application=&inf.name=eth0
會匹配到http。USER
[a-zA-Z0-9._-]+
匹配字母、數字和符號(
._-
)組合。URIHOST
%{IPORHOST}(?::%
匹配IPORHOST和POSINT。
URIPATHPARAM
%{URIPATH}(?:%{URIPARAM})?
匹配uri參數部分。
針對需求3解析uri_param,加工編排如下。
e_regex('uri_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}"))
對應加工結果:
uri_path: /_astats uri_query: application=&inf.name=eth0
使用GROK的以下模式即可完成對uri_param的解析,如下表所示。
模式
規則
說明
GREEDYDATA
.*
匹配任意或多個除換行符。
加工結果
__source__:192.168.0.1 __tag__:__receive_time__: 1563443076 agent:"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)" auth:- bytes:273932 clientip:192.168.0.2 content:192.168.0.2 - - [04/Jan/2019:16:06:38 +0800] "GET http://example.aliyundoc.com/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.example.com/bot.html)" httpversion:1.1 ident:- referrer:"-" request:http://example.aliyundoc.com/_astats?application=&inf.name=eth0 response:200 timestamp:04/Jan/2019:16:06:38 +0800 uri_domain:example.aliyundoc.com uri_param:/_astats?application=&inf.name=eth0 uri_path:/_astats uri_proto:http uri_query:application=&inf.name=eth0 verb:GET
使用GROK解析Nginx失敗訪問日志
以下以一條Nginx失敗訪問日志為例,介紹如何使用GROK解析Nginx失敗訪問日志。
原始日志
__source__: 192.168.0.1 __tag__:__client_ip__: 192.168.254.254 __tag__:__receive_time__: 1563443076 content: 2019/08/07 16:05:17 [error] 1234#1234: *1234567 attempt to send data on a closed socket: u:111111ddd, c:0000000000000000, ft:0 eof:0, client: 1.2.3.4, server: sls.aliyun.com, request: "GET /favicon.ico HTTP/1.1", host: "sls.aliyun.com", referrer: "https://sls.aliyun.com/question/answer/123.html?from=singlemessage"
解析需求
從content中解析出錯誤訪問日志host、http_version、log_level、pid、referrer、request、request_time、server、verb信息。
SLS DSL編排
e_regex('content',grok('%{DATESTAMP:request_time} \[%{LOGLEVEL:log_level}\] %{POSINT:pid}#%{NUMBER}: %{GREEDYDATA:errormessage}(?:, client: (?<client>%{IP}|%{HOSTNAME}))(?:, server: %{IPORHOST:server})(?:, request: "%{WORD:verb} %{NOTSPACE:request}( HTTP/%{NUMBER:http_version})")(?:, host: "%{HOSTNAME:host}")?(?:, referrer: "%{NOTSPACE:referrer}")?'))
加工結果
___source__: 192.168.0.1 __tag__:__client_ip__: 192.168.254.254 __tag__:__receive_time__: 1563443076 content:2019/08/07 16:05:17 [error] 1234#1234: *1234567 attempt to send data on a closed socket: u:111111ddd, c:0000000000000000, ft:0 eof:0, client: 1.2.3.4, server: sls.aliyun.com, request: "GET /favicon.ico HTTP/1.1", host: "sls.aliyun.com", referrer: "https://sls.aliyun.com/question/answer/123.html? host: sls.aliyun.com http_version: 1.1 log_level: error pid: 1234 referrer: https://sls.aliyun.com/question/answer/123.html?from=singlemessage request: /favicon.ico request_time:19/08/07 16:05:17 server: sls.aliyun.com verb: GET