Доступ к логингу в PHP - PullRequest
       38

Доступ к логингу в PHP

13 голосов
/ 31 января 2012

Я хочу зарегистрировать доступ к любым файлам в папке /files, чтобы я мог обработать его с помощью PHP для генерации некоторой статистики.

Я не хочу писать собственный обработчик PHP, вызываемый через RewriteRule, потому что мне не нужно иметь дело с кодами состояния, MIME-типами и заголовками кэширования, а также проблемами с блокировкой файлов.

У меня нет доступа к конфигурации сервера, поэтому я не могу использовать CustomLog (у меня есть доступ к .htacess).

Я не могу использовать X-Sendfile, потому что он не включен.

У меня нет доступа к access.log.


Ищет авторский ответ.

Ответы [ 10 ]

12 голосов
/ 04 февраля 2012

Это довольно много ограничений, которые вы там поместили.

Вы можете сделать это с помощью пользовательского обработчика, установленного через PHP include вверху каждого применимого (или, с помощью __FILE__ синтаксического анализа,не применимо) скрипт. У вас должен быть скрипт, который запускается при нажатии на каждый файл , и вы исключили изменения в конфигурации сервера (включая, я полагаю, .htaccess, когда вы сказали, что RewriteRule недостаточно хорошо), так что это означает, что вы будете делать это через привратник на основе сценариев.Вы не можете иметь решение, которое отвечает вашим ограничениям и позволяет пользователям переходить к файлам, не обращаясь к PHP (или другому динамическому языку на стороне сервера).Кэширование может быть сохранено путем перенаправления пользователя на фактические файлы вместо запуска статического содержимого через PHP.

Вы можете сохранить информацию журнала в базе данных или файл в месте, доступном для записи с помощьюсервер (остерегайтесь конфликтов, если вы используете файлы - режим добавления сложен).

РЕДАКТИРОВАТЬ: quickshiftin указывает на два способа вызова PHP без необходимости добавлять include вызовыот руки.

5 голосов
/ 04 февраля 2012

Создайте auto_prepend_file и определите функцию для записи по вашему желанию. Вам понадобится доступ к .htaccess, чтобы установить их (и веб-хосту потребуется что-то вроде AllowOverride all в vhost) или с PHP 5.3 вы можете использовать возможность INI для каждого каталога .

.htaccess

php_value auto_prepend_file /path/to/file.php

для каждого каталога php.ini (PHP 5.3 CGI / Fast CGI SAPI)

user_ini.auto_prepend_file = /path/to/file.php

Тогда для вашего файла /path/to/file.php (я уверен, что-то более элегантное;))

file_put_contents(
    LOG_FILE,
    implode(PHP_EOL . PHP_EOL, array(
                'SERVER: ' . PHP_EOL . print_r($_SERVER, true),
                'REQUEST: ' . PHP_EOL . print_r($_REQUEST, true)
            )),
    FILE_APPEND
);

Прелесть этого подхода в том, что вам, вероятно, удастся избежать этого, и вам нужно будет только определить / включить код регистрации в одном месте.

EDIT:

После ретроспекции я вижу, что вы хотите, чтобы это работало для файлов произвольного типа ... Да, это было бы довольно грубо. Лучше всего думать о том, чтобы пометить эти файлы как .php или определить пользовательские типы пантомимы в .htaccess. Идея заключалась бы в том, чтобы запустить файлы через интерпретатор PHP, выполнив, таким образом, auto_prepend_file , и поскольку в файле нет тегов PHP, содержимое отправляется непосредственно клиенту. Возможно, даже чуть-чуть PHP поверх каждого файла содержимого, устанавливающего заголовок ContentType . Я даже не уверен, что это сработает, но может.

3 голосов
/ 10 февраля 2012

Работает только в случае mod_php. Есть некоторое снижение производительности - apache_lookup_uri () выполняет дополнительный внутренний подзапрос apache.

Как указали другие, вам нужен .htaccess, как

RewriteEngine On
RewriteRule ^/handler.php$ - [L]
RewriteRule ^/([a-zA-Z0-9\.]+)$ /handler.php?filename=$1 [L]

В файле handler.php используйте функцию virtual () для выполнения подзапроса apache. Пример здесь: http://www.php.net/manual/en/function.virtual.php#88722

Обновленное и протестированное (но достаточно минимальное) решение:

<?php
//add some request logging here
$file = $_GET["filename"];

$file_info = apache_lookup_uri($file);
header('content-type: ' . $file_info -> content_type);
// add other headers?
virtual($file);
exit(0);
?>
3 голосов
/ 09 февраля 2012

Может быть не совсем то, что вы хотите, но почему бы вам вообще не использовать другое решение?

Вы можете использовать Google Analytics VirtualPageviews для отслеживания загрузки файлов через Javascript.

Смотрите здесь для получения дополнительной информации: http://support.google.com/googleanalytics/bin/answer.py?hl=en&answer=55529

Вы даже можете создать свой собственный JS, чтобы отслеживать загрузки файлов через браузер, не беспокоясь о GA.

Обновление :

Как я уже сказал, вы можете легко создать свой собственный JS, чтобы отслеживать их, не беспокоясь о GA. Вот глупый пример в jQuery, который будет работать (не проверял его - просто написал это из головы):

Пример кода:

JS Side:

$(document).ready(function() {
  $("a").click(function() {
    if( $(this).attr('href').match(/\/files\/(.*)/) ) {
      $.ajax({
        url: '/tracking/the/file/downloads.php'
        data: {
          'ok': 'let\'s',
          'add': 'some information',
          'about': 'the user that initiated',
          'the': 'request',
          'file': $(this).attr('href')
        }
      });
    }

    return true;
  });
});
3 голосов
/ 08 февраля 2012

Похоже, что намерение здесь состоит в том, чтобы обойти все системы, которые изначально заложены в Apache и PHP. Если на вашем экземпляре сервера действительно есть эти ограничения, вам гораздо лучше попросить об изменении ваших привилегий, чем придумывать обходной путь, который ваш системный администратор может или не может быть доволен вашей реализацией.

3 голосов
/ 04 февраля 2012

Это довольно просто сделать, учитывая, что вам не нужно ограничивать доступ.

построить страницу logger.php, которая принимает на вход запрошенный файл, например:

logger.php?file=abc.exe

Вlogger.php вам просто нужно войти в систему, а затем перенаправить в файл:

file_put_contents('log', $_GET['file'] . ' requested',FILE_APPEND);
header('Location: files/'.$_GET['file']);

Просто проверьте $_GET['file'] на наличие вредоносных файлов

Конечно, вы должны заменить ссылки на вашем сайтеот:

<a href="files/abc.exe">

до

<a href="logger.php?file=abc.exe">
2 голосов
/ 10 февраля 2012

Если вы используете PHP в качестве скомпилированного модуля Apache, тогда функция virtual () может сделать это. Смотри: http://www.php.net/manual/en/function.virtual.php

<?php

$fn = $_GET['fn'];

log_file_access($fn); // You define how you want this to happen    
virtual($fn);

Затем вы ссылаетесь на файлы через:

http://example.com/file.php?fn=files/lolcat.jpg

2 голосов
/ 09 февраля 2012

Единственный ненавязчивый мониторинг, который вы могли бы сделать без фильтрации содержимого через PHP, - это проверка всех файлов и запись их времени доступа к файлу каждый раз, когда запрашивается любой файл PHP (вы просто добавляете функцию к своему файлу. PHP файлы или использовать переписать). Это потребует немного накладных расходов, но это единственная ненавязчивая статистика, которую вы можете получить.

Очевидно, что таким образом вы не можете получить точное количество обращений, но больше похоже на частоты, так что это тоже своего рода (жизнеспособная) статистика. Чтобы получить что-то вроде числа попаданий (это было открыто 1000 тысяч раз 25 марта в 2 часа ночи), вам нужно иметь доступ к журналам или передавать все это через скрипт PHP или cgi - что-то просто нужно выполнить ручной подсчет.

2 голосов
/ 08 февраля 2012

ОК, вот идея.Примите это во внимание, поначалу это может показаться неподходящим, но в конце прочитайте немного.Надеюсь, это работает с тем, что у вас на месте.В папке, содержащей ваши файлы, вы помещаете .htaccess, который перезаписывает все запросы к сценарию-обработчику PHP, в тот же каталог, что-то вроде этого (не проверено):

RewriteEngine On
RewriteRule ^/handler.php$ - [L]
RewriteRule ^/([a-zA-Z0-9\.]+)$ /handler.php?filename=$1 [L]

В сценарии PHP вы делаете все, что угоднорегистрация необходима с помощью file_put_contents().Затем вы создаете handler.php с таким кодом:

<?php
if (!file_exists) {
    header("Status: 404 Not Found");
    //if you have a 404 error page, you can use an include here to show it
    exit(0);
}

header("Content-disposition: attachment; filename={$_GET["filename"]}");
header("Content-type: ".get_mime_type($_GET["filename"]));
readfile($filename);

function get_mime_type($filename, $mimePath = '/etc') {
    $fileext = substr(strrchr($filename, '.'), 1);
    if (empty($fileext)) return (false);
    $regex = "/^([\w\+\-\.\/]+)\s+(\w+\s)*($fileext\s)/i";
    $lines = file("$mimePath/mime.types");
    foreach($lines as $line) {
        if (substr($line, 0, 1) == '#') continue; // skip comments
        $line = rtrim($line) . " ";
        if (!preg_match($regex, $line, $matches)) continue; // no match to the extension
        return ($matches[1]);
    }
    return (false); // no match at all
}
?>

По сути, вы создаете слой между запросом файла и фактической обработкой файла.Этот слой PHP регистрирует доступ к файлу, а затем обслуживает файл.Вы сказали, что не хотите возиться с кодами состояния и типами MIME, но прелесть заключается в том, что все, о чем позаботятся.В случае, если файл не существует, он просто генерирует стандарт 404, и вы можете включить пользовательскую страницу ошибки 404.Да, заголовок статуса здесь меняется, но ничего сложного.Что касается типов MIME, они обнаруживаются для вас в соответствии с теми же правилами типов MIME, которые использует Apache.Укажите функцию get_mime_type на файл mime.types на вашем сервере.Если вы не знаете, где это, просто скачайте копию с здесь .Я признаю, что это решение, вероятно, более техническое, чем вы искали, но с учетом имеющихся ограничений это хорошее решение.Самое приятное то, что он полностью прозрачен как для конечного пользователя, так и для тех, кто загружает данные.

1 голос
/ 10 февраля 2012

Я перепробовал очень много вещей, и кажется, что нет простого решения.

Мое решение использует хитрость заголовка Location, предложенную @ yes123, но я настроил его так, чтобы он соответствовал моим предпочтениям.

Ссылки на файлы остаются без изменений, поэтому они по-прежнему: /files/path/to/my/file.abc У меня есть RewriteRule:

RewriteRule ^files/(.*) path/to/tracker.php?path=/$1

Затем в файле я выдаю заголовок Location, добавив ?track=no к URL-адресу и исключение к более раннему RewriteRule:

RewriteCond %{QUERY_STRING} !(&|^)track=no(&|$)

Я добавил еще одну оптимизацию. Я включил E-Tag, поэтому, если клиент отправляет заголовок E-Tag, проверьте, соответствует ли он файлу, и верните 304 Not Modified вместо Location.

$fs = stat($document_root . $path);
$apache_etag = calculate_apache_etag($fs);
if ((isset($_SERVER["HTTP_IF_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_MATCH"], $apache_etag))
    || (isset($_SERVER["HTTP_IF_NONE_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_NONE_MATCH"], $apache_etag))
) {
    header("ETag: " . $apache_etag, true, 304);
    exit;
}

function etag_within_range($etag1, $etag2) {
    list($size1, $mtime1) = explode("-", $etag1);
    list($size2, $mtime2) = explode("-", $etag2);
    $mtime1 = floor(hexdec($mtime1) / 1000000);
    $mtime2 = floor(hexdec($mtime2) / 1000000);
    return $mtime1 === $mtime2 && $size1 === $size2;
}

И реализацию для calculate_apache_etag можно найти здесь: Как создать этаг, соответствующий Apache?

etag_withing_range решает проблему сравнения с более высокой точностью mtime в Apache.


Примечания о решениях, которые не работали

virtual

Тестовый скрипт:

var_dump(apache_response_headers());
virtual("/path/to/image.jpg");
var_dump(apache_response_headers());

Выходы:

array(1) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" }
[[binary junk]]
array(5) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" ["Keep-Alive"]=> string(18) "timeout=5, max=100" ["Connection"]=> string(10) "Keep-Alive" ["Transfer-Encoding"]=> string(7) "chunked" ["Content-Type"]=> string(9) "text/html" }

Content-Type: text/html reaaaaalllly ? (

Возможно, функция PHP5.3 header_remove может решить эту проблему? Я не пробовал.

...