Grok/Regex解析日志键值对 适配字段值含/不含嵌套括号场景
Grok提取含可选嵌套括号的键值对字段方案
问题背景
需要通过Grok/正则表达式提取日志消息中以()包裹的各动态键值对字段,示例日志内容如下:
2021/05/23 16:01:26.094 High Messaging.Message.Delivered Id(ci1653336085475.12327434@test_te) MessageId(EPIUM#1130754#84601671) SenderPartyName(Mcdonalds (CFH) Restaurant Glen) ReceiverPartyName(TEST_HERE_AGAIN) SenderRoutingId(08Mdsfkm853)
已编写的基础Grok模式如下:
%{DATESTAMP:ts} %{WORD:loglevel} %{DATA:reason}\s ?(Id\(%{DATA:id}\))? ?(MessageId\(%{DATA:originalmessageid}\))? ?(SenderPartyName\((?<senderpartyname>.+?\).+?)\))? ?(ReceiverPartyName\(%{DATA:receiverpartyname}\))? ?(SenderRoutingId\(%{DATA:senderroutingid}\))?
现存问题
原有模式针对SenderPartyName字段的匹配规则存在逻辑缺陷:
- 字段值内含嵌套括号(如值为
Mcdonalds (CFH) Restaurant Glen)时可正常解析 - 字段值不含嵌套括号(如值为
Mcdonalds Restaurant Glen)时会出现匹配越界,将后续ReceiverPartyName等字段内容错误纳入senderpartyname的匹配结果,错误返回值为:
"senderpartyname": "Mcdonalds Restaurant Glen) ReceiverPartyName(TEST_HERE_AGAIN"
期望实现效果:无论字段值是否包含嵌套括号,都能准确提取对应完整值
- 无嵌套时返回
"senderpartyname": "Mcdonalds Restaurant Glen" - 有嵌套时返回
"senderpartyname": "Mcdonalds (CFH) Restaurant Glen"
修正方案
修正后完整Grok模式
%{DATESTAMP:ts} %{WORD:loglevel} %{DATA:reason}\s?(Id\(%{DATA:id}\))? ?(MessageId\(%{DATA:originalmessageid}\))? ?(SenderPartyName\((?<senderpartyname>(?:[^()]|\([^()]*\))*)\))? ?(ReceiverPartyName\(%{DATA:receiverpartyname}\))? ?(SenderRoutingId\(%{DATA:senderroutingid}\))?
核心匹配逻辑
针对senderpartyname的匹配段(?<senderpartyname>(?:[^()]|\([^()]*\))*)做了逻辑调整,匹配规则如下:
- 仅匹配两类合法内容:
- 非括号的普通字符
[^()] - 单层嵌套括号包裹的内容
\([^()]*\),适配当前日志场景下字段值最多存在一层嵌套括号的情况
- 非括号的普通字符
- 匹配到当前字段对应的闭合右括号时自动停止,不会越界读取后续字段内容,同时兼容有无嵌套括号的两种场景
多层嵌套扩展适配
如果后续业务场景出现字段值包含多层嵌套括号的情况,可将senderpartyname的匹配段替换为支持递归匹配的写法(Grok底层基于Oniguruma正则引擎,支持递归语法):
(?<senderpartyname>(?:[^()]|\((?&senderpartyname)\))*)
该写法可支持任意层数的嵌套括号匹配。
内容的提问来源于stack exchange,提问作者nobrac
相关产品推荐
相关产品推荐

