Grokking grok
Elastic Stack Serverless
Grok 是一种支持可重用别名表达式的正则表达式方言。Grok 非常适用于 syslog 日志、Apache 和其他 Web 服务器日志、mysql 日志,以及通常为人类而非计算机消费而编写的任何日志格式。
Grok 基于 Oniguruma 正则表达式库,因此任何正则表达式在 grok 中都有效。Grok 使用此正则表达式语言来命名现有模式,并将它们组合成更复杂的模式来匹配您的字段。
Elastic Stack 附带了许多 预定义的 grok 模式,简化了 grok 的使用。重用 grok 模式的语法采用以下形式之一
%{{SYNTAX}} |
%{SYNTAX:ID} |
%{SYNTAX:ID:TYPE} |
SYNTAX
- 将匹配文本的模式名称。例如,
NUMBER
和IP
都是默认模式集中提供的模式。NUMBER
模式匹配诸如3.44
之类的数据,而IP
模式匹配诸如55.3.244.1
之类的数据。 ID
- 您赋予正在匹配的文本片段的标识符。例如,
3.44
可能是事件的持续时间,因此您可以将其称为duration
。字符串55.3.244.1
可能会标识发出请求的client
。 TYPE
- 您要转换命名字段的数据类型。支持
int
、long
、double
、float
和boolean
类型。
例如,假设您有如下消息数据
3.44 55.3.244.1
第一个值是一个数字,后跟一个看起来像是 IP 地址的值。您可以使用以下 grok 表达式来匹配此文本
%{NUMBER:duration} %{IP:client}
为了简化到 Elastic Common Schema (ECS) 的迁移,除了现有模式之外,还提供了一组新的符合 ECS 的模式。新的 ECS 模式定义捕获符合该模式的事件字段名称。
ECS 模式集具有旧模式集的所有模式定义,并且是即插即用的替代品。使用 ecs-compatability
设置来切换模式。
新功能和增强功能将添加到符合 ECS 的文件中。旧模式可能仍会收到向后兼容的错误修复。
您可以将预定义的 grok 模式合并到 Painless 脚本中以提取数据。要测试您的脚本,请使用 Painless 执行 API 的字段上下文或创建一个包含该脚本的运行时字段。运行时字段提供更大的灵活性并接受多个文档,但如果您没有对要测试脚本的群集具有写访问权限,则 Painless 执行 API 是一个不错的选择。
如果您在构建 grok 模式以匹配您的数据时需要帮助,请使用 Kibana 中的 Grok Debugger 工具。
例如,如果您正在处理 Apache 日志数据,则可以使用 %{{COMMONAPACHELOG}}
语法,该语法了解 Apache 日志的结构。一个示例文档可能如下所示
"timestamp":"2020-04-30T14:30:17-05:00","message":"40.135.0.0 - -
[30/Apr/2020:14:30:17 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"
要从 message
字段中提取 IP 地址,您可以编写一个包含 %{{COMMONAPACHELOG}}
语法的 Painless 脚本。您可以使用 Painless 执行 API 的 ip
字段上下文 测试此脚本,但让我们改用运行时字段。
根据示例文档,索引 @timestamp
和 message
字段。为了保持灵活性,请使用 wildcard
作为 message
的字段类型
PUT /my-index/
{
"mappings": {
"properties": {
"@timestamp": {
"format": "strict_date_optional_time||epoch_second",
"type": "date"
},
"message": {
"type": "wildcard"
}
}
}
}
接下来,使用 批量 API 将一些日志数据索引到 my-index
中。
POST /my-index/_bulk?refresh
{"index":{}}
{"timestamp":"2020-04-30T14:30:17-05:00","message":"40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:30:53-05:00","message":"232.0.0.0 - - [30/Apr/2020:14:30:53 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:12-05:00","message":"26.1.0.0 - - [30/Apr/2020:14:31:12 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:19-05:00","message":"247.37.0.0 - - [30/Apr/2020:14:31:19 -0500] \"GET /french/splash_inet.html HTTP/1.0\" 200 3781"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:22-05:00","message":"247.37.0.0 - - [30/Apr/2020:14:31:22 -0500] \"GET /images/hm_nbg.jpg HTTP/1.0\" 304 0"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:27-05:00","message":"252.0.0.0 - - [30/Apr/2020:14:31:27 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:28-05:00","message":"not a valid apache log"}
现在,您可以在包含 Painless 脚本和 grok 模式的映射中定义一个运行时字段。如果模式匹配,则脚本会发出匹配的 IP 地址的值。如果模式不匹配 (clientip != null
),则脚本只会返回字段值而不会崩溃。
PUT my-index/_mappings
{
"runtime": {
"http.clientip": {
"type": "ip",
"script": """
String clientip=grok('%{COMMONAPACHELOG}').extract(doc["message"].value)?.clientip;
if (clientip != null) emit(clientip);
"""
}
}
}
或者,您可以在搜索请求的上下文中定义相同的运行时字段。运行时定义和脚本与先前在索引映射中定义的完全相同。只需将该定义复制到搜索请求中的 runtime_mappings
部分下,并包含一个与运行时字段匹配的查询。此查询返回的结果与您在索引映射中为 http.clientip
运行时字段定义搜索查询相同,但仅在此特定搜索的上下文中有效
GET my-index/_search
{
"runtime_mappings": {
"http.clientip": {
"type": "ip",
"script": """
String clientip=grok('%{COMMONAPACHELOG}').extract(doc["message"].value)?.clientip;
if (clientip != null) emit(clientip);
"""
}
},
"query": {
"match": {
"http.clientip": "40.135.0.0"
}
},
"fields" : ["http.clientip"]
}
使用 http.clientip
运行时字段,您可以定义一个简单的查询来搜索特定的 IP 地址并返回所有相关字段。_search
API 上的 fields
参数适用于所有字段,即使是那些未作为原始 _source
的一部分发送的字段也是如此
GET my-index/_search
{
"query": {
"match": {
"http.clientip": "40.135.0.0"
}
},
"fields" : ["http.clientip"]
}
响应包括搜索查询中指示的特定 IP 地址。Painless 脚本中的 grok 模式在运行时从 message
字段提取了此值。
{
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "my-index",
"_id" : "1iN2a3kBw4xTzEDqyYE0",
"_score" : 1.0,
"_source" : {
"timestamp" : "2020-04-30T14:30:17-05:00",
"message" : "40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"
},
"fields" : {
"http.clientip" : [
"40.135.0.0"
]
}
}
]
}
}