Эффективный анализ логов Apache в PHP - PullRequest
0 голосов
/ 18 января 2012

Хорошо, это сценарий: мне нужно проанализировать мои журналы, чтобы найти, сколько раз были загружены эскизы изображений, фактически не просматривая страницу "большого изображения" ... Это в основном система защиты от хотлинков, основанная на соотношенииот «большого пальца» к «полному» представлению изображения

Учитывая, что сервер постоянно подвергается бомбардировке запросами к миниатюрам, наиболее эффективным решением представляется использование буферизованных журналов apache, которые записывают на диск один раз, скажем, 1 МБ, а затемпериодически анализировать журналы

Мой вопрос таков: как мне проанализировать журнал apache в PHP для сохранения данных, при этом верно следующее:

  • Журнал будет использоваться иобновлять в режиме реального времени, и мне нужен мой PHP-скрипт, чтобы иметь возможность читать его, пока это делается
  • PHP-скрипт должен будет «помнить», какие части журнала он прочитал, чтобы не читатьодна и та же часть дважды и перекос данных
  • Потребление памяти должно быть минимальным, поскольку журналы могут легко достигать 10 ГБ данных за несколько часов

Сценарий php logger будет вызываться один раз каждые 60 секунд и обрабатывать любое количество строк журнала, которое он может за это время ..

Я пытался взломать некоторый код вместе, но у меня естьпроблемы с использованием минимального объема памяти, поиск способа отслеживать указатель с «движущимся» размером файла

Вот часть журнала:

212.180.168.244 - - [18/Jan/2012:20:06:57 +0100] "GET /t/0/11/11441/11441268.jpg HTTP/1.1" 200 3072 "-" "Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.60" "-"
122.53.168.123 - - [18/Jan/2012:20:06:57 +0100] "GET /t/0/11/11441/11441276.jpg HTTP/1.1" 200 3007 "-" "Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.60" "-"
143.22.203.211 - - [18/Jan/2012:20:06:57 +0100] "GET /t/0/11/11441/11441282.jpg HTTP/1.1" 200 4670 "-" "Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.60" "-"

Прикрепление кода для вашегообзор здесь:

<?php
//limit for running it every minute
error_reporting(E_ALL);
ini_set('display_errors',1);
set_time_limit(0);
include(dirname(__FILE__).'/../kframework/kcore.class.php');
$aj = new kajaxpage;
$aj->use_db=1;
$aj->init();
$db=kdbhandler::getInstance();
$d=kdebug::getInstance();
$d->debug=TRUE;
$d->verbose=TRUE;

$log_file = "/var/log/nginx/access.log"; //full path to log file when run by cron
$pid_file = dirname(__FILE__)."/../kframework/cron/cron_log.pid";
//$images_id = array("8308086", "7485151", "6666231", "8343336");

if (file_exists($pid_file)) {
    $pid = file_get_contents($pid_file);
    $temp = explode(" ", $pid);
    $pid_timestamp = $temp[0];
    $now_timestamp = strtotime("now");
    //if (($now_timestamp - $pid_timestamp) < 90) return;
    $pointer = $temp[1];
    if ($pointer > filesize($log_file)) $pointer = 0;
}
else $pointer = 0;

$pattern = "/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})[^\[]*\[([^\]]*)\][^\"]*\"([^\"]*)\"\s([0-9]*)\s([0-9]*)(.*)/";
$last_time = 0;
$lines_processed=0;

if ($fp = fopen($log_file, "r+")) {
    fseek($fp, $pointer);
    while (!feof($fp)) {
        //if ($lines_processed>100) exit;
        $lines_processed++;
        $log_line = trim(fgets($fp));
        if (!empty($log_line)) {
            preg_match_all($pattern, $log_line, $matches);
            //print_r($matches);
            $size = $matches[5][0];
            $matches[3][0] = str_replace("GET ", "", $matches[3][0]);
            $matches[3][0] = str_replace("HTTP/1.1", "", $matches[3][0]);
            $matches[3][0] = str_replace(".jpg/", ".jpg", $matches[3][0]);
            if (substr($matches[3][0],0,3) == "/t/") {
                $get = explode("-",end(explode("/",$matches[3][0])));
                $imgid = $get[0];
                $type='thumb';
            }
            elseif (substr($matches[3][0], 0, 5) == "/img/") {
                $get1 = explode("/", $matches[3][0]);
                $get2 = explode("-", $get1[2]);
                $imgid = $get2[0];
                $type='raw';
            }
            echo $matches[3][0];
            // put here your sql insert or update
            $imgid=(int) $imgid;
            if (isset($type) && $imgid!=1) {
                switch ($type) {
                    case 'thumb':
                        //use the second slave in the registry
                        $sql=$db->slave_query("INSERT INTO hotlink SET thumbviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE thumbviews=thumbviews+1 ",2);
                        echo "INSERT INTO hotlink SET thumbviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE thumbviews=thumbviews+1";
                    break;
                    case 'raw':
                        //use the second slave in the registry
                        $sql=$db->slave_query("INSERT INTO hotlink SET rawviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE rawviews=rawviews+1",2);
                        echo "INSERT INTO hotlink SET rawviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE rawviews=rawviews+1";
                    break;
                }
            }

            // $imgid - image ID
            // $size - image size

            $timestamp = strtotime("now");
            if (($timestamp - $last_time) > 30) {
                file_put_contents($pid_file, $timestamp . " " . ftell($fp));
                $last_time = $timestamp;
            }
        }
    }
    file_put_contents($pid_file, (strtotime("now") - 95) . " " . ftell($fp));
    fclose($fp);
}

?>

Ответы [ 4 ]

1 голос
/ 19 января 2012

Может быть, вы можете настроить мою версию PHP tail для поиска вашей последней временной отметки, а не для подсчета строк, а затем для чтения строк с этой точки, обрабатывая их одну за другой?

Я бы попробовал сам, потому что мне немного любопытно, но, к сожалению, я не могу сделать это прямо сейчас: (

0 голосов
/ 08 января 2013

Я знаю, что этот ответ запоздал, но все равно может помочь (код всегда можно улучшить).

Размер файла 10 Гб и требуемая память соответствуют вашим основным проблемам. Apache поддерживает несколько файлов журналов, и реальная сила нескольких файлов журналов заключается в возможности создавать файлы журналов в различных форматах http://httpd.apache.org/docs/1.3/multilogs.html

Создайте второй файл журнала с минимальными данными, необходимыми для мониторинга журнала в реальном времени. В этом случае вы, возможно, сможете убрать строку пользовательского агента и т. Д. Из журнала в первую очередь.

Исходя из ваших примеров строк журнала, это может вдвое сократить объем данных, необходимых PHP для загрузки.

0 голосов
/ 19 января 2012

Вместо этого я лично отправляю записи журнала в запущенный скрипт. Apache разрешит это, запустив имя файла журнала с помощью канала (|). Если это не работает, вы также можете создать fifo (см. Mkfifo).

Запущенный скрипт (чем бы он ни был) может буферизовать x строк и делать то, что ему нужно, основываясь на этом. Чтение данных не так уж сложно, и не должно быть там, где будет ваше узкое место.

Я подозреваю, что у вас возникнут проблемы с инструкциями INSERT в базе данных.

0 голосов
/ 18 января 2012

Решением было бы сохранить журнал в базе данных mysql.Может быть, вы можете написать программу на языке C для анализа файла журнала после сохранения его в mysql.Это было бы на порядок быстрее, и это не очень сложно.Другой вариант - использовать фитон, но я думаю, что использование базы данных необходимо.Вы можете использовать полнотекстовый индекс, чтобы соответствовать вашей строке.Python также может быть скомпилирован в двоичный файл.Это делает это более эффективно.Согласно запросу: файл журнала стеков увеличивается.Дело не в том, что вы даете 10 ГБ сразу.

...