3

我有一个服务器日志文件,我试图从中创建一个 PHP 页面,该页面总结了它存储的数据。日志中的每条记录都存储在一个新行上,格式为:

207.3.35.52 -- [2007-04-01 01:24:42] "GET index.php HTTP/1.0" 200 11411 "Mozilla/4.0"

//ip -- [timestamp] "GET url HTTP/1.0" status code bytes "user agent".

我正在尝试编写一个摘要,其中显示:请求总数、来自文章目录的请求总数、消耗的总带宽以及最后 404 错误及其页面的数量。

PHP:

$handle = fopen('logfiles/april.log','r') or die ('File opening failed');
$requestsCount = 0;
    while (!feof($handle)) {
        $dd = fgets($handle);
        $requestsCount++;   
        $parts = explode('"', $dd);
        $statusCode = substr($parts[2], 0, 4);
        }
fclose($handle);

此代码打开文件并统计记录数量,分离并找到记录中的状态代码编号。回显 $statusCode 时,它​​确实显示了正确的信息,显示了日志中的所有状态代码。

一个接受两个论点来总计 404 错误的函数:

function requests404($l,$s) {
        $r =  substr_count($l,$s);
        return "Total 404 errors: ".$r."<br />";
}

回显结果:

echo requests404($statusCode, '404');

这个函数不起作用,它只返回 0。在 PHP 中处理 txt 文件是我最薄弱的技能,我非常感谢一些帮助,因为我认为我可能会以完全错误的方式进行操作。谢谢。

4

3 回答 3

3
$handle = fopen('logfiles/april.log','r') or die ('File opening failed');
$requestsCount = 0;
$num404 = 0;

while (!feof($handle)) {
    $dd = fgets($handle);
    $requestsCount++;   
    $parts = explode('"', $dd);
    $statusCode = substr($parts[2], 0, 4);
    if (hasRequestType($statusCode, '404')) $num404++;
}

echo "Total 404 Requests: " . $num404 . "<br />";
fclose($handle);

function hasRequestType($l,$s) {
        return substr_count($l,$s) > 0;
}
于 2012-11-14T14:32:37.807 回答
3

虽然我喜欢在很多事情上使用 PHP。但解析日志并不是其中之一。

相反,我真的强烈建议您考虑使用 awk 进行所有未来的日志解析工作。

这是我一起完成的简单 bash/awk 脚本,它以一种非常易于阅读/易于理解的方式实现了您的所有要求:

#!/bin/bash

awk '
BEGIN {
    total_requests = 0;
    total_requests_articles = 0;
    total_404s = 0;
    total_bandwidth = 0;
} {
    total_requests++;

    if ( $8 == "404" ) {
        total_404s++;
    }

    if ( $6 ~ /articles/ ) {
        total_requests_articles++;
    }

    total_bandwidth += $9
} END {
    printf "total requests: %i\n", total_requests
    printf "total requests for articles: %i\n", total_requests_articles
    printf "total 404s: %i\n", total_404s
    printf "total bandwidth used: %i\n", total_bandwidth
}' ${1}

使用此文件作为演示:

207.3.35.52 -- [2007-04-01 01:24:42] "GET index.php HTTP/1.0" 200 11411 "Mozilla/4.0"
207.3.35.52 -- [2007-04-01 01:24:42] "GET index.php HTTP/1.0" 200 11411 "Mozilla/4.0"
207.3.35.52 -- [2007-04-01 01:24:42] "GET index.php HTTP/1.0" 200 11411 "Mozilla/4.0"
207.3.35.52 -- [2007-04-01 01:24:42] "GET articles/index.php HTTP/1.0" 404 11411 "Mozilla/4.0"
207.3.35.52 -- [2007-04-01 01:24:42] "GET articles/index.php HTTP/1.0" 200 11411 "Mozilla/4.0"
207.3.35.52 -- [2007-04-01 01:24:42] "GET index.php HTTP/1.0" 404 11411 "Mozilla/4.0"

结果如下:

[root@hacklab5 tmp]# ./apache.bash apache.log
total requests: 6
total requests for articles: 2
total 404s: 2
total bandwidth used: 68466

只是说.. awk 很棒。并且快速燃烧。并为解析日志量身定制。现在,向您学习一些 awk 非常有用;)

干杯——

于 2012-11-14T14:58:43.077 回答
0

substr_count将“404”的出现次数相加$statusCode,并且$statusCode每次仅是单行日志的四个字节“200”(或“304”或“404”)。

所以只要状态码不是404,你就会得到零,这是正确的。

您需要调用requests404每一输入,并对总数求和。

实际上使用数组可能会更好:

$totals = array(
    200 => 0,
    404 => 0,
    304 => 0,
);
$requestsCount = 0;
$bytesSent = 0;
$totalBytes = 0;
while (!feof($handle)) {
    $dd = fgets($handle);
    $requestsCount++;   
    $parts = explode('"', $dd);
    list($statusCode, $bytes) = explode(" ", $parts[2]);
    if (!isset($totals[$statusCode]))
       $totals[$statusCode] = 0;
    $totals[$statusCode]++;
    if (200 == $statusCode)
        $bytesSent += $bytes;
    $totalBytes += $bytes;
}
fclose($handle);


printf("We got $totals[404] 404 errors\n");

在循环结束时, $totals 将包含类似

{
    200 =>  12345,
    404 =>   1234,
    401 =>     22,
    304 =>   7890,
    ...
}
于 2012-11-14T14:32:24.903 回答