Logstash 处理日志中可有可无的可选字段?
Logstash grok 处理可选字段:用括号加问号 (模式)? 把字段段标记为可选;或用多个模式备选。本文给出具体写法和示例。
核心语法:在 grok 模式中把可选部分用圆括号包起来再加问号——(可选段)?。正则的 ? 让整个分组"出现 0 次或 1 次",未匹配时通常不新增该捕获字段,但不会删除事件中已有同名字段,整条日志仍能正常匹配。
示例:可选字段
日志有两种格式(有的带用户 ID,有的不带):
2024-01-15 10:30:00 INFO User logged in
2024-01-15 10:30:01 INFO User 42 logged in
grok 模式:
filter {
grok {
match => { "message" => "\A%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} User( %{INT:user_id:int})? logged in\z" }
}
}
( %{INT:user_id:int})? 表示"空格+数字"这一段是可选的:匹配到时提取 user_id 字段,没匹配到时整个事件照常解析、不新增 user_id;若之前已有此字段,它仍保留。
多个可选段
match => { "message" => "\A%{IP:ip}( - %{WORD:user})? \"%{WORD:method} %{URIPATH:path}\" %{INT:status:int}( %{NUMBER:duration:float})?\z" }
后续处理时判断字段是否存在
if [user_id] {
mutate { convert => { "user_id" => "integer" } }
}
备选方案:多模式匹配
格式差异大时,与其堆叠可选段不如写多个模式:
grok {
match => { "message" => [
"%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} User %{NUMBER:user_id} %{GREEDYDATA:msg}",
"%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} User %{GREEDYDATA:msg}"
] }
}
常见问题(FAQ)
Q:可选段没匹配时字段是什么值?
A:通常不会新增捕获字段,但不清除已有字段。if [field] 判断的是truthiness,缺失、null、false都判为假,并非严格存在判断。
Q:可选段用非贪婪匹配 (??) 有必要吗?
A:一般不需要。(...)? 已足够;?? 是非贪婪量词,用于不同的场景。
Q:给可选字段设默认值?
A:不要把数字 user_id 混写为字符串 anonymous;可以给独立分类字段设置默认值,例如 if ![user_id] { mutate { add_field => { "user_kind" => "anonymous" } } },并先明确 false/null 与缺失的处理。