diff --git a/class/class.logs.php b/class/class.logs.php index 883fd6b..68bdab0 100644 --- a/class/class.logs.php +++ b/class/class.logs.php @@ -4,9 +4,10 @@ * AVE.cms * * Класс, предназначенный для управления журналом системных сообщений + * (Оптимизировано чтение для больших файлов логов) * * @package AVE.cms - * @version 3.x + * @version 4.x * @filesource * @copyright © 2007-2014 AVE.cms, http://www.ave-cms.ru * @@ -32,13 +33,65 @@ * Внутренние методы класса */ + /** + * Вспомогательный метод для чтения последних строк файла с конца + * + * @param string $file_name Путь к файлу + * @param int $_count Количество строк, которые нужно прочитать + * @param int $chunk_size Размер блока для чтения + * @return array Массив строк в прямом хронологическом порядке + */ + private function readLastLines($file_name, $_count, $chunk_size = 8192) + { + if (!file_exists($file_name) || !($fp = @fopen($file_name, 'rb'))) { + return []; + } + + $file_size = @filesize($file_name); + $pos = $file_size; + $buffer = ''; + $raw_lines = []; + + // Чтение файла блоками с конца + while ($pos > 0 && count($raw_lines) < $_count) { + $bytes_to_read = min($chunk_size, $pos); + $pos -= $bytes_to_read; + + fseek($fp, $pos); + $chunk = fread($fp, $bytes_to_read); + $buffer = $chunk . $buffer; + + if ($pos == 0) { + $lines = explode("\n", $buffer); + $buffer = ''; + } else { + $lines = explode("\n", $buffer); + $buffer = array_shift($lines); + } + + foreach (array_reverse($lines) as $line) { + if (trim($line) !== '') { + $raw_lines[] = $line; + if (count($raw_lines) >= $_count) { + break 2; + } + } + } + } + + fclose($fp); + + // Разворачиваем, чтобы получить правильный хронологический порядок + return array_reverse($raw_lines); + } + /** * Внешние методы класса */ -/** - * Метод, предназначенный для отображения всех записей Журнала событий + /** + * Метод, предназначенный для отображения всех записей Журнала событий (log.csv) * */ function logList () @@ -46,49 +99,37 @@ global $AVE_Template; $file_name = BASE_DIR . $this->_logdir; + $_count = 10000; $_lines = []; - if (file_exists($file_name) && $fp = @fopen($file_name, 'rb')) - { - $_count = 10000; + $raw_lines = $this->readLastLines($file_name, $_count); - $_size = @filesize($file_name); + foreach ($raw_lines as $line) { - $_slice = 10240; + $event = str_getcsv($line, ',', '"', '\\'); - $_size > $_slice && fseek($fp, $_size - $_slice); - - while (!feof($fp)) - { - // ИСПРАВЛЕНИЕ: Добавляем обязательные параметры: разделитель (','), ограничитель ('"'), экранирование ('\') - $event = fgetcsv($fp, $_slice, ',', '"', '\\'); - - if (empty($event[0]) || count($event) < 3) { - continue; - } - - $_lines[] = [ - 'log_time' => $event['0'] ?? '', - 'log_ip' => $event['1'] ?? '', - 'log_url' => $event['2'] ?? '', - 'log_user_id' => $event['3'] ?? '', - 'log_user_name' => $event['4'] ?? '', - 'log_text' => $event['5'] ?? '', - 'log_type' => $event['6'] ?? '', - 'log_rubric' => $event['7'] ?? '' - ]; + if (empty($event[0]) || count($event) < 3) { + continue; } - count($_lines) > $_count && $_lines = array_slice($_lines, -$_count); + $_lines[] = [ + 'log_time' => $event['0'] ?? '', + 'log_ip' => $event['1'] ?? '', + 'log_url' => $event['2'] ?? '', + 'log_user_id' => $event['3'] ?? '', + 'log_user_name' => $event['4'] ?? '', + 'log_text' => $event['5'] ?? '', + 'log_type' => $event['6'] ?? '', + 'log_rubric' => $event['7'] ?? '' + ]; } - // Передаем данные в шаблон для вывода и отображаем страницу $AVE_Template->assign('logs', $_lines); $AVE_Template->assign('content', $AVE_Template->fetch('logs/logs.tpl')); } /** - * Метод, предназначенный для отображения всех записей Журнала событий 404 + * Метод, предназначенный для отображения всех записей Журнала событий 404 (404.csv) * */ function List404() @@ -96,37 +137,27 @@ global $AVE_Template; $file_name = BASE_DIR . $this->_404dir; + $_count = 10000; $_lines = []; - if (file_exists($file_name) && $fp = @fopen($file_name, 'rb')) - { - $_count = 10000; + $raw_lines = $this->readLastLines($file_name, $_count); - $_size = @filesize($file_name); + foreach ($raw_lines as $line) { - $_slice = 10240; + $event = str_getcsv($line, ',', '"', '\\'); - $_size > $_slice && fseek($fp, $_size - $_slice); - - while (!feof($fp)) - { - $event = fgetcsv($fp, $_slice, ',', '"', '\\'); - - if (empty($event[0]) || count($event) < 3) { - continue; - } - - $_lines[] = [ - 'log_time' => $event['0'] ?? '', - 'log_ip' => $event['1'] ?? '', - 'log_query' => $event['2'] ?? '', - 'log_user_agent' => $event['3'] ?? '', - 'log_user_referer' => $event['4'] ?? '', - 'log_request_uri' => $event['5'] ?? '' - ]; + if (empty($event[0]) || count($event) < 3) { + continue; } - count($_lines) > $_count && $_lines = array_slice($_lines, -$_count); + $_lines[] = [ + 'log_time' => $event['0'] ?? '', + 'log_ip' => $event['1'] ?? '', + 'log_query' => $event['2'] ?? '', + 'log_user_agent' => $event['3'] ?? '', + 'log_user_referer' => $event['4'] ?? '', + 'log_request_uri' => $event['5'] ?? '' + ]; } // Передаем данные в шаблон для вывода и отображаем страницу @@ -135,7 +166,7 @@ } /** - * Метод, предназначенный для отображения всех записей Журнала событий 404 + * Метод, предназначенный для отображения всех записей Журнала событий SQL (sql.csv) * */ function ListSql() @@ -143,42 +174,29 @@ global $AVE_Template; $file_name = BASE_DIR . $this->_sqldir; + $_count = 10000; $_lines = []; - if (file_exists($file_name) && $fp = @fopen($file_name, 'rb')) - { - $_count = 10000; + $raw_lines = $this->readLastLines($file_name, $_count); - $_size = @filesize($file_name); + foreach ($raw_lines as $line) { - $_slice = 10240; + $event = str_getcsv($line); - $_size > $_slice && fseek($fp, $_size - $_slice); - - while (!feof($fp)) - { - // Используем разделители по умолчанию, как в оригинале, но добавим безопасное чтение. - $event = fgetcsv($fp, $_slice); - - if (empty($event[0]) || count($event) < 3) { - continue; - } - - $_lines[] = [ - 'log_time' => $event['0'] ?? '', - 'log_ip' => $event['1'] ?? '', - 'log_url' => $event['2'] ?? '', - 'log_user_id' => $event['3'] ?? '', - 'log_user_name' => $event['4'] ?? '', - // Предполагаем, что event[5] всегда должно быть (base64_decode и unserialize) - 'log_text' => isset($event['5']) ? unserialize(base64_decode($event['5'])) : '' - ]; + if (empty($event[0]) || count($event) < 3) { + continue; } - count($_lines) > $_count && $_lines = array_slice($_lines, -$_count); + $_lines[] = [ + 'log_time' => $event['0'] ?? '', + 'log_ip' => $event['1'] ?? '', + 'log_url' => $event['2'] ?? '', + 'log_user_id' => $event['3'] ?? '', + 'log_user_name' => $event['4'] ?? '', + 'log_text' => isset($event['5']) ? unserialize(base64_decode($event['5'])) : '' + ]; } - // Передаем данные в шаблон для вывода и отображаем страницу $AVE_Template->assign('logs', $_lines); $AVE_Template->assign('content', $AVE_Template->fetch('logs/sql.tpl')); } @@ -302,7 +320,7 @@ } /** - * Метод, предназначенный для экспорта сообщений 404 + * Метод, предназначенный для экспорта сообщений MySql * */ function ExportSql() @@ -329,4 +347,5 @@ exit; } - } \ No newline at end of file + } +?> \ No newline at end of file