【问题标题】:how to use fluentd regexp when meet the nginx bad request遇到nginx错误请求时如何使用fluentd regexp
【发布时间】:2025-12-19 18:45:11
【问题描述】:

我用fluentd代替logstash,我用in-tail插件拖尾nginx access日志,访问日志格式如下:

log_format  main  '$remote_addr - $remote_user [$time_local] $request '
'"$status" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" $request_time';

fluentd conf 就像

format /^(?<host>\S+)\s-\s(?<user>\S+)\s\[(?<time>[^\]]*)\]\s(?<method>\S+)\s(?<url>\S+)\s(?<http_version>\S+)\s"(?<status>[^\"]+)"\s(?<bytes>\d+)\s"(?<rfc>[^\"]+)"\s"(?<agent>[^\"]+)"\s"(?<x_forward>[^\"]+)"\s(?<time_spent>\S+).*$/

请求正确时可以正常工作,但请求错误时会报错,如下所示:

172.31.33.157 - - [08/May/2017:16:30:20 +0800] - "400" 0 "-" "-" "-" 0.000

错误的请求错过了methodrfc 字段,所以fluentd 运行错误。如何修改format,让我不关心请求是错误的还是正确的?

任何答案将不胜感激

遇到另一种情况,agentrfc字段为none,运行错误。就像

172.31.44.196 - - [08/May/2017:18:47:31 +0800] GET /click?mb_pl=ios&version=1.1 HTTP/1.1 "302" 5 "-" "" "100.38.38.149, 54.224.136.60" 0.004

172.31.44.196 - - [08/May/2017:18:47:31 +0800] GET /click?mb_pl=ios&version=1.1 HTTP/1.1 "302" 5 "" "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304" "100.38.38.149, 54.224.136.60" 0.004

如何解决这种情况?

【问题讨论】:

  • 正确的请求是什么样的?
  • 172.31.44.196 - - [08/May/2017:18:47:31 +0800] GET /click?mb_pl=ios&version=1.1 HTTP/1.1 "302" 5 "-" "Mozilla/ 5.0 (iPhone; CPU iPhone OS 10_3_1 像 Mac OS X) AppleWebKit/603.1.30 (KHTML, 像 Gecko) Mobile/14E304" "100.38.38.149, 54.224.136.60" 0.004

标签: regex fluentd


【解决方案1】:

您可以将模式的可选部分包装在可选的非捕获组中(?:...)?

^(?<host>\S+)\s-\s(?<user>\S+)\s\[(?<time>[^\]]*)\](?:\s(?<method>\S+))?(?:\s(?<url>\S+))?\s(?<http_version>\S+)\s"(?<status>[^\"]+)"\s(?<bytes>\d+)(?:\s"(?<rfc>[^\"]+)")?\s"(?<agent>[^\"]+)"\s"(?<x_forward>[^\"]+)"\s(?<time_spent>\S+).*$

regex demo

在这里,我包装了以下部分:

(?:\s(?<method>\S+))?
(?:\s(?<url>\S+))?
(?:\s"(?<rfc>[^\"]+)")?

这意味着,整个子模式序列将是可选的、空格和命名的捕获组模式。

注意:当您有更多可选字段时,您可能会发现自己处于模式组开始匹配属于其他组的不需要的输入部分的情况。在这种情况下,请确保您限制通用模式并使用可选模式:将 + 替换为 * 以匹配 0 次或更多次而不是 1 次或多次,使用如上所示的可选组,并确保只匹配预期的字符/模式。

查看下面的增强模式:

^(?<host>\S+)\s-\s(?<user>\S+)\s\[(?<time>[^\]]*)\](?:\s(?<method>\w+))?(?:\s(?<url>\/\S+))?\s(?<http_version>\S+)\s"(?<status>\d+)"\s(?<bytes>\d+)(?:\s"(?<rfc>[^\"]*)")?(?:\s"(?<agent>[^\"]*)")?\s"(?<x_forward>[^\"]*)"\s(?<time_spent>[\d.]+).*$

请参阅regex demo

这里有一些兴趣点:

  • (?&lt;method&gt;\w+))? - 在这里,我们只匹配单词字符(\S > \w,你甚至可以考虑使用[A-Z]
  • (?:\s(?&lt;url&gt;\/\S+))? - 添加了/,因为您的网址以/ 开头
  • (?&lt;status&gt;\d+) - \S 更改为 \d(因为状态码仅由数字组成)
  • (?&lt;rfc&gt;[^\"]*)")? - + 更改为 *(值可以为空)
  • (?:\s"(?&lt;agent&gt;[^\"]*)")? - 与rfc 相同
  • \s"(?&lt;x_forward&gt;[^\"]*)" - 同上
  • (?&lt;time_spent&gt;[\d.]+ - time_spent 值仅包含数字和点。

【讨论】:

  • 如果您有其他可选字段,请尝试使用可选的非捕获组将 \s+the subpattern 包装起来。