Elastic:在 Elasticsearch ingest 处理器中调试损坏的 grok 表达式

在之前的文章:

但是如果我们的模式不能正常工作,那该如何处理呢?

在本文中,我们将使用 Kibana 的 Grok Debugger 来帮助我们调试损坏的 grok 模式。 如下所述的分治法应该可以帮助你快速找到给定的 grok 模式与你的数据不匹配的原因。 调试 grok 模式并使其正常工作将使你能够构建数据结构,这将确保你的可观察性安全用例将以最佳性能运行。

 

划分(你的模式)并修改(您的错误)

假设我们正在尝试解析相对较长的消息,例如以下消息,它是来自 Elasticsearch 慢日志的条目:

[2020-05-14T20:39:29,644][INFO ][index.search.slowlog.fetch.S-OhddFHTc2h6w8NDzPzIw] [instance-0000000000] [kibana_sample_data_flights][0] took[3.7ms], took_millis[3], total_hits[416 hits], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{"query":{"match":{"DestCountry":{"query":"AU","operator":"OR","prefix_length":0,"max_expansions":50,"fuzzy_transpositions":true,"lenient":false,"zero_terms_query":"NONE","auto_generate_synonyms_phrase_query":true,"boost":1.0}}}}], id[],

并假设我们在互联网上找到了以下 grok 模式,我们被告知应该解析 Elasticsearch 慢日志,但是由于某种原因,它无法正常工作!

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*\[%{DATA:slowlog.index}\]\s*\[%{DATA:slowlog.shard:int}]took\[%{DATA:slowlog.took}\],\stook_millis\[%{DATA:slowlog.took_millis:float}\],\stotal_hits\[%{DATA:slowlog.total_hits:int}\shits\]\,\stypes\[%{DATA:slowlog.types}\],\sstats\[%{DATA:slowlog.stats}\],\ssearch_type\[%{DATA:slowlog.search_type}\],\stotal_shards\[%{DATA:slowlog.total_shards:int}\],\ssource\[%{GREEDYDATA:slowlog.source}\],\sid\[%{DATA:slowlog.x-opaque-id}\]

互联网似乎在保证它无法兑现。 幸运的是,我们可以使用 Grok Debugger 来帮助找出错误所在。 在 Kibana 中,Dev Tools > Grok Debugger,然后粘贴数据和 grok 模式,如下所示:

点击上面的 Simulate 按钮。结构化数据响应为空,这表明 grok 模式与样本数据不匹配。 通过定义一个我们知道将匹配任何内容的模式,然后将结果存储在一个名为 my_greedy_match 的字段中,确保 Grok Debugger 正常运行。 这可以通过将 grok 模式定义:为:

%{GREEDYDATA:my_greedy_match}

对于此模式,grok 已将样本数据的全部内容存储到一个名为 my_greedy_match 的字段中,这是我们对该测试的期望。

接下来,我们开始采用分而治之的方法来找出错误在 grok 模式中的位置。 为此,我们可以将分开的 grok 模式的前半部分复制到一个新表达式中,然后用刚才看到的 GREEDYDATA 表达式替换后半部分。 这种新的 grok 模式如下所示:

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*\[%{DATA:slowlog.index}\]\s*\[%{DATA:slowlog.shard:int}]took\[%{DATA:slowlog.took}\],\stook_millis\[%{DATA:slowlog.took_millis:float}\],%{GREEDYDATA:my_greedy_match}

将这个 grok 模式粘贴到 Grok Debugger 后,我们看到结构化数据响应仍然为空。

这意味着错误在 grok 模式的前半部分。 因此,我们将其再次分成两部分,如下所示:

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*%{GREEDYDATA:my_greedy_match}

 

在中间的位置出错

现在我们知道在 grok 模式的第一个 1/4 中没有错误,并且在 grok 模式的中点之前存在错误。 因此,让我们将 GREEDYDATA 表达式放在原始 grok 模式的大约八分之三的位置,如下所示:

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*\[%{DATA:slowlog.index}\]\s*\[%{DATA:slowlog.shard:int}]%{GREEDYDATA:my_greedy_match}

在调试器中将如下所示(这是一个匹配项):

因此,我们知道该误差在 grok 模式的八分之三点和中点之间。 让我们尝试添加一些更多的原始 grok 模式,如下所示:

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*\[%{DATA:slowlog.index}\]\s*\[%{DATA:slowlog.shard:int}]took%{GREEDYDATA:my_greedy_match}

这将返回一个空响应,如下面的调试器所示:

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*\[%{DATA:slowlog.index}\]\s*\[%{DATA:slowlog.shard:int}]took%{GREEDYDATA:my_greedy_match}

 

就快到了

提取 slowlog.shard.int 值后出了点问题。 如果我们重新检查我们正在解析的消息,我们将看到被接受的字符串应该在其前面有一个空格字符。 让我们修改 grok 模式,看看在 take 前面指定一个空格时它是否起作用,如下所示:

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*\[%{DATA:slowlog.index}\]\s*\[%{DATA:slowlog.shard:int}]\stook%{GREEDYDATA:my_greedy_match}

它起作用,如下所示:

但是我们仍然在 my_greedy_match 中存储了一堆数据。 让我们重新添加原始 grok 模式的其余部分,如下所示:

\[%{TIMESTAMP_ISO8601:event.end}\]\[%{LOGLEVEL:log.level}\s*\]\[%{DATA:slowlog.type}\]\s*\[%{DATA:host.name}\]\s*\[%{DATA:slowlog.index}\]\s*\[%{DATA:slowlog.shard:int}]\stook\[%{DATA:slowlog.took}\],\stook_millis\[%{DATA:slowlog.took_millis:float}\],\stotal_hits\[%{DATA:slowlog.total_hits:int}\shits\]\,\stypes\[%{DATA:slowlog.types}\],\sstats\[%{DATA:slowlog.stats}\],\ssearch_type\[%{DATA:slowlog.search_type}\],\stotal_shards\[%{DATA:slowlog.total_shards:int}\],\ssource\[%{GREEDYDATA:slowlog.source}\],\sid\[%{DATA:slowlog.x-opaque-id}\]

然后将 grok 模式粘贴到 Grok Debugger 中,如下所示:

grok 模式正在运行! 现在,我们从以前非结构化的慢日志条目中提取了结构化数据。

 

公开可用的 grok 模式

使用基本的 grok 模式,你可以建立复杂的模式以匹配你的数据。 此外,Elastic Stack 附带了120多个可重复使用的 grok 模式。 有关模式的完整列表,请参见摄取节点 grok模式Logstash grok 模式

替代 grok

在某些情况下,可以使用 dissect 处理器从单个文本字段中提取结构化字段。 与 Grok 处理器类似,dissect 也从文档中的单个文本字段中提取结构化字段。 但是,与Grok 处理器不同,dissect 不使用正则表达式。 这使得 Dissect 的语法很简单,并且可能比 Grok Processor 更快。