1.Grok filter入门
有效分析和查询送入Elastic Stack的数据的能力取决于信息的可读性。 这意味着,当将非结构化数据摄取到系统中时,必须将其转换为结构化数据。
通常,这个至关重要的任务留给Logstash(尽管还有其他日志传送器可用,比如 Fluentd)。无论你定义什么数据源,都必须提取日志并执行一些魔力来美化它们,以确保在将它们输出到Elasticsearch之前正确地对其进行了解析。
Logstash中的数据操作是使用过滤器插件执行的。 本文重点介绍最流行和有用的过滤器插件之一 ——Logstash grok过滤器,该过滤器用于将非结构化数据解析为结构化数据。
1.1 什么是 Grok?
最初的术语实际上是很新的由罗伯特·A·海因莱因(Robert A. Heinlein)在他的1961年的《陌生的土地上的陌生人》一书中创造的,指的是理解某种东西,使人们真正沉浸其中。 这是 grok 语言和 Logstash grok 插件的合适名称,它们可以以一种格式修改信息并将其浸入另一种格式(特别是 JSON)。 已经有数百种用于记录的 Grok 模式。
1.2 Grok 是如何工作的?
简而言之,grok是一种将行与正则表达式匹配,将行的特定部分映射到专用字段中以及根据此映射执行操作的方法。
内置了超过 200 种 Logstash 模式 ,用于过滤 AWS,Bacula,Bro,Linux-Syslog 等中的单词,数字和日期等项目。 如果找不到所需的模式,则可以编写自己的自定义模式。 还有多个匹配模式的选项,可简化表达式的编写以捕获日志数据。
这是 Logstash grok 过滤器的基本语法格式:
%{PATTERN:FieldName}
这将匹配预定义的模式,并将其映射到特定的标识字段。 由于grok本质上是基于正则表达式的组合,因此你也可以创建自己的基于正则表达式的 grok 过滤器。 例如:
(?\d\d-\d\d-\d\d)
这将使 22-22-22(或任何其他数字)的正则表达式与字段名称匹配。
1.3 一个 Logstash Grok例子
这个 grok 调试工具是开始构建 grok 过滤器的好方法:Grok Debugger。你也可以在 Kibana 中找到 grok 的调试工具:
使用此工具,你可以粘贴日志消息并逐步构建 grok 模式,同时连续测试编译。 通常,我建议从 %{GREEDYDATA:message}
模式开始,然后逐步添加越来越多的模式。
在上面的示例中,我将从以下内容开始:
%{GREEDYDATA:message}
比如针对这条信息:
83.149.9.216 - - [17/May/2015:10:05:03 +0000] "GET /presentations/logstash-monitorama-2013/images/kibana-search.png HTTP/1.1" 200 203023 "http://semicomplete.com/presentations/logstash-monitorama-2013/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36"
我们可以看到整个信息都赋予给 message 了。
我们再接着尝试如下的例子:
%{IPORHOST:clientip} - - \[%{HTTPDATE:timestamp}\] %{GREEDYDATA:message}
在上面,我们可以看到 clientip, message及timestamp。
1.4 常见的例子
以下是一些示例,可帮助你熟悉如何构造grok过滤器:
1.4.1 Syslog
用 Grok 解析 syslog 消息是新用户更普遍的需求之一。 syslog 还有几种不同的日志格式,因此请牢记编写自己的自定义 grok 模式。 这是常见系统日志解析的一个示例:
May 4 00:10:36 liuxg xpcproxy[69746]: libcoreservices: _dirhelper_userdir: 557: bootstrap_look_up returned (ipc/send) invalid destination port
grok解析为:
%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?:%{GREEDYDATA:syslog_message}
在logstash中,我们应该使用如下的grok filter:
grok {
match => {
"message" => "%{SYSLOGTIMESTAMP:syslog_timestamp}
%{SYSLOGHOST:syslog_hostname}
%{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?:
%{GREEDYDATA:syslog_message}"
}
}
1.4.2 Apache access logs
80.135.37.131 - - [11/Sep/2019:23:56:45 +0000] "GET /item/giftcards/4852 HTTP/1.1" 200 91 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"
grok解析为:
%{COMBINEDAPACHELOG}
在logstash中,我们应该使用如下的grok filter:
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
1.4.3 Elasticsearch
[2017-09-10T12:07:26,683][WARN ][index.indexing.slowlog.index] [GOgO9TD] [testindex-slowlogs/yNbyYk1ARSW_hd0YRh6J0A] took[142.3micros], took_millis[0], type[product], id[105], routing[] , source[{"price":9925,"name":"Nariko"}]
grok解析为:
\[%{TIMESTAMP_ISO8601:timestamp}\]\[%{DATA:loglevel}%{SPACE}\]\[%{DATA:source}\]%{SPACE}\[%{DATA:node}\]%{SPACE}\[%{DATA:index}\] %{NOTSPACE} \[%{DATA:updated-type}\]
或
\[%{TIMESTAMP_ISO8601:timestamp}\]\[%{DATA:loglevel}%{SPACE}\]\[%{DATA:source}\]%{SPACE}\[%{DATA:node}\] (\[%{NOTSPACE:index}\]\[%{NUMBER:shards}\])?%{GREEDYDATA}
在logstash中,我们应该使用如下的grok filter:
grok {
match => ["message", "\[%{TIMESTAMP_ISO8601:timestamp}\]\[%{DATA:loglevel}%{SPACE}\]\[%{DATA:source}%{SPACE}\]%{SPACE}\[%{DATA:node}\]%{SPACE}\[%{DATA:index}\] %{NOTSPACE} \[%{DATA:updated-type}\]",
"message", "\[%{TIMESTAMP_ISO8601:timestamp}\]\[%{DATA:loglevel}%{SPACE}\]\[%{DATA:source}%{SPACE}\]%{SPACE}\[%{DATA:node}\] (\[%{NOTSPACE:Index}\]\[%{NUMBER:shards}\])?%{GREEDYDATA}"
]
}
上面有两种匹配的方法,只要其中的一种可以匹配就可以了。
1.4.4 Redis
grok {
match => ["redistimestamp", "\[%{MONTHDAY} %{MONTH} %{TIME}]",
["redislog", "\[%{POSINT:pid}\] %{REDISTIMESTAMP:timestamp}"],
["redismonlog", "\[%{NUMBER:timestamp} \[%{INT:database} %{IP:client}:%{NUMBER:port}\] "%{WORD:command}"\s?%{GREEDYDATA:params}"]
]
}
1.4.5 MogoDB
MONGO_LOG %{SYSLOGTIMESTAMP:timestamp} \[%{WORD:component}\] %{GREEDYDATA:message}MONGO_QUERY \{ (?<={ ).*(?= } ntoreturn:) \}MONGO_SLOWQUERY %{WORD} %{MONGO_WORDDASH:database}\.%{MONGO_WORDDASH:collection} %{WORD}: %{MONGO_QUERY:query} %{WORD}:%{NONNEGINT:ntoreturn} %{WORD}:%{NONNEGINT:ntoskip} %{WORD}:%{NONNEGINT:nscanned}.*nreturned:%{NONNEGINT:nreturned}..+ (?`<duration>`[0-9]+)msMONGO_WORDDASH \b[\w-]+\bMONGO3_SEVERITY \wMONGO3_COMPONENT %{WORD}|-MONGO3_LOG %{TIMESTAMP_ISO8601:timestamp} %{MONGO3_SEVERITY:severity} %{MONGO3_COMPONENT:component}%{SPACE}(?:\[%{DATA:context}\])? %{GREEDYDATA:message}
1.4.6 AWS
ELB_ACCESS_LOG %{TIMESTAMP_ISO8601:timestamp} %{NOTSPACE:elb} %{IP:clientip}:%{INT:clientport:int} (?:(%{IP:backendip}:?:%{INT:backendport:int})|-) %{NUMBER:request_processing_time:float} %{NUMBER:backend_processing_time:float} %{NUMBER:response_processing_time:float} %{INT:response:int} %{INT:backend_response:int} %{INT:received_bytes:int} %{INT:bytes:int} "%{ELB_REQUEST_LINE}"CLOUDFRONT_ACCESS_LOG (?`<timestamp>`%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\t%{TIME})\t%{WORD:x_edge_location}\t(?:%{NUMBER:sc_bytes:int}|-)\t%{IPORHOST:clientip}\t%{WORD:cs_method}\t%{HOSTNAME:cs_host}\t%{NOTSPACE:cs_uri_stem}\t%{NUMBER:sc_status:int}\t%{GREEDYDATA:referrer}\t%{GREEDYDATA:agent}\t%{GREEDYDATA:cs_uri_query}\t%{GREEDYDATA:cookies}\t%{WORD:x_edge_result_type}\t%{NOTSPACE:x_edge_request_id}\t%{HOSTNAME:x_host_header}\t%{URIPROTO:cs_protocol}\t%{INT:cs_bytes:int}\t%{GREEDYDATA:time_taken:float}\t%{GREEDYDATA:x_forwarded_for}\t%{GREEDYDATA:ssl_protocol}\t%{GREEDYDATA:ssl_cipher}\t%{GREEDYDATA:x_edge_response_result_type}
1.4.7 总结
Logstash grok 只是一种过滤器,可以在将日志转发到 Elasticsearch 之前将其应用于你的日志。 因为它在日志管道中扮演着如此重要的角色,所以 grok 也是最常用的过滤器之一。
以下是一些有用的资源列表,可以帮助你深入了解:
http://grokdebug.herokuapp.com ——这是在日志上构建和测试 grok 过滤器的有用工具
http://grokconstructor.appspot.com/ —— 另一个 grok builder/tester
https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns —— Logstash 支持的模式列表
1.5 总结
Logstash grok 只是在将日志转发到 Elasticsearch 之前可以应用于你的日志的一种过滤器。 由于 grok 在测井管道中起着至关重要的作用,因此它也是最常用的过滤器之一。
2. 日志解析的Grok模式示例
如果没有日志解析,搜索和可视化日志几乎是不可能的。 解析结构化您的传入(非结构化)日志,以便用户可以在调查期间或设置仪表板时搜索清晰的字段和值。
最流行的日志解析语言是Grok。 可以使用Grok插件在各种日志管理和分析工具中解析日志数据。
但是用 Grok 解析日志可能会很棘手。 本文将研究一些 Grok 模式示例,这些示例可以帮助你了解如何解析日志数据。
2.1 什么是 grok?
最初的术语实际上是相当新的——由 Robert A. Heinlein 在他 1961 年的《陌生土地上的陌生人》一书中创造 ——它指的是理解某事到一个人真正沉浸在其中的水平。 它是 grok 语言和 Logstash grok 插件的合适名称,它以一种格式修改信息并将其浸入另一种格式(特别是 JSON)中。 已经有几百种可被日志解析所使用的 Grok 模式。
2.2 Grok 是如何工作的?
简而言之,grok 是一种将行与正则表达式匹配、将行的特定部分映射到专用字段并基于此映射执行操作的方法。
内置有 200 多种 Logstash 模式,用于过滤 AWS、Bacula、Bro、Linux-Syslog 等中的单词、数字和日期等项目。 如果找不到你需要的模式,你可以编写自己的自定义模式。 还有多个匹配模式的选项,这简化了表达式的编写以捕获日志数据。
以下是 Logstash grok 过滤器的基本语法格式:
%{SYNTAX:SEMANTIC}
SYNTAX 将指定每个日志文本中的模式。 SEMANTIC 将是你在已解析日志中实际给出该语法的识别标记。 换一种说法:
%{PATTERN:FieldName}
这将匹配预定义的模式并将其映射到特定的识别字段。
例如,像 127.0.0.1 这样的模式将匹配 Grok IP 模式,通常是 IPv4 模式。
Grok 具有独立的 IPv4 和 IPv6 模式,但它们可以与语法 IP 一起过滤。
该标准模式如下:
IPV4 (?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])
假装没有统一的 IP 语法,你可以简单地用相同的语义字段名称来理解两者:
%{IPv4:Client IP} %{IPv6:Client IP}
同样,只需使用 IP 语法,除非出于任何原因要将这些各自的地址分隔到单独的字段中。
由于 grok 本质上是基于正则表达式的组合,因此你还可以使用以下模式创建自己的自定义基于正则表达式的 grok 过滤器:
(?<custom_field>custom pattern)
例如:
(?\d\d-\d\d-\d\d)
此 grok 模式会将 22-22-22(或任何其他数字)的正则表达式与字段名称匹配。
2.3 Logstash Grok 模式例子
为了演示如何开始使用 grok,我将使用以下应用程序日志:
2016-07-11T23:56:42.000+00:00 INFO [MySecretApp.com.Transaction.Manager]:Starting transaction for session -464410bf-37bf-475a-afc0-498e0199f008
我想用 grok 过滤器实现的目标是将日志线分解为以下字段:timestamp、log level、class,然后是 message 的其余部分。
%{TIMESTAMP_ISO8601:timestamp} %{WORD:log-level} \[%{DATA:class}\]:%{GREEDYDATA:message}
以下 grok 模式将完成这项工作:
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:log-level} \[%{DATA:class}\]:%{GREEDYDATA:message}" }
}
在开发的过程中,很多开发者自己不知道如何测试。在 Kibana 中,它含有一个 grok debugger:
如上所示,我们使用 %{GREEDYDATA:message}
把所有的信息都映射到 message 字段中。如果熟悉英文的开发者,greedy 也就是贪婪的意思。它可以把剩余的所有内容都映射为该字段。在kibana的grok debugger界面可以逐步调试grok。
2.4 Grok 数据类型转换
默认情况下,所有 SEMANTIC 条目都是字符串,但你可以使用简单的公式翻转数据类型。 以下 Logstash grok 示例将标识为语义 num 的任何语法 NUMBER 转换为语义浮点数 float:
%{NUMBER:num:float}
这是一个非常有用的工具,尽管它目前仅可用于转换为 float 或整数 int。
2.5 _grokparsefailure
这将尝试将传入日志与给定的 grok 模式匹配。 如果匹配,日志将根据过滤器中定义的 grok 模式分解为指定的字段。 如果不匹配,Logstash 将添加一个名为 _grokparsefailure
的标签。
2.6 操作数据
在匹配的基础上,你可以定义额外的 Logstash grok 配置来操作数据。 例如,你可以让 Logstash 1) 添加字段,2) 覆盖字段,或 3) 删除字段。
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:log-level} \[%{DATA:class}\]:%{GREEDYDATA:message}" }
overwrite => ["message"]
add_tag => [ "My_Secret_Tag" ]
}
在我们的例子中,我们使用 “overwrite” 操作来覆盖 “message” 字段。 这样,我们的 “message” 字段将不会与我们定义的其他字段(timestamp、log-level 和 class)一起出现。 此外,我们正在使用 “add_tag” 操作将自定义标签字段添加到日志中。
此处提供了可用于操作日志的可用操作的完整列表,以及它们的输入类型和默认值。
2.7 完整示例
让我们从一个示例非结构化日志消息开始,然后我们将使用 Grok 模式对其进行结构化:
128.39.24.23 - - [25/Dec/2021:12:16:50 +0000] "GET /category/electronics HTTP/1.1" 200 61 "/category/finance" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"
想象一下搜索数百万条看起来像这样的日志行! 看起来很可怕。 这就是为什么我们有像 Grok 这样的解析语言——让数据更易于阅读和搜索。
查看该数据并使用 Kibana 等分析工具进行搜索的一种更简单的方法是将其分解为具有值的字段,如下表所示:
1.ip:128.39.24.23
2.timestamp:25/Dec/2021:12:16:50 +0000
3.verb:GET
4.request:/category/electronics HTTP/1.1
5.status: 200
6.bytes: 61
7.referrer:/category/finance
8.os: Windows
让我们使用一个示例 Grok 模式来生成这些字段。 以下部分将展示 Grok 模式语法以生成上述每个字段。
对于每个部分,我们的 Grok 模式都会扩展,因为它包含更多要解析的字段。 这些模式是 Grok 可以阅读的正则表达式——我们可以使用它们来表达我们的信息。
下面是一些有用的链接,可以帮助你开始使用一些 Grok 模式。 但我们将在博客的其余部分提供更多示例。
Grok pattern
Grok Debugger
在 Grok pattern 页面,我们可以找到我们需要的预定义好的 pattern:
从某种意义上讲,这些 pattern 在本质上就是基于正则表达式的组合。也可以创建自己定义的 pattern。
让我们开始构建 Grok 模式来结构化数据。
我们使用以下方式描述模式的开始: ^
语法如下: %
请注意:不建议使用空格来描述字段的名称。
2.7.1 提取 IP
假设我们要提取 IP,我们可以使用 IP 方法:
^%{IP:ip}
然后我们有这些: – –
为了告诉 Grok 忽略它们,我们只需将它们添加到我们的模式中。
^%{IP:ip} - -
这个模式给了我们这个字段:
ip:128.39.24.23
2.7.2 提取 timestamps
在我们非结构化日志消息的下一部分中,我们将时间戳 “含” 在一个数组中:
[25/Dec/2021:12:16:50 +0000]
要提取它,我们需要使用正则表达式和 HTTPDATE 方法,同时在外面添加括号,以便 Grok 知道忽略它们:
\[%{HTTPDATE:timestamp}\]
基于我们之前的模式,我们现在有:
^%{IP:ip} - - \[%{HTTPDATE:timestamp}\]
这给了我们:
"ip": "128.39.24.23",
"timestamp": "25/Dec/2021:12:16:50 +0000"
回到我们最初的非结构化消息,看起来我们有一个空间,时间戳结束,“GET 开始”。 我们还需要告诉 Grok 忽略空格。 为此,我们只需按键盘上的空格键,或者我们可以使用 %{SPACE}
-> 直到 4 个空格。
2.7.3 提取 verbs
是时候提取 GET 字段了。 首先,我们需要告诉 Grok 忽略引号 - 然后使用 WORD 方法,我们将通过编写来做到这一点:
"%{WORD:verb}
所以,现在我们的模式是:
^%{IP:ip} - - \[%{HTTPDATE:timestamp}\] "%{WORD:verb}
这给了我们这些字段:
"ip": "128.39.24.23",
"verb": "GET",
"timestamp": "25/Dec/2021:12:16:50 +0000"
2.7.4 提取 request
为了提取请求 -> /category/electronics HTTP/1.1"
,我们需要使用 DATA 方法,它本质上是正则表达式中的通配符。
这意味着我们需要添加一个句号来提取此信息,以告诉 DATA 方法在哪里停止——否则,它不会捕获任何数据。 我们可以使用引号作为停止标记:
%{DATA:request}"
现在,我们有以下 Grok 模式:
^%{IP:ip} - - \[%{HTTPDATE:timestamp}\] "%{WORD:verb} %{DATA:request}"
这给了我们这些字段:
"request": "/category/electronics HTTP/1.1",
"ip": "128.39.24.23",
"verb": "GET",
"timestamp": "25/Dec/2021:12:16:50 +0000"
2.7.5 提取 status
接下来是状态,但我们在请求结束和状态之间再次有一个空格,我们可以添加一个空格或 %{SPACE}。 为了提取数字,我们使用 NUMBER 方法。
%{NUMBER:status}
现在我们的模式扩展到:
^%{IP:ip} - - \[%{HTTPDATE:timestamp}\] "%{WORD:verb} %{DATA:request}" %{NUMBER:status}
这给了我们如下的字段:
"request": "/category/electronics HTTP/1.1",
"ip": "128.39.24.23",
"verb": "GET",
"timestamp": "25/Dec/2021:12:16:50 +0000",
"status": "200"
2.7.6 提取 Bytes
为了提取字节,我们需要再次使用NUMBER方法,但在此之前,我们需要使用常规空格或 %{SPACE}
^%{IP:ip} - - \[%{HTTPDATE:timestamp}\] "%{WORD:verb} %{DATA:request}" %{NUMBER:status} %{NUMBER:bytes}
这给了我们如下的字段:
"request": "/category/electronics HTTP/1.1",
"bytes": "61",
"ip": "128.39.24.23",
"verb": "GET",
"timestamp": "25/Dec/2021:12:16:50 +0000",
"status": "200"
2.7.7 提取 referrer
要提取"referrer",我们需要使用常规空格或 %{SPACE}
-> " 所以 Grok 会忽略它:
%{DATA:referrer}"
如你所见,我们添加 DATA 直到它遇到 " 字符:
这给了我们如下的字段:
"request": "/category/electronics HTTP/1.1",
"referrer": "",
"bytes": "61",
"ip": "128.39.24.23",
"verb": "GET",
"timestamp": "25/Dec/2021:12:16:50 +0000",
"status": "200"
2.7.8 忽略数据及提取 os
现在我们想忽略这个数据 ->" "Mozilla/5.0 (compatible; MSIE 9.0; 为此,我们将使用 "Mozilla/5.0"的 DATA 方法,但不写入该字段将忽略它。
然后我们将使用不带冒号或字段名的 WORD 方法忽略 (compatible;
最后,我们将使用 DATA 忽略 MSIE 9.0;
这给我们留下了以下模式来忽略该数据
%{DATA}\(%{WORD};%{DATA};
为了进一步解释这种模式......
( -> 停止直到它到达 (
; -> 停止直到到达 ;
请注意上面的模式中的 ( 字符。第一个 DATA 遇到 ( 即停止,也就是第一个数据。第二个 DATA 停止直到遇到 ; 符号。同样地,第三个 DATA 停止直到遇到 ;
现在我们可以使用 WORD 方法提取 os -> %{WORD:os}。就是这样! 现在我们剩下以下 Grok 模式来构建我们的数据。
^%{IP:ip} - - \[%{HTTPDATE:timestamp}\] "%{WORD:verb} %{DATA:request}" %{NUMBER:status} %{NUMBER:bytes} %{DATA:referrer}" %{DATA}\(%{WORD};%{DATA}; %{WORD:os}
这为我们提供了这些整洁的字段,我们可以使用这些字段更轻松地搜索和可视化我们的日志数据:
"request": "/category/electronics HTTP/1.1",
"referrer": "\"/category/finance",
"os": "Windows",
"bytes": "61",
"ip": "128.39.24.23",
"verb": "GET",
"timestamp": "25/Dec/2021:12:16:50 +0000",
"status": "200"
至此,我们已经完成了对整个日志信息的结构化分析。