Grok 是 Logstash 最重要的插件。你可以在 grok 里预定义好命名正则表达式,在稍后(grok参数或者其他正则表达式里)引用它。它非常适用于syslog logs,apache和一些其他的webserver logs,以及mysql logs。grok有很多定义好pattern,当然也可以自己定义。
grok的语法:
%{SYNTAX:SEMANTIC}
SYNTAX表示grok定义好的pattern,SEMANTIC表示自定义的字段。
例如192.168.0.100
用%{IP:client}可以将IP定义为client
假如现在某webserver log中的内容为以下格式,
55.3.244.1 GET /index.html 15824 0.043
我们完全可以利用grok将这些信息定义成以下字段
%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}
写到配置文件中通常这样子:
input { file { path => "/var/log/http.log" }}filter { grok { match => { "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}" } }}
grok过滤后得到的信息成了以下这样子
client: 55.3.244.1
method: GET
request: /index.html
bytes: 15824
duration: 0.043
如何自定义Pattern?
语法:(?<field_name>the pattern here)
假如有以下内容“
begin 123.456 end
我们希望将123.456定义成request_time字段,可以向下面这样写这个正则表达式
\s+(?<request_time>\d+(?:\.\d+)?)\s+
解释:
\s:匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。+表示匹配次数为1次或者多次
(?<request_time> ):这个是grok语法,request_time表示要将捕获的字符定义成的字段名
\d+:匹配一个或者多个数字
(?:\.\d+):为正则表达式,
(?: pattern):非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
\.\d+:表示点后面跟一个或者多个 数字,(?:\.\d+)?表示点后面跟一个或多个数字这种情况出现0次或者多次,如果为0次,则request_time为一个整数。所以匹配到的结果可能为123.456或者123或者123.4.5.6,这些都满足条件
测试下:
创建一个配置文件,内容如下:
input {stdin{}} filter { grok { match => { "message" => "\s+(?<request_time>\d+(?:\.\d+)?)\s+" } } } output {stdout{}}
运行 logstash 进程然后输入 "begin 123.456 end",你会看到类似下面这样的输出:
{ "message" => "begin 123.456 end", "@version" => "1", "@timestamp" => "2014-08-09T11:55:38.186Z", "host" => "raochenlindeMacBook-Air.local", "request_time" => "123.456" }
练习:
/var/log/userlog.info 日志文件中获取到的信息为以下格式,需要自定义 2016-05-20T20:00:15.703407+08:00 localhost [audit root/13283 as root/13283 on pts/0/172.16.100.99:64790->10.10.10.6:22]: #=== session closed === 2016-05-21T09:52:54.424055+08:00 localhost [audit root/13558 as root/13558 on pts/0/172.16.100.99:50897->10.10.10.6:22]: #=== session opened === 2016-05-21T09:53:25.687134+08:00 localhost [audit root/13558 as root/13558 on pts/0/172.16.100.99:50897->10.10.10.6:22] /root: cd /etc/logstash/conf.d/ 2016-05-21T09:53:26.284741+08:00 localhost [audit root/13558 as root/13558 on pts/0/172.16.100.99:50897->10.10.10.6:22] /etc/logstash/conf.d: ll
注意上面的日志文件中不是每一行的内容格式都是一样的,grok表达式如下
%{TIMESTAMP_ISO8601:timestamp} %{IPORHOST:login_host} \[\S+ %{USER:login_user}/%{NUMBER:pid} as %{USER:sudouser}/%{NUMBER:sudouser_pid} on %{WORD:tty}/%{NUMBER:tty_id}/%{IPORHOST:host_ip}:%{NUMBER:source_port}-\>%{IPORHOST:local_ip}:%{NUMBER:dest_port}\](?:\:|) (%{UNIXPATH:current_path} %{GREEDYDATA:command}|%{GREEDYDATA:detail})
注意:(?:\:|) (%{UNIXPATH:current_path} %{GREEDYDATA:command}|%{GREEDYDATA:detail})
上面的日志内容在后面出现了不一样的地方
该如何处理?冒号可能有也可能没有,必须对冒号进行匹配,正则表达式如下
(?:\:|) 还可以这样写 (\:)? 表示冒号出现一次或者多次
当没有冒号,后面的信息是以下内容时,
/root: cd /etc/logstash/conf.d/
grok这样写,
%{UNIXPATH:current_path} %{GREEDYDATA:command}
当出现
#=== session closed ===
这样的内容时,grok这样写,
%{GREEDYDATA:detail}
但是这两种情况需要加“|”来判断,表示或,所以正确的写法为
(%{UNIXPATH:current_path} %{GREEDYDATA:command}|%{GREEDYDATA:detail})
注意如果上面的grok语句顺序调整为下面这样子时
(%{GREEDYDATA:detail}|%{UNIXPATH:current_path} %{GREEDYDATA:command})
就会有问题,它会将
/root: cd /etc/logstash/conf.d/
全部匹配成detail。