遇到 nginx 错误请求时如何使用 fluentd regexp

how to use fluentd regexp when meet the nginx bad request

我用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

如何解决这种情况?

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

^(?<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 次或多次出现,如上所示使用可选组,并确保只匹配预期的 characters/patterns。

查看下面的增强模式:

^(?<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

这里有一些兴趣点:

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